Transient Props in styled‑components v6: Ensuring Consistent Class Names After the v5→v6 Upgrade
0 reputation · 19 Feb 2022, 03:47 UTC
0 reputation · 19 Feb 2022, 03:47 UTC
styled‑components switched from a custom prop‑filtering strategy in v5 to the transient‑prop pattern ($‑prefixed props) in v6, alongside an internal stylis v4 upgrade. The new model removes implicit prop forwarding to the DOM, but it also changes how class names are generated during server‑side rendering (SSR) and client hydration.
After migrating to v6, team members have observed that class names produced on the server differ from those generated on the client when transient props are used, leading to hydration mismatches. The library offers no built‑in mechanism to scope or deduplicate these class names across the rendering boundary.
There is no documented approach for synchronizing transient‑prop‑driven class names between SSR and client hydration. Teams must decide whether to rely on a Babel/SWC plugin configuration, enforce a naming convention, or implement a custom deduplication strategy.
1. What best‑practice patterns exist for keeping transient‑prop class names stable across SSR and client hydration in styled‑components v6?
2. How does the new stylis v4 processing affect class name determinism when transient props are used, and can this be mitigated?
3. Are there recommended Babel or SWC plugin setups that preserve class name consistency without manual intervention?
30275 reputation · 19 Feb 2022, 14:51 UTC
In v6, the class name is a hash of the component’s style definition plus every prop that reaches the style interpolator. When a prop is marked transient (prefixed with $), it is excluded from the hash and from DOM forwarding. To keep class names identical on the server and the client you must make sure that the set of props that influence the hash is identical in both environments.
$ prefix or shouldForwardProp to decide what lands in the style function.styled( and ensure every prop used only for styling is prefixed with $. Remove the prefix from props that must reach the DOM.
shouldForwardProp: For wrapper components that pass props through, define shouldForwardProp: (prop) => !prop.startsWith('$') to lock the forwarding contract.
@styled-components/babel-plugin with displayName: true and fileName: true in both server and client builds. This guarantees the same component name is used in the hash.
ServerStyleSheet logic in pages/_document.js and use the same styled‑components instance on the client. Avoid mixing styled-components imports from different package versions.
npm test -- --runInBand (or your test runner) and diff the snapshot files. Any changed class names are a signal to review the prop usage.
Stylis v4 sorts nested rules alphabetically before hashing. Ensure that your interpolations are deterministic: avoid runtime functions that return different objects on each render. Use css`…` literals or memoized callbacks.
"@styled-components/babel-plugin" to plugins with the same options in both server and client .babelrc or babel.config.js.
swc-loader, enable the styledComponents preset with displayName: true and fileName: true.
ReactDOMServer.renderToString.
className strings.
If you are using a framework that injects its own SSR logic (e.g., Next.js, Gatsby), let us know so we can tailor the advice for that environment.
Use comments to ask for clarification. Post a solution as an answer.
30,275 reputation · 19 Feb 2022, 07:16 UTC
In styled‑components v6 a prop prefixed with $ is stripped before the style function runs, so it never contributes to the CSS that is hashed. Consequently, a transient prop cannot change the generated class name on either the server or the client. Hydration mismatches only appear when a prop that does influence the style (i.e., is not $-prefixed) is treated differently in the two environments—for example, because the shouldForwardProp filter or the Babel/SWC transform is applied only on one side. Making the transient‑prop convention and the transform options identical in server and client builds eliminates this source of drift.