Advanced

Theming

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.

// Define our button, but with the use of props.theme this time
const Button = styled.button`
	font-size: 1em;
	margin: 1em;
	padding: 0.25em 1em;
	border-radius: 3px;

	/* Color the border and text with theme.main */
	color: ${props => props.theme.main};
	border: 2px solid ${props => props.theme.main};
`;

// We're passing a default theme for Buttons that aren't wrapped in the ThemeProvider
Button.defaultProps = {
	theme: {
		main: 'palevioletred'
	}
}

// Define what props.theme will look like
const theme = {
	main: 'mediumseagreen'
};

render(
	<div>
		<Button>Normal</Button>

		<ThemeProvider theme={theme}>
			<Button>Themed</Button>
		</ThemeProvider>
	</div>
);

Function themes

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 colours. The function invertTheme receives the upper theme and creates a new one.

// Define our button, but with the use of props.theme this time
const Button = styled.button`
	color: ${props => props.theme.fg};
	border: 2px solid ${props => props.theme.fg};
	background: ${props => props.theme.bg};

	font-size: 1em;
	margin: 1em;
	padding: 0.25em 1em;
	border-radius: 3px;
`;

// Define our `fg` and `bg` on the theme
const theme = {
	fg: 'palevioletred',
	bg: 'white'
};

// This theme swaps `fg` and `bg`
const invertTheme = ({ fg, bg }) => ({
	fg: bg,
	bg: fg
});

render(
	<ThemeProvider theme={theme}>
		<div>
			<Button>Default Theme</Button>

			<ThemeProvider theme={invertTheme}>
				<Button>Inverted Theme</Button>
			</ThemeProvider>
		</div>
	</ThemeProvider>
);

Getting the theme without styled components

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.

import { withTheme } from 'styled-components'

class MyComponent extends React.Component {
  render() {
    console.log('Current theme: ', this.props.theme);
    // ...
  }
}

export default withTheme(MyComponent)

The theme prop

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.

// Define our button
const Button = styled.button`
	font-size: 1em;
	margin: 1em;
	padding: 0.25em 1em;
	border-radius: 3px;

	/* Color the border and text with theme.main */
	color: ${props => props.theme.main};
	border: 2px solid ${props => props.theme.main};
`;

// Define what main theme will look like
const theme = {
	main: 'mediumseagreen'
};

render(
	<div>
		<Button theme={{ main: 'royalblue' }}>Ad hoc theme</Button>
		<ThemeProvider theme={theme}>
			<div>
				<Button>Themed</Button>
				<Button theme={{ main: 'darkorange' }}>Overidden</Button>
			</div>
		</ThemeProvider>      
	</div>
);

Refs

Passing a ref prop to a styled component will give you an instance of the StyledComponent wrapper, but not to the underlying DOM node. This is due to how refs work. It's not possible to call DOM methods, like focus, on our wrappers directly.

To get a ref to the actual, wrapped DOM node, pass the callback to the innerRef prop instead.

Note

We don't support string refs (i.e. innerRef="node"), since they're already deprecated in React.

This example uses innerRef to save a ref to the styled input and focuses it once the user hovers over it.

const Input = styled.input`
	padding: 0.5em;
	margin: 0.5em;
	color: palevioletred;
	background: papayawhip;
	border: none;
	border-radius: 3px;
`;

class Form extends React.Component {
	render() {
		return (
			<Input
				placeholder="Hover here..."
				innerRef={x => { this.input = x }}
				onMouseEnter={() => this.input.focus()}
			/>
		);
	}
}

render(
	<Form />
);

Security

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.

// Oh no! The user has given us a bad URL!
const userInput = '/api/withdraw-funds';

const ArbitraryComponent = styled.div`
  background: url(${userInput});
  /* More styles here... */
`;

Be very careful! This is obviously a made-up example, but CSS injection can be unobvious and have bad repercussions. Some IE versions even execute arbitrary JavaScript within url declarations.

There is an upcoming standard to sanitize CSS from JavaScript, CSS.escape. It's not very well supported across browsers yet, so we recommend using the polyfill by Mathias Bynens in your app.

Existing CSS

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.

Styling normal React components

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:

class MyComponent extends React.Component {
  render() {
    // Attach the passed-in className to the DOM node
    return <div className={this.props.className} />;
  }
}

If you have pre-existing styles with a class, you can combine the global class with the passed-in one:

class MyComponent extends React.Component {
  render() {
    // Attach the passed-in className to the DOM node
    return <div className={`some-global-class ${this.props.className}`} />;
  }
}

Issues with Specificity

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 specificty, the last one will win.

// MyComponent.js
const MyComponent = styled.div`background-color: green;`;

// my-component.css
.red-bg {
  background-color: red;
}

// For some reason this component still has a green background,
// even though you're trying to override it with the "red-bg" class!
<MyComponent className="red-bg" />

In the above example the styled component class takes precendence over the global class, since styled-components injects its styles during runtime into the DOM at the end of the head. Thus its styles win over the other ones.

Since it's often hard to control where your global CSS is injected into the DOM with tools like Webpack, the easiest thing is to bump up the specificity of your global class by repeating the classname:

/* my-component.css */
.red-bg.red-bg {
  background-color: red;
}

Media Templates

Media queries are an indispensable tool when developing responsive web apps.

This is a very simple example. It shows a basic component changing its background colour, once the screen's width drops below a threshold of 700px.

const Content = styled.div`
	background: papayawhip;
	height: 3em;
	width: 3em;

	@media (max-width: 700px) {
		background: palevioletred;
	}
`;

render(
	<Content />
);

Since media queries are long and are often repeated throughout an application, it can be useful to create a template for them.

Due to the functional nature of JavaScript, you can easily define your own tagged template literal to wrap styles in media queries. Let's rewrite the last example to try just that out.

const sizes = {
	desktop: 992,
	tablet: 768,
	phone: 376
}

// Iterate through the sizes and create a media template
const media = Object.keys(sizes).reduce((acc, label) => {
	acc[label] = (...args) => css`
		@media (max-width: ${sizes[label] / 16}em) {
			${css(...args)}
		}
	`

	return acc
}, {})

const Content = styled.div`
	height: 3em;
	width: 3em;
	background: papayawhip;

	/* Now we have our methods on media and can use them instead of raw queries */
	${media.desktop`background: dodgerblue;`}
	${media.tablet`background: mediumseagreen;`}
	${media.phone`background: palevioletred;`}
`;

render(
	<Content />
);

Tagged Template Literals

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.

// These are equivalent:
fn`some string here`;
fn([ 'some string here' ]);

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.

const aVar = 'good';

// These are equivalent:
fn`this is a ${aVar} day`;
fn([ 'this is a ', ' day' ], aVar);

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

Server Side Rendering
v2

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 injectGlobal and allows you to use styled-components with React DOM's SSR, or even Rapscallion.

The basic API goes as follows:

import { renderToString } from 'react-dom/server'
import { ServerStyleSheet } from 'styled-components'

const sheet = new ServerStyleSheet()
const html = renderToString(sheet.collectStyles(<YourApp />))
const styleTags = sheet.getStyleTags() // or sheet.getStyleElement()

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.

import { renderToString } from 'react-dom/server'
import { ServerStyleSheet, StyleSheetManager } from 'styled-components'

const sheet = new ServerStyleSheet()
const html = renderToString(
  <StyleSheetManager sheet={sheet.instance}>
    <YourApp />
  </StyleSheetManager>
)

const styleTags = sheet.getStyleTags() // or sheet.getStyleElement()

The sheet.getStyleTags() returns a string of multiple &lt;style&gt; 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.

Next.js

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>.

You'll also need to customize the .babelrc and use babel-plugin-styled-components.

Refer to our example in the Next.js repo for an up-to-date usage example.

Continue on the next page

API Reference