Library / SDK
styled-components/styled-components avatar
styled-components/styled-components

styled-components 7: One Styling API Across Server Components, Streaming SSR and React Native

Fast, expressive styling for React. Server components, client components, streaming SSR, React Native—one API.

41,104 stars2,675 forksTypeScriptMIT

At a glance

What is it?
styled-components keeps CSS inside the component file and now targets React Server Components, streaming SSR and React Native through a single API. The v7 prereleases also replace the v6 vendor-prefix prop with a plugin hook, which changes how you configure browser support.
Who is it for?
Adopt styled-components if you already write React components in TypeScript and want CSS colocated with them, including in React Native, without adding a build plugin. Do not adopt it if you need a zero-runtime output, or if your team cannot absorb a project that the README says is largely maintained by one person and asks to be funded through Open Collective.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The problem styled-components solves for React teams

The project targets a specific friction point: writing CSS for React components usually means either maintaining a parallel stylesheet with class names that must be kept in sync with markup, or accepting a build-time CSS pipeline. styled-components takes the first option off the table. The README's framing is that you style components with real CSS, scoped automatically and delivered only when needed, with no class name juggling, no separate files and no build step required. That last clause is the differentiator. Several CSS-in-JS tools require a compiler plugin or a bundler integration; this one is a runtime, installed as a single npm package.

The intended audience is React developers working in TypeScript. The README states that built-in types ship with the package, props flow through to styles with full inference, and there is no @types install and no manual generics. That matters because the ergonomics of a styling library in a typed codebase depend less on the CSS syntax than on whether a prop like $primary is checked when you write the interpolation. The library also claims coverage of React Native, server components, client components and streaming SSR under one API, with automatic runtime detection. If that holds, it removes the need to pick a different styling tool per rendering mode, which is the practical reason teams end up with two systems in one repository.

How the runtime turns a template literal into a scoped class

The mechanism visible in the README is straightforward. You call styled.button with a tagged template literal, optionally parameterized with a TypeScript type, and the interpolation functions receive the component's props. In the dynamic props example, background and color are computed from props.$primary, so the same component produces different declarations per render. Transient props are prefixed with $, and the README's stated purpose is to keep those props off the DOM. That is the mechanism that prevents a styling-only flag from leaking into rendered HTML as an invalid attribute.

Scoping is handled by the library rather than by you. The README says class names are generated and that the ampersand in a nested selector refers to the component's generated class name, which is why pseudo-classes and pseudo-elements such as &:focus and &::placeholder work inside the template without you naming anything. Keyframes follow the same model: names are scoped automatically, so a rotate animation defined once can be referenced from multiple components without collisions. Theming is delivered through React context, and the README states that every styled component receives props.theme, which is what makes tokens reachable from any interpolation.

The v7 prereleases add a second path for theming. createTheme converts tokens into CSS custom properties, and the README gives the resolution form explicitly: theme.colors.fg becomes var(--sc-colors-fg, palevioletred). The stated benefit is that class name hashes stay stable across theme variants, so switching light and dark does not produce a hydration mismatch. That is a design decision worth noting, because it moves part of the theming work from JavaScript into the stylesheet, and it changes what a token is: a placeholder reference resolved at render time rather than a raw value.

Installing and running it without a build plugin

Setup is one command. The README gives npm install styled-components, with pnpm add styled-components and yarn add styled-components listed as equivalents. There is no bundler configuration in the install instructions, and no compiler step mentioned, which is consistent with the no build step claim in the opening description. A minimal component is a tagged template: import styled from 'styled-components', then const Button = styled.button<{ $primary?: boolean }>`...`, and render Button with or without the $primary prop.

Extension and polymorphism are both first-class. styled(Button) builds a variant on top of an existing styled component, and the as prop swaps the rendered element while keeping the styles, so Button as="a" with an href renders an anchor carrying the button styles. Animations import keyframes from the same package. Theming wraps the tree in ThemeProvider with a theme object, and every styled component below it can read props.theme.

The configuration surface that changed in v7 is vendor prefixing. CSS is emitted unprefixed by default, and the README says the v6 enableVendorPrefixes prop has been removed in favor of an opt-in plugin. You import StyleSheetManager and prefixPlugin from styled-components/plugins, then pass plugins={[prefixPlugin]} to StyleSheetManager at the subtree where you want it applied. The included prefix set targets Chrome 45, Firefox 36, Safari and iOS 9, and Edge 12, which the README describes as matching the browser floor for the JavaScript APIs React requires. If that floor is wrong for your users, the README offers two routes: declare both the prefixed and standard forms yourself, or extend the plugin with one of your own.

The plugin hook contract and why the miss path matters

The custom plugin example in the README defines an object with a name and a decl function that takes a property and a value. It returns an array of declarations for transform-style, or undefined otherwise. That undefined is not incidental. The README states that plugins apply left to right, and that a later decl runs on every declaration an earlier plugin emitted. So a custom plugin placed after prefixPlugin will see the prefixed declarations that prefixPlugin produced, and returning undefined on the miss path is what keeps it cheap. The README also advises skipping properties that already start with a hyphen, as prefixPlugin does, so that plugins can compose in either order without double-prefixing.

The types are scoped to a subpath. SCPlugin, DeclResult, DeclTransform and SelectorTransform are exported only from styled-components/plugins, not from the package root, so an import from 'styled-components' will not find them. The full hook contract is documented under authoring custom plugins on the project site, according to the README. This is the part of v7 most likely to cause a quiet problem during migration: a custom plugin that returns an empty array instead of undefined, or that transforms a property it did not check for a leading hyphen, will still run and may still produce output, just with duplicated declarations. The failure is visible in the emitted CSS rather than as an error.

Where the runtime model becomes the wrong choice

The README states the package is under 13kB gzipped, and that it requires no build plugin. Those two facts are the same fact seen from different sides: the work happens at runtime, in the browser, rather than at build time. For an application that renders a large number of distinct styled components, the cost is paid on the client. The README does not publish a benchmark for interpolation or class generation, and I have not measured it, so the honest position is that the size figure is documented and the runtime cost is not quantified in the README.

The more concrete limitation is in the theming story. createTheme tokens are described as placeholder references that resolve at render time, not raw values, and the README explicitly warns not to combine them with JavaScript arithmetic. That is a real constraint on how you write token-driven styles: a token can be interpolated anywhere a CSS value goes, but it cannot be treated as a number in JavaScript. Teams that compute spacing scales or color mixes in JavaScript and pass the result into an interpolation will find that pattern does not carry over unchanged.

Vendor prefixing is a second constraint, and it is narrower than it looks. The default output is unprefixed, and the bundled prefixPlugin set is pinned to a browser floor chosen to match React's own JavaScript requirements. If your support matrix includes browsers below that floor for CSS reasons, the built-in plugin is not a configuration option you can tune; you write the prefixed declarations yourself or author a plugin. The README points to the v7 documentation for the hook contract, and that documentation is where the actual rules live.

How it differs from a zero-runtime compiler approach

The clearest alternative in this space is a zero-runtime CSS-in-JS compiler, which extracts styles at build time so that no styling runtime ships to the browser. The difference is architectural rather than stylistic. A compiler approach moves the interpolation of props into a build step and emits static CSS, which means the bundle carries the stylesheet and not the library that produced it. styled-components keeps the interpolation in the component and evaluates it at render, which is what allows the same API to work in React Native and in server components without a separate extraction pass.

That trade has a visible consequence. With a compiler, a dynamic value that depends on runtime state has to be expressed as a CSS custom property, because the build step cannot know the value. With styled-components, the interpolation function runs and can read props directly, as the dynamic props example shows. The README's createTheme is the bridge between the two worlds: it converts tokens into CSS custom properties and keeps class name hashes stable across theme variants, which is the same stability property a compiler gets by construction. So the two systems are not far apart on theming. They differ on where the work happens and on whether you accept a runtime in exchange for not maintaining a build integration.

A second comparison point is React Native. The README claims the same API covers React Native, server components, client components and streaming SSR with automatic runtime detection. A zero-runtime web compiler does not address React Native at all, because there is no stylesheet to extract into. If your codebase shares component code between web and native, that coverage is the reason to look here first.

Maintenance, funding and the licence

The README is unusually direct about maintenance. It states that styled-components is largely maintained by one person and asks readers to help fund the project for consistent long-term support and updates through Open Collective. That is a governance fact, not a quality judgement, and it belongs in an adoption decision. A single maintainer is a bus-factor consideration: the pace of the v7 prereleases visible in the release list shows active work, but the README's own framing tells you the continuity depends on funding.

The licence is MIT, which permits commercial use, modification and redistribution with the licence and copyright notice retained. That is a permissive licence and it does not impose copyleft obligations on your application. I am not a lawyer and this is not legal advice; if your organisation has a policy on dependency licences, the MIT text is the thing to check against it.

Upgrade cost for v7 is concentrated in two places. The enableVendorPrefixes prop from v6 is removed, so any StyleSheetManager usage that relied on it must move to the prefixPlugin, and the plugin import path is styled-components/plugins rather than the package root. The second is the plugin contract itself: if you already author plugins, verify that the miss path returns undefined and that you skip properties beginning with a hyphen, because those two rules are what the README gives for keeping composition cheap and avoiding double-prefixing. Everything else in the README's examples, including transient props, extension via styled(Button), the as prop, keyframes and ThemeProvider, is presented as the current API without a migration note attached.

Editorial conclusion

Adopt styled-components if you already write React components in TypeScript and want CSS colocated with them, including in React Native, without adding a build plugin. Do not adopt it if you need a zero-runtime output, or if your team cannot absorb a project that the README says is largely maintained by one person and asks to be funded through Open Collective. Before upgrading, verify three things against your own code: whether you relied on the v6 enableVendorPrefixes prop, which is removed in favor of the prefixPlugin, whether your custom plugin returns undefined on the miss path so it composes cheaply, and whether your theme tokens are plain values or createTheme placeholders, since the README warns against combining placeholders with JavaScript arithmetic.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. styled-components/styled-components on GitHub
Community notes

Community notes