First, let's start by updating the necessary packages to their latest versions.
If you're using NPM:
npm install styled-components@^6.0.0 stylis@^4.0.0 npm uninstall @types/styled-components
If you're using Yarn:
yarn add styled-components@^6.0.0 stylis@^4.0.0 yarn remove @types/styled-components
As styled-components now provides its own types, there's no longer a need for community ones.
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 defaultIf 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; }
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.
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.
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; }
$as
and $forwardedAs
props have been droppedTo reduce confusion around application order, we've dropped the transient $as
and $forwardedAs
props. Please use the regular as
and forwardedAs
props instead.
withComponent()
APIThis 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">
In line with the maintenance schedule for Node, we now support v16 as the oldest runtime that's still receiving security patches.
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
StyleSheetManager
has new props:
disableCSSOMInjection
disableVendorPrefixes
stylisPlugins
stylis-plugin-rtl
for your bidi needs!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!
Update to jest-styled-components v7:
npm install jest-styled-components@^7.0.0
@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.
This is a pretty big release with lots of changes both under the hood and at the API level. As the beta progresses, we will try to release codemods to make the items below simpler. Also, if you find any issues with the steps below, please leave constructive feedback!
npm install styled-components@^4.0.0
react
>= 16.3; internally we are using the new React.forwardRef
API and new context APIs if you wish to try and polyfill for older React version supportnpm install react@^16.3 react-dom@^16.3
If you are using enzyme
or other dependencies like react-test-renderer
, there may be more related upgrades to complete if you are coming from an old version of react
.
.extend
API, switch your components to use styled(StyledComponent)
instead.A codemod is available to expedite this.
🚫
import styled from 'styled-components' const Component = styled.div` background: blue; color: red; ` const ExtendedComponent = Component.extend` color: green; `
✅
import styled from 'styled-components' const Component = styled.div` background: blue; color: red; ` const ExtendedComponent = styled(Component)` color: green; `
See the "extending styles" documentation for more examples.
injectGlobal
API to add global styles to your page, use the new createGlobalStyle
helper component instead.A codemod is available to expedite this.
🚫
import { injectGlobal } from 'styled-components' injectGlobal` body { color: red; } `
✅
import { createGlobalStyle } from "styled-components" const GlobalStyle = createGlobalStyle` body { color: red; } ` // later in your app's render method <React.Fragment> <Navigation /> <OtherImportantTopLevelComponentStuff /> <GlobalStyle /> </React.Fragment>
See the documentation for createGlobalStyle
to see all the cool stuff you can do with it that wasn't possible before with injectGlobal
!
innerRef
prop, change it to a normal ref
.🚫
const Component = styled.div` background: blue; color: red; ` // later in your render method <Component innerRef={element => { this.myElement = element }}>Something something</Component>
✅
const Component = styled.div` background: blue; color: red; ` // later in your render method <Component ref={element => { this.myElement = element }}>Something something</Component>
keyframes
component in a partial without the css
helper, you'll need to use the helper now. In general, always use the css helper when composing styling partials to be interpolated into a styled component.🚫
import styled, { keyframes } from 'styled-components' const animation = keyframes` 0% { opacity: 0; } 100 { opacity: 1; } ` const animationRule = ` ${animation} 1s infinite alternate ` const Component = styled.div` animation: ${animationRule}; `
✅
import styled, { css, keyframes } from 'styled-components' const animation = keyframes` 0% { opacity: 0; } 100 { opacity: 1; } ` const animationRule = css` ${animation} 1s infinite alternate; ` const Component = styled.div` animation: ${animationRule}; `
attrs({})
and some of the attributes you pass to it is a Function, it's recommended to switch to the new attrs(props => ({}))
syntax instead for easier and more powerful composition.🚫
import styled from 'styled-components' const Input = styled.input.attrs({ type: props => props.inputType, })` background: blue; color: red; `
✅
import styled from 'styled-components' const Input = styled.input.attrs(props => ({ type: props.inputType, }))` background: blue; color: red; `
npm install @types/styled-components
That's it! Aside from migrating, we also highly recommend reading up on the new "as" prop
which is intended to replace the withComponent API
in the future.
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!
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.
Note that for styled-components v3 and below, the previous API for global styles was injectGlobal
.
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!
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; } `
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.
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.
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:
styled-components
as external in your package dependenciesstyled-components
from your library bundlestyled-components
as external in your package dependenciesTo 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": "^3.4.9" }, + "peerDependencies" : { + "styled-components": ">= 3" + } }
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.
Also, note that in the peerDependencies
section the version string has been made a more permissive >= 3
. This allows future versions of styled-components to work automatically and you can simply narrow the range with a patch update to your library if a breaking change is eventually added.
styled-components
from your library bundleIf 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:
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.
If you are using Rollup.js, you should provide an external
option in your config:
export default { entry: "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 { entry: "my-awesome-library.js", + plugins: [ + // Preferably set as first plugin. + peerDepsExternal(), + ] }
If you are using Webpack, you should provide an externals
option in your config:
modules.export = { entry: "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.
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.
There are several common reasons for this to happen:
styled-components
running on the same page
(e.g., several entry points in webpack are loaded on the same page)styled-components
library somewhere in your dependenciesstyled-components
module is a dependency in more than one package (this one is more or less the same as the previous one)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 CommonsChunkPlugin
to create an explicit vendor chunk,
that will contain the styled-components
module:
module.exports = { entry: { + vendor: ["styled-components"], app1: "./src/app.1.js", app2: "./src/app.2.js", }, plugins: [ + new webpack.optimize.CommonsChunkPlugin({ + name: "vendor", + minChunks: Infinity, + }), ] }
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"), + } }
One possible fix to get styled-components to run in a Lerna monorepo across packages, is to hoist shared dependencies to the root of your monorepo file. Try running the bootstrap option with the --hoist flag.
lerna bootstrap --hoist
Alternatively, you can remove styled-components
from your package.json
file and hoist it manually to your top-level package.json file.
Example of a package.json file in a Lerna root folder
{ "name": "my-styled-monorepo", "devDependencies": { "lerna": "3.6.0" }, "dependencies": { "styled-components": "3.4.5" }, "scripts": { "bootstrap": "lerna bootstrap", "clean": "lerna clean", "start": "lerna run start", "build": "lerna run build" } }
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> ) }
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:
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 />
If you use a version < 5.1 or if you can't use transient props, 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
);
styled-components supports the same set of browsers as the current React version.
Evergreen browsers include Chrome and Firefox (and derivatives) as they can be updated regardless of operating system version. Edge and Safari should both also work fine since all versions for the last several years support the relevant APIs.
create-react-app
?The basic functionality of the library should work out of the box like any other library.
However, if you want to do server-side rendering or take advantage of some of the advanced capabilities of the styled-components babel plugin without ejecting you'll need to set up react-app-rewired
and react-app-rewire-styled-components
.
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.
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 v4 new static 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.
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'
When using global styling APIs like createGlobalStyle
or the former injectGlobal
, 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 */ }
styled-components/native
?If you're getting the error message:
Could not find a declaration file for module 'styled-components/native'
In a React Native project using TypeScript, it is because you need to add @types/styled-components-react-native
.