styled-components has full theming support by exporting a <ThemeProvider> wrapper component. This component provides a theme to all React components underneath itself via the context API. In the render tree all styled-components will have access to the provided theme, even when they are multiple levels deep.
To illustrate this, let's create our Button component, but this time we'll pass some variables down as a theme.
You can also pass a function for the theme prop. This function will receive the parent theme, that is from another <ThemeProvider> higher up the tree. This way themes themselves can be made contextual.
This example renders our above themed Button and a second one that uses a second ThemeProvider to invert the background and foreground colors. The function invertTheme receives the upper theme and creates a new one.
If you ever need to use the current theme outside styled components (e.g. inside big components), you can use the withTheme higher order component.
You can also use useContext to access the current theme outside of styled components when working with React Hooks.
A theme can also be passed down to a component using the theme prop.
This is useful to circumvent a missing ThemeProvider or to override it.
Passing a ref prop to a styled component will give you one of two things depending on the styled target:
Using an older version of styled-components (below 4.0.0) or of React? Use the innerRef prop instead.
Since styled-components allows you to use arbitrary input as interpolations, you must be careful to sanitize that input. Using user input as styles can lead to any CSS being evaluated in the user's browser that an attacker can place in your application.
This example shows how bad user input can even lead to API endpoints being called on a user's behalf.
There are a couple of implementation details that you should be aware of, if you choose to use styled-components together with existing CSS.
styled-components generates an actual stylesheet with classes, and attaches those classes to the DOM nodes of styled components via the className prop. It injects the generated stylesheet at the end of the head of the document during runtime.
If you use the styled(MyComponent) notation and MyComponent does not render the passed-in className prop, then no styles will be applied. To avoid this issue, make sure your component attaches the passed-in className to a DOM node:
If you have pre-existing styles with a class, you can combine the global class with the passed-in one:
If you apply a global class together with a styled component class, the result might not be what you're expecting. If a property is defined in both classes with the same specificity, the last one will win.
In the above example the styled component class takes precedence over the global class, since styled-components injects its styles during runtime at the end of the <head> by default. Thus its styles win over other single classname selectors.
One solution is to bump up the specificity of the selectors in your stylesheet:
If you deploy styled-components on a page you don't fully control, you may need to take precautions to ensure that your component styles don't conflict with those of the host page.
The most common problem is insufficient specificity. For example, consider a host page with this style rule:
Since the rule contains a classname and two tag names, it has higher specificity than the single classname selector generated by this styled component:
There's no way to give your components complete immunity from the host page's styles, but you can at least boost the specificity of their style rules with babel-plugin-styled-components-css-namespace, which allows you to specify a CSS namespace for all of your styled components. A good namespace would be something like #my-widget, if all of your styled-components render in a container with id="my-widget", since ID selectors have more specificity than any number of classnames.
A rarer problem is conflicts between two instances of styled-components on the page. You can avoid this by defining process.env.SC_ATTR in the code bundle with your styled-components instance. This value overrides the default <style> tag attribute, data-styled (data-styled-components in v3 and lower), allowing each styled-components instance to recognize its own tags.
Tagged Template Literals are a new feature in ES6. They let you define custom string interpolation rules, which is how we're able to create styled components.
If you pass no interpolations, the first argument your function receives is an array with a string in it.
Once you pass interpolations, the array contains the passed string, split at the positions of the interpolations. The rest of the arguments will be the interpolations, in order.
This is a bit cumbersome to work with, but it means that we can receive variables, functions, or mixins (css helper) in styled components and can flatten that into pure CSS.
If you want to learn more about tagged template literals, check out Max Stoiber's article: The magic behind 💅🏾 styled-components
styled-components supports concurrent server side rendering, with stylesheet rehydration. The basic idea is that everytime you render your app on the server, you can create a ServerStyleSheet and add a provider to your React tree, that accepts styles via a context API.
This doesn't interfere with global styles, such as keyframes or createGlobalStyle and allows you to use styled-components with React DOM's various SSR APIs.
In order to reliably perform server side rendering and have the client side bundle pick up without issues, you'll need to use our babel plugin. It prevents checksum mismatches by adding a deterministic ID to each styled component. Refer to the tooling documentation for more information.
For TypeScript users, our resident TS guru Igor Oleinikov put together a TypeScript plugin for the webpack ts-loader / awesome-typescript-loader toolchain that accomplishes some similar tasks.
If possible, we definitely recommend using the babel plugin though because it is updated the most frequently. It's now possible to compile TypeScript using Babel, so it may be worth switching off TS loader and onto a pure Babel implementation to reap the ecosystem benefits.
The basic API goes as follows:
The collectStyles method wraps your element in a provider. Optionally you can use the StyleSheetManager provider directly, instead of this method. Just make sure not to use it on the client-side.
The sheet.getStyleTags() returns a string of multiple <style> tags. You need to take this into account when adding the CSS string to your HTML output.
Alternatively the ServerStyleSheet instance also has a getStyleElement() method that returns an array of React elements.
If rendering fails for any reason it's a good idea to use try...catch...finally to ensure that the sheet object will always be available for garbage collection. Make sure sheet.seal() is only called after sheet.getStyleTags() or sheet.getStyleElement() have been called otherwise a different error will be thrown.
sheet.getStyleTags() and sheet.getStyleElement() can only be called after your element is rendered. As a result, components from sheet.getStyleElement() cannot be combined with <YourApp /> into a larger component.
Basically you need to add a custom pages/_document.js (if you don't have one). Then copy the logic for styled-components to inject the server side rendered styles into the <head>.
Refer to our example in the Next.js repo for an up-to-date usage example.
Gatsby has an official plugin that enables server-side rendering for styled-components.
Refer to the plugin's page for setup and usage instructions.
styled-components offers a streaming API for use with ReactDOMServer.renderToNodeStream(). There are two parts to a streaming implementation:
On the server:
ReactDOMServer.renderToNodeStream emits a "readable" stream that styled-components wraps. As whole chunks of HTML are pushed onto the stream, if any corresponding styles are ready to be rendered, a style block is prepended to React's HTML and forwarded on to the client browser.
On the client:
After client-side rehydration is complete, styled-components will take over as usual and inject any further dynamic styles after the relocated streaming ones.
This is a web-specific API and you won't be able to use it in react-native.
There are many ways to apply contextual overrides to a component's styling. That being said, it rarely is easy without rigging up a well-known targeting CSS selector paradigm and then making them accessible for use in interpolations.
styled-components solves this use case cleanly via the "component selector" pattern. Whenever a component is created or wrapped by the styled() factory function, it is also assigned a stable CSS class for use in targeting. This allows for extremely powerful composition patterns without having to fuss around with naming and avoiding selector collisions.
A practical example: here, our Icon component defines its response to the parent Link being hovered:
We could have nested the color-changing rule within our Link component, but then we'd have to consider both sets of rules to understand why Icon behaves as it does.
This behaviour is only supported within the context of Styled Components: attempting to mount B in the following example will fail because component A is an instance of React.Component not a Styled Component.
The error thrown - Cannot call a class as a function - occurs because the styled component is attempting to call the component as an interpolation function.
However, wrapping A in a styled() factory makes it eligible for interpolation -- just make sure the wrapped component passes along className.