fix: significantly speed up styled-components in dev mode #7440
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
TL;DR – 1 LOC makes
sanity dev
render 10 times faster 🤯Description
This PR enables
styled-components
"speedy mode" insanity dev
by explicitly setting theSC_DISABLE_SPEEDY
flag tofalse
. By default,styled-components
have different modes for inserting CSS rules, choosing which mode to use based on whetherprocess.env.NODE_ENV
is set toproduction
or not. WhenNODE_ENV
isproduction
, it uses the "speedy mode," which significantly boosts performance. Otherwise, it falls back to a slower mode.I discovered this performance difference while working on an unrelated benchmark in the
concurrent-styled-components
repository. I noticed thatstyled-components
had drastically different performance results depending on whether I rannext dev
ornext build && next start
. After addingSC_DISABLE_SPEEDY: 'false'
in mynext.config.ts
, the performance in development mode dramatically improved, almost matching production levels (it does match ifreactStrictMode
isfalse
).How are CSS rules inserted when
NODE_ENV
isdevelopment
?styled-components
is a mature library that has been around for a long time. Initially, CSS-in-JS libraries inserted styles by adding text nodes to a<style>
tag:When using this method, you can open the
<style>
tag in DevTools and inspect the text nodes:To confirm this, run the following snippet to see a bunch of text:
The downside is that every time a text node is added to the
<style>
, the browser has to parse the entireinnerText
. In large applications like Sanity Studio, this can start off at almost half a megabyte, and as more CSS is loaded, it only grows larger, taking even longer to parse.What is "speedy mode"?
"Speedy mode" was introduced in v3.1.0. It uses the CSSOM API
CSSStyleSheet.insertRule
instead of text nodes:With "speedy mode" enabled, the
<style>
tag appears empty in DevTools:To verify "speedy mode," inspect the
cssRules
:However,
cssRules
alone isn't a sufficient indicator of "speedy mode" because it's also available when using text nodes. You need to check thatinnerText
is empty whilecssRules
has entries:Why isn't "speedy mode" always enabled?
"Speedy mode" was introduced in v3.1.0 and set to be enabled only when
NODE_ENV
is notdevelopment
because, while browser support forCSSStyleSheet.insertRule
was good (available since v1 versions of Safari, Chrome, Firefox, and IE v9), it was poorly supported in testing environments like jsdom, such as in Jest. For example,jest-styled-components
broke in v3.1.0 and was patched in v3.1.3.Poor DevTools support was another reason it wasn't enabled by default in development. Chrome added support for editing rules in "speedy mode" in version 81, released in April 2020, and Firefox also supports it now. With Speedy disabled, developers could edit rules directly from generated CSS-in-JS in DevTools:
Safari, however, still does not allow editing rules with
CSSStyleSheet.insertRule
in DevTools. It only allows inspecting them:Given Safari already lacks a React DevTools extension, the argument against enabling "speedy mode" in development for Safari users becomes weaker. Developers who need to edit styles can use Chrome or Firefox, which they typically already use as their main development browsers.
What's the purpose of the
SC_DISABLE_SPEEDY
flag?In v4.1.0, the
SC_DISABLE_SPEEDY
flag was added to disable "speedy mode" in production environments due to issues such as the Yandex crawler not supporting CSSOM styles. The idea was that in production, you typically want "speedy mode" enabled for better performance, but in certain edge cases, you can disable it.Over time, more flags were added to support popular bundler setups, as shown in the constants file:
process.env.REACT_APP_SC_DISABLE_SPEEDY
process.env.SC_DISABLE_SPEEDY
It is counterintuitive because while the flag’s name suggests disabling "speedy mode," we use it to force-enable "speedy mode" in development where it is off by default. This double-negative logic can be confusing, but it provides a way to achieve consistent behavior between development and production.
Despite recent improvements in browser DevTools, not all environments support editing dynamically generated and injected CSS rules. Chrome and Firefox have full editing capabilities, while Safari remains read-only for such rules. This limitation is one of the reasons
styled-components
maintainers originally chose to disable "speedy mode" by default in development while enabling it in production, where such editing capabilities are less critical. However, given the current landscape of browser support, enabling "speedy mode" in development aligns with modern expectations and usage.What to review
Run
sanity dev
and verify that "speedy mode" is enabled:When clicking around in the Studio, cold start performance should be significantly and noticeably improved. Clicking on a tool that lazy-loads, like Vision, should reach first render faster. Opening popovers, dialogs, new screens, and UI should all be faster. Re-render performance on components that respond to input and render changed CSS (e.g., hovering inputs that change border color, hovering a tooltip, typing in an input that changes to invalid status, typing into a search field) should also be much faster.
Testing
The performance profiling on Vision Tool shows that before applying the
SC_DISABLE_SPEEDY
flag, the VisionGui component's render was significantly faster than 30 other components. After applying the flag, VisionGui's render time remained the same (2.9ms), but all other styled components in that render pass became significantly faster, making VisionGui the slowest component. This change results in a substantial performance boost, especially for views with many styled components, as seen in the attached profiles.Before:
After:
You can load the profiling JSON files into React DevTools using the "Load profile" button for further analysis:
They were produced by running
pnpm dev
and clicking "Reload and start profiling" on http://localhost:3333/test/visionNotes for release
This change enables
styled-components
"speedy mode" insanity dev
, improving developer mode performance. This change impacts only development builds (sanity dev
), and production builds (sanity build
orsanity start
) remain unaffected since "speedy mode" has always been enabled in production.Developers embedding Sanity Studio in other frameworks like Next.js or Remix need to adjust their own build tooling to declare the
SC_DISABLE_SPEEDY
flag to achieve the same performance benefits. For example, Next.js users can add this snippet to theirnext.config.{js,mjs,ts}
file:For Safari users, while they will still be able to inspect and see CSS rules coming from
styled-components
, these will now be read-only in the DevTools inspector. The performance benefits from enabling "speedy mode" in development mode are significant for Safari users and outweigh the convenience of being able to edit these rules directly in the inspector. With Hot Module Reload, developers can still quickly make changes to their source code, and the new styles will apply immediately.Enabling "speedy mode" offers a faster development experience across the board. Developers using Safari for testing purposes can still verify the functionality of the studio, and for more detailed CSS editing, they can switch to Chrome, Firefox, or other Chromium-based browsers like Arc or Brave.
It's still possible to restore the old behaviour of disabling speedy in dev mode in userland with a custom
sanity.cli.ts
override: