Migration from styled-components
Move from styled-components to yak, a fast alternative that works with React Server Components. Most code only needs a new import. This guide covers the rest.
Guide
One of yak's core goals is an easy migration from styled-components.
This guide walks you through the migration, including common scenarios and edge cases.
ESLint plugin
To make the migration easier, our ESLint plugin flags potential issues during the migration and afterwards.
See the ESLint plugin documentation to install it in your project.
Feature comparison
Supported
- Styled syntax (incl. static and dynamic parts)
- Css tagged template literal
- Css prop
- Keyframes
- Attrs
- References to other components
- Mixins
- Global styles (
createGlobalStylebecomesglobalStyle) - If some features are missing, please let us know and open a feature request on GitHub
Missing
- Object syntax
cssprop values that are not written in place, like variables, arrays or objectsasprop: you can replace it with a type-safe alternative.withConfig
Just change the import
The following cases need the least work, and all other changes build on this one.
Change the import from styled-components to @yak/react and change the import of styled from a default import to a named import.
import styled, { css, keyframes } from "styled-components";
import { styled, css, keyframes } from "@yak/react";Static component styles
For components with static styles, updating the import is enough:
import styled from "styled-components";
import { styled } from "@yak/react";
const Button = styled.button`
background: #bf4f74;
color: white;
padding: 1em 2em;
border-radius: 4px;
transition: all 0.2s ease-in-out;
&:hover {
transform: translateY(-1px);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
`;Static mixins
For static mixins, you can just change the import and you're done.
import styled, { css } from "styled-components";
import { css, styled } from "@yak/react";
const mixin = css`
color: green;
font-size: 1rem;
`;
const MyComp = styled.div`
background-color: yellow;
${mixin};
`;This applies to all static mixins that you use in your styled components, including exported mixins.
Keyframes
For keyframes you can just change the import and you're done.
import styled, { keyframes } from "styled-components";
import { styled, keyframes } from "@yak/react";
const rotate = keyframes`
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
`;
const Rotate = styled.div`
display: inline-block;
animation: ${rotate} 2s linear infinite;
padding: 2rem 1rem;
font-size: 2rem;
`;Component references
Styling based on other components remains straightforward. You only need to change the import.
import styled from "styled-components";
import { styled } from "@yak/react";
import { Button } from "./button";
const Container = styled.div`
${Button} {
color: red;
margin: 0.5em;
&:hover {
color: darkred;
}
}
`;Attrs
If you use .attrs in your styled components, you can just change the import and you're done.
import styled from "styled-components";
import { styled } from "@yak/react";
const Input = styled.input.attrs((props) => ({
type: "text",
size: props.size || "1em",
}))`
color: palevioletred;
font-size: 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;The same applies to the .attrs function for wrapped components.
import styled from "styled-components";
import { styled } from "@yak/react";
import { Button, type ButtonProps } from "./button";
const LocalButton = styled(Button).attrs<Partial<ButtonProps>>(() => ({
tabIndex: 0,
}))`
color: red;
`;Property 'as' does not exist
yak does not support the as prop. Instead, it provides a type-safe way to render dynamic HTML tags. By creating a reusable component with a defined set of allowed tags, you ensure valid HTML with stronger type safety:
type ElementType = "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
const HtmlTag = <T extends ElementType>({
tag: Tag,
...props
}: {
tag: T;
children?: React.ReactNode;
} & React.JSX.IntrinsicElements[T]) => <Tag {...(props as any)} />;You can then pass one of the predefined tags of ElementType as a prop and style it like any other component:
<Title tag="h1">Heading 1</Title>;
const Title = styled(HtmlTag)`
font-weight: bold;
`;This approach is preferred over the as prop because it lists the allowed HTML tags explicitly. That gives you stronger type safety and prevents invalid usage like <Image as="button">, which would be valid in styled-components.
Change some more things
The last part of the migration guide is about more complex cases where you need to change a bit more than just the import.
Dynamic component styles
This is the most complex part of the migration. Unlike styled-components, yak distinguishes between dynamic css properties and dynamic property values. If you use property values, you shouldn't need to change more than the import.
import styled from "styled-components";
import { styled } from "@yak/react";
const Button = styled.button<{ $primary: boolean }>`
background: #bf4f74;
color: ${(props) => (props.$primary ? "white" : "#BF4F74")};
`;This is transformed into a CSS variable, and its value is set at runtime.
// pseudo code
const Button = styled.button`
background: #bf4f74;
color: var(--yak-1);
`;If you use dynamic css properties, you need to use the css tag to define the css.
import styled, { css } from 'styled-components';
import { css, styled } from '@yak/react';
const Button = styled.button<{ $primary: boolean }>`
background: #BF4F74;
${props => props.$primary
? ` // [!code --]
? css`
color: white;
font-size: 1rem;
padding: 1em 2em;
: ` // [!code --]
: css`
color: #BF4F74;
font-size: 2rem;
padding: 2em 4em;
`
}
`;When yak finds a css tag, it transforms the css into a class name at build time.
// pseudo code
const Button = (props) => (
<button className={props.$primary ? "yak-1" : "yak-2"}>Click me</button>
);You can nest the css tags arbitrarily deep and yak will transform them into class names and css variables. You can't nest arbitrary styles inside property values, but you can nest property values inside css tags. To keep your styles fast, simplify them and avoid nesting where you can.
Dynamic mixins
If you use dynamic mixins in your styled components, you need to change them the same way as you would with dynamic component styles.
import styled, { css } from "styled-components";
import { css, styled } from "@yak/react";
const mixin = css<{ $primary: boolean }>`
color: green;
${(props) =>
props.$primary
? css`
background: white;
`
: css`
background: black;
`}
`;
const MyComp = styled.div<{ $primary: boolean }>`
background-color: yellow;
${mixin};
`;Dynamic mixins work differently in yak than in styled-components. With yak you're currently not able to export a dynamic mixin and use it in other components. As we transform the styles on a per-file basis, we can't share dynamic values between files. If you need to use a dynamic mixin in multiple components, you unfortunately need to duplicate the mixin in each file or move the components to the same file.
import styled from "styled-components";
import { css, styled } from "@yak/react";
import { mixin } from "./mixin";
const mixin = css<{ $primary: boolean }>`
color: green;
${(props) =>
props.$primary
? css`
background: white;
`
: css`
background: black;
`}
`;
const MyComp = styled.div<{ $primary: boolean }>`
background-color: yellow;
${mixin};
`;import { css } from "styled-components";
export const mixin = css<{ $primary: boolean }>`
color: green;
${(props) =>
props.$primary
? `background: white;`
: `background: black;`}
`;Global styles
styled-components createGlobalStyle returns a component that you render. In yak global styles are a statement and globalStyle applies as soon as the file with the global styles is imported.
To migrate your global styles, remove the <GlobalStyle /> JSX and import the file where you used to render it. For
app-wide resets that is your root layout, but if it only rendered on a certain page, import the file from that page instead:
import { createGlobalStyle } from "styled-components";
import { globalStyle } from "@yak/react";
const GlobalStyle = createGlobalStyle`
globalStyle`
body {
margin: 0;
}
`;import { GlobalStyle } from "./globals";
import "./globals";
export default function Layout({ children }) {
return (
<html>
<body>
<GlobalStyle />
{children}
</body>
</html>
);
}Theme props like ${(props) => props.theme.color} inside global styles become CSS custom properties
toggled via an attribute or class on the root element. A conditionally rendered <GlobalStyle />
becomes a CSS condition (@media, @supports, :root[data-theme]). See
Global styles for the patterns.
Reference external styles
If you reference external styles (styles that aren't component styles) in your styled components, write the selector as it is. yak writes plain CSS, so it does not rename .myGlobalClass:
import styled from "styled-components";
import { styled } from "@yak/react";
const Button = styled.button`
color: blue;
.myGlobalClass {
color: red;
}
`;Functions that generate styles
If you have utility functions that generate styles, you need to change them. Either turn them into static values that cover every variant, or create a dynamic mixin.
export const generateAllColors = (primaryColor: string) => {
return {
primary: primaryColor,
secondary: "#F7B801",
};
};
export const allColors = {
red: {
primary: "red",
secondary: "#F7B801",
},
blue: {
primary: "blue",
secondary: "#F7B801",
},
green: {
primary: "green",
secondary: "#F7B801",
},
};or
import styled from "styled-components";
import { styled } from "@yak/react";
import { generateAllColors } from "./util";
const Button = styled.button<{ $color: string }>`
color: ${generateAllColors};
color: ${(props) => props.$color};
`;CSS prop (optional)
If you use the css prop in your styled components, changing the import is almost all you need.
You also need to tell TypeScript that native JSX elements can have a css prop. To do that, add the following to your tsconfig.json.
{
"compilerOptions": {
"jsxImportSource": "@yak/react"
}
}To use the css prop in a single file only, add the following to the top of that file instead.
/** @jsxImportSource @yak/react */Then change the import:
import styled, { css } from "styled-components";
import { css, styled } from "@yak/react";
const MyComponent = () => {
return (
<div
css={css`
background: papayawhip;
color: red;
`}
/>
);
};If you need dynamic values, use the same interpolation as in your other styled components.
import { css } from "@yak/react";
const MyComponent = ({ color }: { color: string }) => {
return (
<div
css={css`
background: papayawhip;
color: ${() => color};
`}
/>
);
};Move some code to yak files
Migrating styled-components code with complex logic can take more work. Yak files are evaluated at build time and can hold static values that yak extracts during the build. You can create a .yak.ts or .yak.tsx file anywhere in your project and yak picks it up.
Styles that use calculated values
Move calculated values to .yak.ts files:
import styled from "styled-components";
import { styled } from "@yak/react";
const RADIUS = 5;
const CIRCUMFERENCE = 2 * Math.PI * RADIUS;
import { CIRCUMFERENCE } from "./constants.yak";
const Circle = styled.div`
width: ${CIRCUMFERENCE}px;
height: ${CIRCUMFERENCE}px;
border-radius: 50%;
border: 1px solid black;
`;const RADIUS = 5;
export const CIRCUMFERENCE = 2 * Math.PI * RADIUS;As a rule of thumb, yak doesn't run JavaScript to resolve interpolated values unless the code lives in a .yak.ts (or .yak.js) file or is imported from a yak file.
Values that don't need code to run before they have a value (so everything except calculations and string concatenations) work without yak files. Prefer writing your styles that way to keep your build times fast.
Troubleshooting
For build errors, missing styles and how to see what yak generates, see Troubleshooting.
Styles from styled-components win
If you wrap a styled-components component with a yak component while both libraries run side by side, add && before
the styles to raise the specificity. Otherwise styled-components always wins:
const Button = styled(StyledComponentsButton)`
&& {
color: red;
}
`;Once you've fully migrated to yak, you can remove the && again.