CSS-in-JS

styled-components

styled-components is the CSS-in-JS library that lets you write real CSS inside JavaScript, scoped to a component and driven by its props.

intermediate13 min readUpdated Sep 15, 2026
Button.jsx
jsx
// Button.jsx
import styled from "styled-components";

export const Button = styled.button`
  padding: 0.5rem 1rem;
  border: none;
  border-radius: 0.5rem;
  color: white;
  background: ${(props) => (props.$primary ? "#2563eb" : "#27272a")};

  &:hover { filter: brightness(1.1); }
`;
Approach
CSS-in-JS
Syntax
Tagged template literals
Scoping
Automatic per component
Dynamic
Driven by props
Theming
ThemeProvider
Runtime
Injects styles at runtime

Why it matters

Why teams choose styled-components

Styles driven by props

Values can be computed from component props, so variants and themes need no separate class combinations.

Everything colocated

Markup, behaviour and styles live in one file, which keeps a component self-contained and easy to move.

No naming fatigue

You never invent a class name again. The library generates unique selectors and guarantees no collisions.

The big picture

The three ideas behind styled-components

A tagged template, a props function and a theme context. Everything else is built from those three.

Template literals

Authoring

Write CSS in a tagged template, with full nesting, media queries and pseudo-selectors.

Props

Adapting

Interpolate functions that receive props to change styles based on component state.

The theme

Consistency

A context provider shares design tokens across every styled component in the tree.

styled-components at a glance

What you get

styled.tag

Create a component with attached styles using any HTML tag or existing component.

Props interpolation

Change values based on props for variants, sizes and states.

Extending

Build on an existing styled component with styled(Base) or the as prop.

createGlobalStyle

Add resets, fonts and base styles to the document from JavaScript.

keyframes

Define reusable animations and reference them inside a styled component.

attrs

Attach default props and attributes to a styled component once.

A short history

The rise of CSS-in-JS

  1. 2016

    First release

    styled-components brings the CSS-in-JS idea to React with an ergonomic tagged-template API.

    16
  2. 2017

    Rapid adoption

    Theming, server rendering and a rich ecosystem make it a common React styling choice.

    17
  3. 2019

    Hooks era

    useTheme and a smaller API align the library with modern React patterns.

    19
  4. 2023

    v6

    A lighter runtime and broader tooling support arrive as alternatives gain ground.

    23
  5. Today

    A considered choice

    Still capable, but often weighed against zero-runtime options like CSS Modules and Tailwind.

    Today

The complete guide

styled-components: Everything you need to know

What is styled-components?

styled-components is a CSS-in-JS library. Instead of writing a stylesheet and referencing classes, you write CSS inside a JavaScript file and attach it to a component. The library generates a unique class name, injects the rule into the page and hands you back a normal React component.

// Button.jsx
import styled from "styled-components";

const Button = styled.button`
  padding: 0.5rem 1rem;
  border: none;
  border-radius: 0.5rem;
  background: #2563eb;
  color: white;
`;

export default function App() {
  return <Button>Save</Button>;
}

The styled.button call is a tagged template: the CSS string is parsed by the library, not by the browser. The result is a component you can render, style, extend and theme like any other.

Writing CSS in a template

Inside the template you write ordinary CSS, including nesting, media queries and pseudo-selectors. The ampersand refers to the generated class.

// Card.jsx
const Card = styled.article`
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 1rem;
  padding: 1.5rem;
  transition: border-color 0.2s;

  &:hover {
    border-color: rgba(255, 255, 255, 0.2);
  }

  @media (min-width: 768px) {
    padding: 2rem;
  }
`;

Because the styles are scoped to the generated class, you can write the natural selector without worrying about the rest of the app.

Dynamic styles with props

The real advantage appears when styles depend on data. Any function inside the template receives the component’s props and can return a value.

// Badge.jsx
const Badge = styled.span`
  display: inline-flex;
  align-items: center;
  padding: 0.125rem 0.5rem;
  border-radius: 999px;
  font-size: 0.75rem;
  background: ${(props) => (props.$tone === "success" ? "#16a34a" : "#dc2626")};
  color: white;
`;

<Badge $tone="success">Active</Badge>;

Prefix transient props with $ so they are consumed by the style function and not forwarded to the DOM element, which avoids React warnings about unknown attributes.

Extending and composing

You can build on an existing styled component, which keeps a shared base in one place.

// Buttons.jsx
const Button = styled.button`
  padding: 0.5rem 1rem;
  border: none;
  border-radius: 0.5rem;
  font-weight: 500;
`;

const Primary = styled(Button)`
  background: #2563eb;
  color: white;
`;

const Ghost = styled(Button)`
  background: transparent;
  color: #e5e7eb;
  border: 1px solid rgba(255, 255, 255, 0.15);
`;

The as prop lets a component render a different element while keeping its styles, which is useful for semantic HTML.

// as.jsx
<Button as="a" href="/docs">Read the docs</Button>

Theming

The ThemeProvider shares design tokens with every styled component below it. Components read them from props.theme.

// theme.jsx
import { ThemeProvider } from "styled-components";

const theme = {
  colors: { primary: "#2563eb", text: "#e5e7eb" },
  space: { sm: "0.5rem", md: "1rem", lg: "2rem" },
};

function App() {
  return (
    <ThemeProvider theme={theme}>
      <Page />
    </ThemeProvider>
  );
}
// Page.jsx
const Wrapper = styled.main`
  color: ${(props) => props.theme.colors.text};
  padding: ${(props) => props.theme.space.lg};
`;

To support multiple themes, swap the object passed to ThemeProvider. Every component re-renders with the new tokens, which makes light and dark modes straightforward.

Global styles and animations

createGlobalStyle injects document-level CSS such as resets, fonts and base typography.

// Global.jsx
import { createGlobalStyle } from "styled-components";

export const Global = createGlobalStyle`
  *, *::before, *::after { box-sizing: border-box; }

  body {
    margin: 0;
    font-family: system-ui, sans-serif;
    background: #0b0d0c;
    color: #e5e7eb;
  }
`;

keyframes defines reusable animations that you reference inside a styled component.

// spinner.jsx
import { keyframes } from "styled-components";

const spin = keyframes`
  to { transform: rotate(360deg); }
`;

const Spinner = styled.div`
  width: 2rem;
  height: 2rem;
  border: 2px solid rgba(255, 255, 255, 0.2);
  border-top-color: #2563eb;
  border-radius: 50%;
  animation: ${spin} 0.8s linear infinite;
`;

Attrs and defaults

attrs attaches default props or attributes once, so every instance shares them.

// input.jsx
const Input = styled.input.attrs({
  type: "text",
  autoComplete: "off",
})`
  padding: 0.5rem 0.75rem;
  border-radius: 0.5rem;
`;

Trade-offs to understand

styled-components is ergonomic, but it is not free.

  • Runtime cost. Styles are generated and injected while the app runs, which adds work on the main thread.
  • Bundle size. The library itself ships to the client.
  • Server rendering. You must collect styles during SSR to avoid a flash of unstyled content.
  • React Server Components. The library relies on client context and is not compatible with RSC, which is a significant limitation for modern React apps.
  • Debugging. Generated class names are less readable in DevTools than hand-written ones.

None of these are fatal, but they are why many teams now prefer zero-runtime options. CSS Modules scope plain CSS with no runtime, Tailwind keeps styles in the markup, and vanilla-extract or StyleX extract static CSS from JavaScript at build time.

When to use styled-components

Reach for it when dynamic, prop-driven styles are central to your components, when you value colocating styles with logic, and when your app is a client-rendered React application without strict runtime budgets. It is a pleasure to use and makes theming clean.

Consider alternatives when you need the smallest possible runtime, when you are building with React Server Components, or when your team prefers writing CSS in .css files. The best choice depends on your constraints, not on which library is trending.

Best practices

  • Use transient props ($prop) so styling props never reach the DOM.
  • Define a small, explicit set of variants instead of many nested conditionals.
  • Keep shared values in the theme and read them from props.theme.
  • Use createGlobalStyle for resets and fonts, and styled components for everything else.
  • Split components into their own files as they grow.
  • Add a ServerStyleSheet when you render on the server.
  • Measure the runtime cost if your app is large or performance-sensitive.

Common mistakes

  • Passing styling props without $ and triggering DOM attribute warnings.
  • Recreating a styled component inside the render of another component, which remounts it every time.
  • Building every style from props and losing readability.
  • Forgetting SSR style collection and shipping a flash of unstyled content.
  • Assuming it works with React Server Components when it does not.
  • Choosing it by default without weighing the runtime cost.

Where to go next

styled-components is one answer to component styling. Compare it with CSS Modules for zero-runtime scoping and Tailwind CSS for utilities in markup, and lean on React fundamentals to get the most from the component model.

Dynamic styles

With styled-components a prop changes the style directly. The CSS version needs a class for every variant.

styled-components
const Badge = styled.span`
  background: ${(p) => (p.$ok ? "#16a34a" : "#dc2626")};
`;

<Badge $ok>Saved</Badge>
Plain CSS
.badge--ok { background: #16a34a; }
.badge--error { background: #dc2626; }

/* pick the class in JS */

Reusing styles

Extending keeps the base styles and adds to them, instead of repeating every declaration.

Prefer
const Base = styled.button`
  padding: 0.5rem 1rem;
  border-radius: 0.5rem;
`;

const Danger = styled(Base)`
  background: #dc2626;
`;
Avoid
const Danger = styled.button`
  padding: 0.5rem 1rem;
  border-radius: 0.5rem;
  background: #dc2626;
`;

FAQ

Frequently asked questions

Keep learning

Related topics from the roadmap.

$ start learning

Ready to start learning styled-components?

Our interactive tutorial walks you through styled-components step by step — with quizzes and real code you can run in the browser.