Basics

Motivation

This talk by Max Stoiber is a really thorough introduction to styled-components and goes through what the motivations behind its creation were, along with some other information to get started with.

Installation

Install styled-components from npm:

npm install --save styled-components

Getting Started

styled-components utilises tagged template literals to style your components.

It removes the mapping between components and styles. This means that when you're defining your styles, you're actually creating a normal React component, that has your styles attached to it.

This example creates two simple components, a wrapper and a title, with some styles attached to it. You can edit the code and get a feel for how you'd work with styled-components.

// Create a Title component that'll render an <h1> tag with some styles
const Title = styled.h1`
	font-size: 1.5em;
	text-align: center;
	color: palevioletred;
`;

// Create a Wrapper component that'll render a <section> tag with some styles
const Wrapper = styled.section`
	padding: 4em;
	background: papayawhip;
`;

// Use Title and Wrapper like any other React component – except they're styled!
render(
	<Wrapper>
		<Title>
			Hello World, this is my first styled component!
		</Title>
	</Wrapper>
);

Hello World, this is my first styled component!

NoteThe CSS rules are automatically vendor prefixed, so you don't have to think about it.

Passed props

styled-components pass on all their props.

This example shows how all props of the Input component are passed on to the DOM node that is mounted, as with React elements.

// Create an Input component that'll render an <input> tag with some styles
const Input = styled.input`
	padding: 0.5em;
	margin: 0.5em;
	color: palevioletred;
	background: papayawhip;
	border: none;
	border-radius: 3px;
`;

// Render a styled text input with a placeholder of "@mxstbr", and one with a value of "@geelen"
render(
	<div>
		<Input placeholder="@mxstbr" type="text" />
		<Input value="@geelen" type="text" />
	</div>
);

Adapting based on props

You can pass a function ("interpolations") to a styled component's template literal to adapt it based on its props.

This button component has a primary state that changes its colour. When setting the primary prop to true, we are swapping out its background and text colour.

const Button = styled.button`
	/* Adapt the colours based on primary prop */
	background: ${props => props.primary ? 'palevioletred' : 'white'};
	color: ${props => props.primary ? 'white' : 'palevioletred'};

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

render(
	<div>
		<Button>Normal</Button>
		<Button primary>Primary</Button>
	</div>
);

Styling any components

The styled method works perfectly on all of your own or any third-party components as well, as long as they're accepting the className prop.

NoteIf you are using react-native keep in mind to use style instead of className.

If you're using any external library, you can consider using this pattern to turn them into styled components. The same pattern works for your own components as well, if you need some components to stay unstyled on their own.

// This could be react-router's Link for example
const Link = ({ className, children }) => (
	<a className={className}>
		{children}
	</a>
)

const StyledLink = styled(Link)`
	color: palevioletred;
	font-weight: bold;
`;

render(
	<div>
		<Link>Unstyled, boring Link</Link>
		<br />
		<StyledLink>Styled, exciting Link</StyledLink>
	</div>
);

You can also pass tag names into the styled() call, like so: styled('div').
In fact, the styled.tagname helpers are just aliases.

Notestyled-components always generates a real stylesheet with classes.
The classnames are then passed to the React component (including third party components) via the className prop.

Extending Styles
v2

Quite frequently you might want to use a component, but change it slightly for a single case. Now you could pass in an interpolated function and change them based on some props, but that's quite a lot of effort for overriding the styles once.

To do this in an easier way you can call extend on the component to generate another. You style it like any other styled component. It overrides duplicate styles from the initial component and keeps the others around.

Here we use the button from the last section and create a special one, extending it with some colour-related styling.

// The Button from the last section without the interpolations
const Button = styled.button`
	color: palevioletred;
	font-size: 1em;
	margin: 1em;
	padding: 0.25em 1em;
	border: 2px solid palevioletred;
	border-radius: 3px;
`;

// We're extending Button with some extra styles
const TomatoButton = Button.extend`
	color: tomato;
	border-color: tomato;
`;

render(
	<div>
		<Button>Normal Button</Button>
		<TomatoButton>Tomato Button</TomatoButton>
	</div>
);

We can see that the new TomatoButton still resembles Button, while we have only added two new rules.

NoteThis is different from passing your styled component into styled i.e. styled(Button).
Calling extend creates a new stylesheet by extending the old one, and thus doesn't generate two classes.

In really rare cases you might want to change which tag or component a styled component renders. For this case, we have an escape hatch. You can use the withComponent to extend the styles and use a different tag altogether.

const Button = styled.button`
	display: inline-block;
	color: palevioletred;
	font-size: 1em;
	margin: 1em;
	padding: 0.25em 1em;
	border: 2px solid palevioletred;
	border-radius: 3px;
`;

// We're replacing the <button> tag with an <a> tag, but reuse all the same styles
const Link = Button.withComponent('a')

// Use .withComponent together with .extend to both change the tag and use additional styles
const TomatoLink = Link.extend`
	color: tomato;
	border-color: tomato;
`;

render(
	<div>
		<Button>Normal Button</Button>
		<Link>Normal Link</Link>
		<TomatoLink>Tomato Link</TomatoLink>
	</div>
);

Attaching additional props
v2

To avoid unnecessary wrappers that just pass on some props to the rendered component, or element, you can use the .attrs constructor. It allows you to attach additional props (or "attributes") to a component.

This way you can for example attach static props to an element, or pass a prop third-party prop like activeClassName to React Router's Link component. Furthermore you can also attach more dynamic props to a component. The .attrs object also takes functions, that receive the props that the component receives. The return value will be merged into the resulting props as well.

Here we render an Input component and attach some dynamic and static attributes to it:

const Input = styled.input.attrs({
	// we can define static props
	type: 'password',

	// or we can define dynamic ones
	margin: props => props.size || '1em',
	padding: props => props.size || '1em'
})`
	color: palevioletred;
	font-size: 1em;
	border: 2px solid palevioletred;
	border-radius: 3px;

	/* here we use the dynamically computed props */
	margin: ${props => props.margin};
	padding: ${props => props.padding};
`;

render(
	<div>
		<Input placeholder="A small text input" size="1em" />
		<br />
		<Input placeholder="A bigger text input" size="2em" />
	</div>
);

As you can see, we get access to our newly created props in the interpolations, and the type attribute is passed down to the element.

Animations

CSS animations with @keyframes aren't scoped to a single component but you still don't want them to be global. This is why we export a keyframes helper which will generate a unique name for your keyframes. You can then use that unique name throughout your app.

This way, you get all the benefits of using JavaScript, are avoiding name clashes and get your keyframes like always:

// keyframes returns a unique name based on a hash of the contents of the keyframes
const rotate360 = keyframes`
	from {
		transform: rotate(0deg);
	}

	to {
		transform: rotate(360deg);
	}
`;

// Here we create a component that will rotate everything we pass in over two seconds
const Rotate = styled.div`
	display: inline-block;
	animation: ${rotate360} 2s linear infinite;
	padding: 2rem 1rem;
	font-size: 1.2rem;
`;

render(
	<Rotate>&lt; 💅 &gt;</Rotate>
)
< 💅 >

React Native

styled-components can be used with React Native in the same way, except you import it from styled-components/native instead.

import styled from 'styled-components/native';

const StyledView = styled.View`
  background-color: papayawhip;
`;

const StyledText = styled.Text`
  color: palevioletred;
`;

class MyReactNativeComponent extends React.Component {
  render() {
    return (
      <StyledView>
        <StyledText>Hello World!</StyledText>
      </StyledView>
    )
  }
}

We also support more complex styles (like transform), which would normally be an array, and shorthands (e.g. for margin) thanks to css-to-react-native!

Imagine how you'd write the property in React Native, guess how you'd transfer it to CSS, and you're probably right:

const RotatedBox = styled.View`
  transform: rotate(90deg);
  text-shadow-offset: 10px 5px;
  font-variant: small-caps;
  margin: 5px 7px 2px;
`;

Some of the differences to the web-version are, that you cannot use the keyframes and injectGlobal helpers since React Native doesn't support keyframes or global styles. We will also warn you if you use media queries or nest your CSS.

NoteIn v2 we support percentages. To make this possible we need to enforce units for all shorthands. If you're migrating to v2, a codemod is available.

Continue on the next page

Advanced