Introduction

Styled-components lets you write actual CSS inside your JavaScript files, scoped to a specific component. The styles travel with the component, so there are no class name collisions and no separate CSS file to hunt for when something looks wrong. If you've spent time debugging which global stylesheet is overriding which, the appeal is immediate.

Why Use Styled-Components?

Scoped Styling

Styles are automatically scoped to the component they're defined on. No BEM naming conventions, no worrying about cascade order across files.

Dynamic Styling

You can branch on props directly inside the template literal. Toggling a variant no longer requires maintaining parallel CSS classes and JavaScript logic.

Improved Maintainability

Styles and markup live in the same file. When a component gets deleted, its styles go with it. You don't discover orphaned CSS a year later.

Automatic Vendor Prefixing

Styled-components applies vendor prefixes for you, so you write standard CSS properties and it handles browser compatibility.

Getting Started with Styled-Components

Installation

npm install styled-components

or

yarn add styled-components

Basic Usage

Here's how to create a styled button component:

import React from 'react';
import styled from 'styled-components';

const Button = styled.button`
  background-color: #6200ee;
  color: white;
  padding: 10px 15px;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  
  &:hover {
    background-color: #3700b3;
  }
`;

const App = () => (
  <Button>Click Me</Button>
);

export default App;

styled.button returns a React component with the CSS applied. You use it like any other component.

Styling with Props

Styled-components allow you to pass props to your styled elements to conditionally apply styles.

const Button = styled.button`
  background-color: ${props => props.primary ? '#6200ee' : '#white'};
  color: ${props => props.primary ? 'white' : '#6200ee'};
  padding: 10px 15px;
  border: ${props => props.primary ? 'none' : '2px solid #6200ee'};
  border-radius: 5px;
  cursor: pointer;
  
  &:hover {
    background-color: ${props => props.primary ? '#3700b3' : '#f2e7fe'};
  }
`;

const App = () => (
  <>
    <Button primary>Primary Button</Button>
    <Button>Secondary Button</Button>
  </>
);

export default App;

By passing the primary prop, we can toggle the button's styles dynamically.

Extending Styled Components

You can create new styled components by extending existing ones:

const OutlineButton = styled(Button)`
  background-color: transparent;
  color: #6200ee;
  border: 2px solid #6200ee;
  
  &:hover {
    background-color: #f2e7fe;
  }
`;

OutlineButton inherits everything from Button and adds its own overrides.

Theming with Styled-Components

For consistent design tokens across a large component tree, use ThemeProvider.

import { ThemeProvider } from 'styled-components';

const theme = {
  primary: '#6200ee',
  secondary: '#03dac6',
};

const Button = styled.button`
  background-color: ${props => props.theme.primary};
  color: white;
  padding: 10px 15px;
`;

const App = () => (
  <ThemeProvider theme={theme}>
    <Button>Themed Button</Button>
  </ThemeProvider>
);

Update the theme object in one place and every component using props.theme picks up the change.

Creating a Complete Example Application

Here's a basic to-do app that demonstrates how these pieces fit together.

Setting Up the Project

npx create-react-app styled-components-todo
cd styled-components-todo
npm install styled-components

Structuring the App

Three components:

  • App: the main component and state container.
  • TodoList: renders the list of to-dos.
  • TodoForm: form for adding new to-dos.

App Component

// src/App.js
import React, { useState } from 'react';
import styled from 'styled-components';
import TodoForm from './TodoForm';
import TodoList from './TodoList';

const Container = styled.div`
  max-width: 500px;
  margin: 50px auto;
  padding: 20px;
  background-color: #f2e7fe;
  border-radius: 10px;
`;

const Title = styled.h1`
  text-align: center;
  color: #6200ee;
`;

const App = () => {
  const [todos, setTodos] = useState([]);

  const addTodo = text => {
    const newTodos = [...todos, { text }];
    setTodos(newTodos);
  };

  return (
    <Container>
      <Title>Styled Components To-Do App</Title>
      <TodoForm addTodo={addTodo} />
      <TodoList todos={todos} />
    </Container>
  );
};

export default App;

TodoForm Component

// src/TodoForm.js
import React, { useState } from 'react';
import styled from 'styled-components';

const Form = styled.form`
  display: flex;
  margin-bottom: 20px;
`;

const Input = styled.input`
  flex: 1;
  padding: 10px;
  border: 2px solid #6200ee;
  border-radius: 5px;
  margin-right: 10px;
  font-size: 16px;
`;

const Button = styled.button`
  background-color: #6200ee;
  color: white;
  padding: 0 20px;
  border: none;
  border-radius: 5px;
  font-size: 16px;
  cursor: pointer;
  
  &:hover {
    background-color: #3700b3;
  }
`;

const TodoForm = ({ addTodo }) => {
  const [value, setValue] = useState('');

  const handleSubmit = event => {
    event.preventDefault();
    if (!value) return;
    addTodo(value);
    setValue('');
  };

  return (
    <Form onSubmit={handleSubmit}>
      <Input
        type="text"
        placeholder="Add a new task"
        value={value}
        onChange={e => setValue(e.target.value)}
      />
      <Button type="submit">Add</Button>
    </Form>
  );
};

export default TodoForm;

TodoList Component

// src/TodoList.js
import React from 'react';
import styled from 'styled-components';

const List = styled.ul`
  list-style: none;
  padding-left: 0;
`;

const ListItem = styled.li`
  background-color: white;
  padding: 15px 20px;
  border-bottom: 1px solid #ddd;
  
  &:nth-child(even) {
    background-color: #fafafa;
  }
`;

const TodoList = ({ todos }) => (
  <List>
    {todos.map((todo, index) => (
      <ListItem key={index}>{todo.text}</ListItem>
    ))}
  </List>
);

export default TodoList;

Running the Application

npm start

Navigate to http://localhost:3000 to interact with your styled to-do app.

Conclusion

Styled-components is a practical choice when you want styles and components to stay in sync. Scoped CSS means no surprise overrides, prop-based styling replaces conditional class logic, and theming keeps design tokens consistent without CSS variables scattered across files.

The trade-off is bundle size and the additional runtime cost of generating class names. For most React applications that trade-off is worthwhile, but it's worth benchmarking if you're working on a performance-sensitive context.

Additional Tips

  • Use createGlobalStyle for base resets and font imports that should apply to the whole document.
  • When adding styled-components to an existing project, migrate one component at a time rather than doing a full rewrite.
  • The styled-components Babel plugin improves class name readability in DevTools and is worth adding to any non-trivial project.

Further Reading