FAQs
What do I need to do to migrate to v6?
v6.3.0+: Native React Server Components (RSC) support was added. Styled components now work in RSC without 'use client' directives or registry setup. See the RSC section for details.
First, let's start by updating the necessary packages to their latest versions.
If you're using NPM:
npm install styled-components@^6 stylis@^4 npm uninstall @types/styled-components
If you're using Yarn:
yarn add styled-components@^6 stylis@^4 yarn remove @types/styled-components
As styled-components now provides its own types, there's no longer a need for community ones.
TypeScript
Good news for TypeScript enthusiasts – styled-components is now natively written in TypeScript! Even if you haven't used TypeScript before, it's recommended for improving the reliability of your project as it can alert you when you're using an unknown prop or if a prop has a different value than expected.
However, if you don't use TypeScript in your project, don't worry! IDEs like VS Code will still pick up the types and provide hints as you write your code.
shouldForwardProp is no longer provided by default
If you haven't migrated your styling to use transient props ($prefix), you might notice React warnings about styling props getting through to the DOM in v6. To restore the v5 behavior, use StyleSheetManager:
import isPropValid from '@emotion/is-prop-valid'; import { StyleSheetManager } from 'styled-components'; function MyApp() { return ( <StyleSheetManager shouldForwardProp={shouldForwardProp}> {/* other providers or your application's JSX */} </StyleSheetManager> ) } // This implements the default behavior from styled-components v5 function shouldForwardProp(propName, target) { if (typeof target === "string") { // For HTML elements, forward the prop if it is a valid HTML attribute return isPropValid(propName); } // For other elements, forward all props return true; }
Vendor prefixes are omitted by default
As the web and browsers have matured significantly by 2023, vendor prefixing is often unnecessary. Therefore, for the v6 release, we've decided to omit automatic prefixing by default to reduce the amount of CSS delivered to a page. If you prefer the v5 behavior, you can restore it via StyleSheetManager:
import { StyleSheetManager } from 'styled-components'; function MyApp() { return ( <StyleSheetManager enableVendorPrefixes> {/* other providers or your application's JSX */} </StyleSheetManager> ) }
To accommodate this change, the original disableVendorPrefixes prop was inverted to enableVendorPrefixes; if you have disableVendorPrefixes set, you can now remove it as it's the new default.
Update stylis plugins
styled-components v6 uses the newer stylis v4; if you are providing stylisPlugins to StyleSheetManager, ensure the plugins are up-to-date. For instance, stylis-plugin-rtl released a new version to support the updated stylis.
Nested syntax handling
With the stylis v4 upgrade came a change to how nested selectors are handled which now correctly mirrors browser behavior. Specifically, pseudoselectors (e.g. :before) that do not start with & will not have the ampersand implicitly added anymore.
v5 behavior
styled.div` :hover { color: red; } ` // .[classname]:hover { color: red; } styled.div` &:hover { color: red; } ` // .[classname]:hover { color: red; }
v6 behavior
styled.div` :hover { color: red; } ` // .[classname] :hover { color: red; } (equivalent to .[classname] *:hover) styled.div` &:hover { color: red; } ` // .[classname]:hover { color: red; }
Transient $as and $forwardedAs props have been dropped
To reduce confusion around application order, we've dropped the transient $as and $forwardedAs props. Please use the regular as and forwardedAs props instead.
Dropped legacy withComponent() API
This change has been a long time coming. The withComponent API is no longer supported, so please use the as prop instead. You can specify as at definition time via attrs or at runtime:
import styled from 'styled-components'; const Button = styled.button` background: blue; color: white; `; const ButtonLink = styled(Button).attrs({ as: 'a' })``; // These are equivalent, but `ButtonLink` allows for attaching further styling if desired. <Button as="a" href="https://styled-components.com"> <ButtonLink href="https://styled-components.com">
Minimum Node support raised to v16+
In line with the maintenance schedule for Node, we now support v16 as the oldest runtime that's still receiving security patches.
Notable changes in later v6.x releases
Babel macro removed (v6.1.0)
The babel-plugin-macros integration was removed due to low usage. If you were importing from styled-components/macro, switch to the standard import and use the Babel or SWC plugin directly.
Streaming SSR support (v6.2.0)
Experimental renderToPipeableStream support was added for React 18+ streaming SSR. See the streaming rendering section for details.
React Server Components (v6.3.0)
Styled components now work in RSC environments without 'use client' directives. ThemeProvider and StyleSheetManager become no-ops in RSC. Use CSS custom properties for theming instead of ThemeProvider in server components. See the RSC section for best practices.
New HTML & SVG element helpers (v6.3.0)
Added styled.search, styled.slot, styled.template, and SVG filter primitive helpers (styled.clipPath, styled.linearGradient, styled.feGaussianBlur, etc.).
CSS custom properties in TypeScript (v6.3.0)
--var style props now work without TypeScript errors in .attrs() and JSX style props.
How should I handle dynamic styles?
styled-components offers several ways to apply dynamic styles. Each has different trade-offs for performance, readability, and compatibility with React Server Components.
Quick reference
| Approach | Best for | Generates new classes? | RSC compatible? |
|---|---|---|---|
| Prop interpolation | Computed styles based on props | Yes, per unique value | Yes, with overhead |
| CSS custom properties | Theming, frequently-changing values | No | Yes |
| Data attributes | Discrete variants (size, color scheme) | No | Yes |
.attrs() with style | Per-frame updates (mouse, scroll, animation) | No | Yes |
Prop interpolation
Use for values that are truly dynamic and derived from component props. Each unique result produces a new CSS class.
const Heading = styled.h1<{ $color: string }>` color: ${props => props.$color}; `; <Heading $color="tomato">Hello</Heading>
Best when the set of possible values is small. Avoid for continuously-varying values like slider positions or mouse coordinates — see Performance.
CSS custom properties
Use for values that change frequently or cascade through a component tree. The CSS rule is static; only the variable value changes.
const Button = styled.button` background: var(--btn-bg, #ccc); color: var(--btn-color, #333); `; <Button style={{ '--btn-bg': '#BF4F74', '--btn-color': 'white' } as React.CSSProperties}> Primary </Button>
This is the recommended approach for theming in RSC environments, where ThemeProvider is a no-op.
Data attributes
Use for a finite set of variants. Keeps the component fully static — no JavaScript in the template literal, one cached CSS class.
const Badge = styled.span` padding: 2px 8px; border-radius: 4px; background: #eee; &[data-variant='success'] { background: #d4edda; color: #155724; } &[data-variant='warning'] { background: #fff3cd; color: #856404; } &[data-variant='error'] { background: #f8d7da; color: #721c24; } `; <Badge data-variant="success">Deployed</Badge>
.attrs() with inline style
Use for values that change on every frame. Applies values as inline styles — zero class generation.
const Tooltip = styled.div.attrs<{ $x: number; $y: number }>(props => ({ style: { transform: `translate(${props.$x}px, ${props.$y}px)`, }, }))` position: fixed; padding: 4px 8px; background: #333; color: white; border-radius: 4px; pointer-events: none; `;
See When to use attrs? and the Performance section for more detail.
When to use attrs?
You can pass in attributes to styled components using attrs, but it is not always sensible to do so.
The rule of thumb is to use attrs when you want every instance of a styled component to have that prop, and pass props directly when every instance needs a different one:
const PasswordInput = styled.input.attrs(props => ({ // Every <PasswordInput /> should be type="password" type: "password" }))`` // This specific one is hidden, so let's set aria-hidden <PasswordInput aria-hidden="true" />
The same goes for props that can be inferred based on the "mode" of another prop. In this case you can set a property on attrs to a function that computes that prop based on other props.
Why should I avoid declaring styled components in the render method?
By declaring a styled component inside the render method of a react component, you are dynamically creating a new component on every render. This means that React will have to discard and re-calculate that part of the DOM subtree on each subsequent render, instead of just calculating the difference of what changed between them. This leads to performance bottlenecks and unpredictable behavior.
🚫
const Header = () => { const Title = styled.h1` font-size: 10px; ` return ( <div> <Title /> </div> ) }
✅
const Title = styled.h1` font-size: 10px; ` const Header = () => { return ( <div> <Title /> </div> ) }
Can I nest rules?
Yes: nesting is a feature intentionally ported from Sass. Used sparingly it's a great way to lighten your code by reducing the need to create explicit classes for every element.
It can also be used by parent components to define contextual constraints that aren't properly a concern of the affected children:
FirstSecondThirdFirstSecondThird
It's also incredibly convenient to co-locate media queries, since we can see at a glance exactly how the component will respond at any resolution.
Hello world!
How can I override styles with higher specificity?
The way to override styles with a high specificity is to simply increase the specificity of your own styles. This could be done using !important, but that's error prone and generally not a good idea.
We recommend the following technique:
const MyStyledComponent = styled(AlreadyStyledComponent)` &&& { color: #BF4F74; font-weight: bold; } `
Each & gets replaced with the generated class, so the injected CSS then looks like this:
.MyStyledComponent-asdf123.MyStyledComponent-asdf123.MyStyledComponent-asdf123 { color: #BF4F74; font-weight: bold; }
The repeated class bumps the specificity high enough to override the source order without being very tedious to write!
How can I override inline styles?
Inline styles will always take precedence over external CSS, so you cannot override it by simply increasing specificity.
There is a neat trick however, which is to use the style element-attr CSS Selector in conjunction with !important:
const MyStyledComponent = styled(InlineStyledComponent)` &[style] { font-size: 12px !important; color: blue !important; } `
Why am I getting HTML attribute warnings?
The warning message below indicates that non-standard attributes are being attached to
HTML DOM elements such as <div> or <a>. If you are seeing this warning message, it is likely that you or a library you are using is attaching props as attributes to HTML DOM elements.
Warning: Received "true" for a non-boolean attribute
If you're seeing this warning you are probably passing true where "true" would be appropriate. It's likely that this comes from a .attrs property, or from a completely unrelated prop that you're passing to a styled(Component) component.
To learn more about how props are passed, see this section.
For example:
const Link = props => ( <a {...props} className={props.className}> {props.text} </a> ) const StyledComp = styled(Link)` color: ${props => (props.red ? 'red' : 'blue')}; ` <StyledComp text="Click" href="https://www.styled-components.com/" red />
This will render:
<a text="Click" href="https://www.styled-components.com/" red="true" class="[generated class]">Click</a>
React will warn on non-standard attributes being attached such as "red" and "text", which are not valid HTML attributes for the <a> element.
To fix this, you can use transient props or destructure props:
Transient props
You can use transient props to fix this:
const Link = ({ className, text, ...props }) => ( <a {...props} className={className}> {text} </a> ) const StyledComp = styled(Link)` color: ${props => (props.$red ? 'red' : 'blue')}; ` <StyledComp text="Click" href="https://www.styled-components.com/" $red />
Destructure props
Alternatively, you can use argument destructuring to pull out those known styling props:
const Link = ({ className, red, text, ...props }) => ( <a {...props} className={className}> {text} </a> ) const StyledComp = styled(Link)` color: ${props => (props.red ? 'red' : 'blue')}; ` <StyledComp text="Click" href="https://www.styled-components.com/" red />
This will render:
<a href="https://www.styled-components.com/" class="[generated class]">Click</a>
When you use argument destructuring, any variables pulled out of the props object will not be included when spread-applying the remaining props (...props).
Which browsers are supported?
styled-components supports the same set of browsers as the current React version.
- v5.x (React v16.3+): IE11, all evergreen browsers
- v6.x (React v18+): Modern evergreen browsers (Chrome, Firefox, Edge, Safari). IE support dropped.
Evergreen browsers include Chrome, Firefox, Edge, Safari, and their derivatives.
Why do my DOM nodes have two classes?
Each node actually has two classes connected to it: one is static per component, meaning each element of a styled component has this class. It hasn't any style attached to it. Instead, it's used to quickly identify which styled component a DOM objects belongs to or to make minor changes in the DevTools. It's also used for component selectors. The static class probably will look something like: .sc-fVOeaW.
The other is dynamic, meaning it will be different for every element of your styled component with different props, based on what the interpolations result in. It will probably look like .fVOeaW (note the lack of "sc" prefix.)
For example, the styled component <Button /> would render with the same static class every time. If the styles are changed using interpolations, like <Button secondary />, then the dynamic class will be a different one, while the static class would remain the same.
Why am I getting a warning about several instances of module on the page?
If you are seeing a warning message in the console like the one below, you probably
have several instances of styled-components initialized on the page.
It looks like there are several instances of "styled-components" initialized in this application. This may cause dynamic styles not rendering properly, errors happening during rehydration process and makes you application bigger without a good reason. If you are using a building tool like webpack, consider checking your bundle for duplication of the "styled-components" module.
This may cause dynamic styles not working properly or even errors during rehydration if you are using server-side rendering.
Possible reasons
There are several common reasons for this to happen:
- You have several applications that are using
styled-componentsrunning on the same page (e.g., several entry points in webpack are loaded on the same page) - You have another
styled-componentslibrary somewhere in your dependencies - You have a monorepo structure for your project (e.g, lerna, yarn workspaces) and
styled-componentsmodule is a dependency in more than one package (this one is more or less the same as the previous one)
Running multiple applications on one page
If you have several applications running on one page, consider using one styled-components module for all of them. If you are using webpack, you can use resolve.alias
to ensure all applications reference the same styled-components instance:
module.exports = { entry: { app1: "./src/app.1.js", app2: "./src/app.2.js", }, + resolve: { + alias: { + "styled-components": path.resolve(__dirname, "node_modules", "styled-components"), + }, + }, }
Duplicated module in node_modules
If you think that the issue is in duplicated styled-components module somewhere in your dependencies, there are several ways to check this. You can use npm ls styled-components, yarn list --pattern styled-components or find -L ./node_modules | grep /styled-components/package.json commands in your application folder.
If none of these commands identified the duplication, try analyzing your bundle for multiple instances of styled-components. You can just check your bundle source, or use a tool like source-map-explorer
or webpack-bundle-analyzer.
If you identified that duplication is the issue that you are encountering there are several things you can try to solve it:
If you are using npm you can try running npm dedupe. This command searches the local dependencies and tries to simplify the structure by moving common dependencies further up the tree.
Be aware that npm dedupe doesn't work well with symlinked folders (i.e., when you use npm link)
If you are using webpack, you can change the way it will resolve
the styled-components module. You can overwrite the default order in which webpack will look for your dependencies and make your application node_modules more prioritized than default node module resolution order:
resolve: { + alias: { + "styled-components": path.resolve(appFolder, "node_modules", "styled-components"), + } }
Usage with monorepos
If you use a monorepo (e.g. with npm, yarn, or pnpm workspaces), ensure styled-components is hoisted to the root of your monorepo so that all packages share a single copy. Most modern package managers handle this automatically when you configure workspaces.
Add styled-components as a dependency in your root package.json:
{ "name": "my-styled-monorepo", "dependencies": { "styled-components": "^6" }, "scripts": { "start": "turbo run start", "build": "turbo run build" } }
Can I use CSS frameworks?
Integrating an existing CSS framework with styled-components is really easy! You can use its existing class names alongside your components.
For example, imagine you have an existing app with two classes you want to use again: .small and .big. If you want the class to always be attached to the component, you should use the attrs method to attach it. If you want to attach it only in some cases you can use the className props like you always have!
If the framework has a bunch of raw global CSS that needs to be included on the page, you can add it using the createGlobalStyle API. This is also useful for things like CSS resets.
I am a library author. Should I bundle styled-components with my library?
If you are a library author, we recommend that you should not bundle and ship styled-components module with your library. There are two steps that you need to do to achieve this:
- Marking
styled-componentsas external in your package dependencies - Removing
styled-componentsfrom your library bundle
Marking styled-components as external in your package dependencies
To do this, you will need to move it from dependencies to devDependencies
and include it in the peerDependencies list in your package.json file:
{ - "dependencies" : { + "devDependencies" : { "styled-components": "^6" }, + "peerDependencies" : { + "styled-components": ">= 6" + } }
Moving styled-components to devDependencies will guarantee that it wouldn't be installed along with your library (npm install or yarn add will ignore devDependencies when a library is installed).
Adding styled-components to peerDependencies will signal your library consumers that styled-components is not included with the library and they need to install it themselves.
The peerDependencies version range is deliberately permissive (>= 6) to allow future versions of styled-components to work automatically. Narrow the range with a patch update to your library if a breaking change is eventually introduced.
Removing styled-components from your library bundle
If you are bundling your library before shipping it, make sure that you are not bundling styled-components along with it. Here are some examples of how to do this with some popular module bundling tools:
With Microbundle
If you are using Microbundle, it will handle this step automatically. Microbundle treats every dependency in the peerDependencies list as external and excludes it from the build for you.
With Rollup.js
If you are using Rollup.js, you should provide an external option in your config:
export default { input: "my-awesome-library.js", + external: [ + "styled-components" + ] }
Another approach is to use the rollup-plugin-peer-deps-external plugin which will automatically add the peerDependencies in the external option array for you.
+ import peerDepsExternal from 'rollup-plugin-peer-deps-external'; export default { input: "my-awesome-library.js", + plugins: [ + // Preferably set as first plugin. + peerDepsExternal(), + ] }
With Webpack
If you are using Webpack, you should provide an externals option in your config:
module.exports = { input: "my-awesome-library.js", + externals: { + "styled-components": { + commonjs: "styled-components", + commonjs2: "styled-components", + amd: "styled-components", + }, + }, }
You can find more useful information on how to bundle a library with Webpack at "Authoring Libraries" section of Webpack documentation.
How do I fix flickering text after server side rendering?
When using global styling APIs like createGlobalStyle, adding and removing certain styles from the DOM like @font-face definitions can cause momentary flickering of text on the page. This typically happens during the rehydration phase of server-side rendering. We're still tweaking how these behaviors work to avoid the issue long-term.
However, there is a CSS solution to the problem in the font-display CSS rule. By setting the rule to "fallback" mode, once a font has been loaded it will not be reloaded. This eliminates the flicker.
@font-face { font-family: 'Foo'; src: url('/path/to/foo.woff') format('woff'); font-style: normal; font-weight: 400; font-display: fallback; /* <- this can be added to each @font-face definition */ }
How can I fix issues when using npm link or yarn link?
Local linking can be a useful tool to co-develop projects simultaneously. However, it creates chaotic situations with libraries that are meant to be used as singletons like react and styled-components since each of your local projects likely has a full set of development dependencies downloaded (and bundlers prefer local versions of dependencies by default.)
The solution is to add aliasing. Here's an example config for webpack:
// const path = require('path'); { resolve: { alias: { // adjust this path as needed depending on where your webpack config is 'styled-components': path.resolve('../node_modules/styled-components') } } }
This ensures that for your build the same copy of the library will always be used, even across symlinked projects.
Linking in an SSR SCENARIO.
If you are using the collectStyles function on a project with linked components you will end up in a complex scenario. Basically what's happening is that because of the context API different styled-component modules are now managing their own list of styled-components to render, from the host app it appears as though there's nothing to extract because no styled components have been created in that scope they were created from the linked package scope.
How do I solve it?
One solution is to add an alias to the styled-components module path resolution to always point to the 'host' application. Hopefully there are a bunch of libraries to do that we will use for this example module-alias. At the very top of your SSR index file add:
const path = require('path'); const moduleAlias = require('module-alias'); moduleAlias.addAlias('styled-components', path.join(__dirname, '../node_modules/styled-components'));
This will tell node to resolve all import/require's of styled-components to __dirname, '../node_modules/styled-components'
Missing Declarations for styled-components/native?
If you're getting the error message:
Could not find a declaration file for module 'styled-components/native'
If you're using styled-components v6+, TypeScript types are included out of the box and you should not need a separate types package.
For older versions, you need to install @types/styled-components-react-native to resolve this error.
What do I need to do to migrate to v5?
Historical note: This migration guide is for upgrading from v4 to v5 (released 2020). If you're on a recent version, see the v6 migration guide instead.
Ready for this?
npm install styled-components@^5.0.0 react@^16.8 react-dom@^16.8 react-is@^16.8
If you're using React Native, you'll need at least v0.59 (the first version to support hooks.)
That's it. 💥
styled-components v5 does not introduce any breaking public API changes, and adds the following:
-
Total rewrite of the core stylesheet engine, tuned for performance
-
New hooks-based component model
-
StyleSheetManagerhas new props:disableCSSOMInjectiondisableVendorPrefixesstylisPlugins- try it with
stylis-plugin-rtlfor your bidi needs!
- try it with
Note: The subfunction object-form .attrs({ prop: props => {} }) syntax that was deprecated in v4 is removed in v5. Use function-form attrs instead .attrs(props => ({})) (you should have been seeing console warnings to make this update ahead of time.)
Check out the official announcement post for more information and to learn about what went into v5!
For jest users
Update to jest-styled-components v7:
npm install jest-styled-components@^7.0.0
Note regarding css @import and createGlobalStyle
At this time we do not recommend using @import within cGS due to some issues with how browsers process @import via the CSSOM APIs. Instead it's best to place these in your core index.html file (generated or static) within a typical <style> tag.