Skip to content

Conversation

@stipsan
Copy link
Member

@stipsan stipsan commented Nov 27, 2025

Description

On auto updating studios createGlobalStyle is broken and no global styles are rendered!

When we publish auto updating studios we vendor react, react-dom and styled-components.
Unfortunately we vendored styled-components to styled-components/dist/styled-components.esm.js while we should've aliased to styled-components/dist/styled-components.browser.esm.js.
styled-components.esm.js is for node environments, and using it instead of styled-components.browser.esm.js breaks createGlobalStyle.

When styled-components builds for npm it sets the __SERVER__ constant to true for styled-components.esm.js and false for styled-components.browser.esm.js.
This constant branches important logic in createGlobalStyle: https://github.com/styled-components/styled-components/blob/a21089e1cde9d2492349088787c59dd85358a337/packages/styled-components/src/constructors/createGlobalStyle.ts#L51-L62

Because __SERVER__ is false the useLayoutEffect that calls renderStyles isn't there: https://github.com/styled-components/styled-components/blob/a21089e1cde9d2492349088787c59dd85358a337/packages/styled-components/src/constructors/createGlobalStyle.ts#L58C11-L58C76

And since ssc.styleSheet.server is always false it also doesn't insert styles during render: https://github.com/styled-components/styled-components/blob/a21089e1cde9d2492349088787c59dd85358a337/packages/styled-components/src/constructors/createGlobalStyle.ts#L51-L53

Why is ssc.styleSheet.server false?

The value of ssc.styleSheet.server is set during the construction of the default StyleSheet, the options.isServer.

The isServer option is by default set to !IS_BROWSER. IS_BROWSER is basically defined as typeof window !== 'undefined'

We have two cases of createGlobalStyle in our own code:

  • export const GlobalStyle = createGlobalStyle((props) => {
    const {color, font} = getTheme_v2(props.theme)
    return css`
    ::-webkit-resizer {
    background-image: ${buildResizeHandleDataUri(color.icon)};
    background-repeat: no-repeat;
    background-position: bottom right;
    }
    ::-webkit-scrollbar {
    width: ${SCROLLBAR_SIZE}px;
    height: ${SCROLLBAR_SIZE}px;
    }
    ::-webkit-scrollbar-corner {
    background-color: transparent;
    }
    ::-webkit-scrollbar-thumb {
    background-clip: content-box;
    background-color: var(--card-border-color, ${color.border});
    border: ${SCROLLBAR_BORDER_SIZE}px solid transparent;
    }
    ::-webkit-scrollbar-thumb:hover {
    background-color: var(--card-muted-fg-color, ${color.muted.fg});
    }
    ::-webkit-scrollbar-track {
    background: transparent;
    }
    *::selection {
    background-color: ${rgba(color.focusRing, 0.3)};
    }
    html {
    background-color: ${color.bg};
    }
    body {
    scrollbar-gutter: stable;
    }
    #sanity {
    font-family: ${font.text.family};
    }
    b {
    font-weight: ${font.text.weights.medium};
    }
    strong {
    font-weight: ${font.text.weights.medium};
    }
    `
    })
  • const GlobalViewTransition = createGlobalStyle`
    html:active-view-transition-type(sanity-iframe-viewport) {
    view-transition-name: none;
    &::view-transition {
    pointer-events: none;
    }
    /* &::view-transition-old(root) {
    display: none;
    }
    &::view-transition-new(root) {
    animation: none;
    } */
    }
    `

I've sometimes noticed that auto updating studios have cases of text that lack our Inter font-family, and never understood why, but today I learned it's because we set the default body font-family in a createGlobalStyle.

I've made a repro showing the issue, during sanity dev it renders the font (it's missing later because I'm intentionally rendering the @sanity/ui button as <Button>Zoom In</Button> when it's supposed to be <Button text="Zoom In" />):

sc.before.mov

Once deployed with autoUpdates: true all global styling is missing and clicking the Zoom In/Out button does nothing:

after.sc.mov

What to review

Missed anything?

Testing

You can try the pkg.pr.new build on a studio that has auto-updating studio, and once deployed you can verify styles are there by inspecting body and seeing it has scrollbar-gutter: stable;:

image

Notes for release

Fixes an issue where createGlobalStyle from styled-components doesn't work if the Studio is deployed with autoUpdates: true. Styles declared with it were simply not there once deployed, while they work just fine if autoUpdates: false or when working locally with sanity dev.
Usually this issue would manifest itself in some UI text using Times New Roman instead of Inter, and missing browser scrollbar styling.
If you're affected by this issue you'll need to run a new sanity deploy after updating to sanity@latest, you might as well use our styled-components fork while at it and give your studio a nice performance boost.
If you're already using @sanity/styled-components instead of styled-components then you're not affected by this issue.

@vercel
Copy link

vercel bot commented Nov 27, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
page-building-studio Ready Ready Preview Comment Nov 27, 2025 3:56pm
test-studio Ready Ready Preview Comment Nov 27, 2025 3:56pm
3 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
e2e-studio Ignored Ignored Nov 27, 2025 3:56pm
studio-workshop Ignored Ignored Nov 27, 2025 3:56pm
test-next-studio Ignored Ignored Nov 27, 2025 3:56pm

@github-actions
Copy link
Contributor

github-actions bot commented Nov 27, 2025

🧪 E2E Preview environment

🔑 Environment Variables for Local Testing

This is the preview URL for the E2E tests: https://e2e-studio-5z2uq29iq.sanity.dev

To run the E2E tests locally, you can use the following environment variables, then run pnpm test:e2e --ui to open the Playwright test runner.

💬 Remember to build the project first with pnpm build:e2e.

  SANITY_E2E_PROJECT_ID=ittbm412
  SANITY_E2E_BASE_URL=https://e2e-studio-5z2uq29iq.sanity.dev
  SANITY_E2E_DATASET="update depending the project you want to test (pr-11313-chromium-19741784377 || pr-11313-firefox-19741784377 )"
  SANITY_E2E_DATASET_CHROMIUM=pr-11313-chromium-19741784377
  SANITY_E2E_DATASET_FIREFOX=pr-11313-firefox-19741784377

@github-actions
Copy link
Contributor

github-actions bot commented Nov 27, 2025

📊 Playwright Test Report

Download Full E2E Report

This report contains test results, including videos of failing tests.

@github-actions
Copy link
Contributor

github-actions bot commented Nov 27, 2025

⚡️ Editor Performance Report

Updated Thu, 27 Nov 2025 16:09:45 GMT

Benchmark reference
latency of sanity@latest
experiment
latency of this branch
Δ (%)
latency difference
article (title) 24.1 efps (42ms) 24.7 efps (41ms) -1ms (-2.4%)
article (body) 31.3 efps (32ms) 33.6 efps (30ms) -2ms (-6.6%)
article (string inside object) 26.3 efps (38ms) 26.3 efps (38ms) +0ms (-/-%)
article (string inside array) 23.0 efps (44ms) 23.3 efps (43ms) -1ms (-1.1%)
recipe (name) 52.6 efps (19ms) 55.6 efps (18ms) -1ms (-5.3%)
recipe (description) 69.0 efps (15ms) 71.4 efps (14ms) -1ms (-/-%)
recipe (instructions) 99.9+ efps (5ms) 99.9+ efps (6ms) +1ms (-/-%)
singleString (stringField) 71.4 efps (14ms) 71.4 efps (14ms) +0ms (-/-%)
synthetic (title) 17.2 efps (58ms) 17.2 efps (58ms) +0ms (-/-%)
synthetic (string inside object) 16.7 efps (60ms) 17.5 efps (57ms) -3ms (-5.0%)

efps — editor "frames per second". The number of updates assumed to be possible within a second.

Derived from input latency. efps = 1000 / input_latency

Detailed information

🏠 Reference result

The performance result of sanity@latest

Benchmark latency p75 p90 p99 blocking time test duration
article (title) 42ms 47ms 83ms 159ms 118ms 9.9s
article (body) 32ms 44ms 96ms 170ms 206ms 7.1s
article (string inside object) 38ms 44ms 55ms 121ms 9ms 6.2s
article (string inside array) 44ms 48ms 77ms 102ms 19ms 6.6s
recipe (name) 19ms 21ms 24ms 48ms 0ms 6.8s
recipe (description) 15ms 17ms 20ms 45ms 0ms 4.1s
recipe (instructions) 5ms 10ms 10ms 23ms 0ms 3.0s
singleString (stringField) 14ms 15ms 18ms 28ms 0ms 5.5s
synthetic (title) 58ms 60ms 110ms 169ms 308ms 13.6s
synthetic (string inside object) 60ms 64ms 109ms 146ms 179ms 7.8s

🧪 Experiment result

The performance result of this branch

Benchmark latency p75 p90 p99 blocking time test duration
article (title) 41ms 45ms 77ms 109ms 45ms 9.9s
article (body) 30ms 50ms 97ms 146ms 190ms 6.9s
article (string inside object) 38ms 42ms 52ms 105ms 2ms 6.1s
article (string inside array) 43ms 48ms 77ms 116ms 0ms 6.6s
recipe (name) 18ms 21ms 23ms 45ms 0ms 6.5s
recipe (description) 14ms 17ms 22ms 29ms 0ms 4.1s
recipe (instructions) 6ms 10ms 11ms 16ms 0ms 3.0s
singleString (stringField) 14ms 17ms 19ms 28ms 0ms 5.9s
synthetic (title) 58ms 62ms 72ms 141ms 270ms 14.4s
synthetic (string inside object) 57ms 60ms 68ms 174ms 167ms 7.5s

📚 Glossary

column definitions

  • benchmark — the name of the test, e.g. "article", followed by the label of the field being measured, e.g. "(title)".
  • latency — the time between when a key was pressed and when it was rendered. derived from a set of samples. the median (p50) is shown to show the most common latency.
  • p75 — the 75th percentile of the input latency in the test run. 75% of the sampled inputs in this benchmark were processed faster than this value. this provides insight into the upper range of typical performance.
  • p90 — the 90th percentile of the input latency in the test run. 90% of the sampled inputs were faster than this. this metric helps identify slower interactions that occurred less frequently during the benchmark.
  • p99 — the 99th percentile of the input latency in the test run. only 1% of sampled inputs were slower than this. this represents the worst-case scenarios encountered during the benchmark, useful for identifying potential performance outliers.
  • blocking time — the total time during which the main thread was blocked, preventing user input and UI updates. this metric helps identify performance bottlenecks that may cause the interface to feel unresponsive.
  • test duration — how long the test run took to complete.

@github-actions
Copy link
Contributor

⚡️ Editor Performance Report

Deploying studio and running performance tests…

@github-actions
Copy link
Contributor

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 44.64% 63658 / 142589
🔵 Statements 44.64% 63658 / 142589
🔵 Functions 48.23% 3397 / 7043
🔵 Branches 79.34% 12932 / 16298
File CoverageNo changed files found.
Generated in workflow #46481 for commit 8d6c11d by the Vitest Coverage Report Action

@stipsan stipsan marked this pull request as ready for review November 27, 2025 16:55
@stipsan stipsan requested a review from a team as a code owner November 27, 2025 16:55
@stipsan stipsan requested review from pedrobonamin and removed request for a team November 27, 2025 16:55
@stipsan stipsan enabled auto-merge (squash) November 27, 2025 17:29
@github-actions
Copy link
Contributor

Preview this PR with pkg.pr.new

Create a new project

npx https://pkg.pr.new/create-sanity@23db03f

Note: This installs the latest Sanity Studio from the npm registry. To apply the changes from this PR in your newly created project, run the install commands below for each package listed in your package.json

...Or run the Sanity CLI

npx https://pkg.pr.new/@sanity/cli@23db03f <command>

...Or upgrade project dependencies

pnpm logo Using pnpm

📦 sanity
pnpm install https://pkg.pr.new/sanity@8d6c11d
📦 @sanity/vision
pnpm install https://pkg.pr.new/@sanity/vision@8d6c11d
📦 Other packages…
📦 create-sanity
pnpm install https://pkg.pr.new/create-sanity@8d6c11d
📦 groq
pnpm install https://pkg.pr.new/groq@8d6c11d
📦 @sanity/cli
pnpm install https://pkg.pr.new/@sanity/cli@8d6c11d
📦 @sanity/codegen
pnpm install https://pkg.pr.new/@sanity/codegen@8d6c11d
📦 @sanity/diff
pnpm install https://pkg.pr.new/@sanity/diff@8d6c11d
📦 @sanity/migrate
pnpm install https://pkg.pr.new/@sanity/migrate@8d6c11d
📦 @sanity/mutator
pnpm install https://pkg.pr.new/@sanity/mutator@8d6c11d
📦 @sanity/schema
pnpm install https://pkg.pr.new/@sanity/schema@8d6c11d
📦 @sanity/types
pnpm install https://pkg.pr.new/@sanity/types@8d6c11d
📦 @sanity/util
pnpm install https://pkg.pr.new/@sanity/util@8d6c11d

npm logo Using npm

📦 sanity
npm install https://pkg.pr.new/sanity@8d6c11d
📦 @sanity/vision
npm install https://pkg.pr.new/@sanity/vision@8d6c11d
📦 Other packages…
📦 create-sanity
npm install https://pkg.pr.new/create-sanity@8d6c11d
📦 groq
npm install https://pkg.pr.new/groq@8d6c11d
📦 @sanity/cli
npm install https://pkg.pr.new/@sanity/cli@8d6c11d
📦 @sanity/codegen
npm install https://pkg.pr.new/@sanity/codegen@8d6c11d
📦 @sanity/diff
npm install https://pkg.pr.new/@sanity/diff@8d6c11d
📦 @sanity/migrate
npm install https://pkg.pr.new/@sanity/migrate@8d6c11d
📦 @sanity/mutator
npm install https://pkg.pr.new/@sanity/mutator@8d6c11d
📦 @sanity/schema
npm install https://pkg.pr.new/@sanity/schema@8d6c11d
📦 @sanity/types
npm install https://pkg.pr.new/@sanity/types@8d6c11d
📦 @sanity/util
npm install https://pkg.pr.new/@sanity/util@8d6c11d

View Commit (8d6c11d)

Copy link
Contributor

@pedrobonamin pedrobonamin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing find 🧙🏻 !

@stipsan stipsan merged commit c964ffe into main Nov 28, 2025
76 checks passed
@stipsan stipsan deleted the fix-styled-component-aliasing branch November 28, 2025 13:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants