# Migration from styled-components (http://yak.js.org/docs/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 [#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 [#eslint-plugin]

To make the migration easier, our ESLint plugin flags potential issues during the migration and afterwards.

See the [ESLint plugin documentation](/docs/eslint-plugin) to install it in your project.

## Feature comparison [#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 (`createGlobalStyle` becomes [`globalStyle`](/docs/features#globalstyle))
- If some features are missing, please let us know and open a feature request on [GitHub](https://github.com/galaxuslabs/yak/issues/new/choose)

Missing

- Object syntax
- `css` prop values that are not written in place, like variables, arrays or objects
- `as` prop: you can replace it with a [type-safe alternative](#property-as-does-not-exist)
- `.withConfig`

## Just change the import [#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.

```tsx title="component.tsx"
import styled, { css, keyframes } from "styled-components"; // [!code --]
import { styled, css, keyframes } from "@yak/react"; // [!code ++]
```

### Static component styles [#static-component-styles]

For components with static styles, updating the import is enough:

```tsx title="component.tsx"
import styled from "styled-components"; // [!code --]
import { styled } from "@yak/react"; // [!code ++]

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 [#static-mixins]

For static mixins, you can just change the import and you're done.

```tsx title="component.tsx"
import styled, { css } from "styled-components"; // [!code --]
import { css, styled } from "@yak/react"; // [!code ++]

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.

> [!NOTE]
> **Important note on semicolons**: You need to add a semicolon after the mixin to separate it from
> the rest of the styles. Otherwise the parser can't tell apart between a selector, a constant
> or a mixin.

### Keyframes [#keyframes]

For keyframes you can just change the import and you're done.

```tsx title="component.tsx"
import styled, { keyframes } from "styled-components"; // [!code --]
import { styled, keyframes } from "@yak/react"; // [!code ++]

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 [#component-references]

Styling based on other components remains straightforward. You only need to change the import.

```tsx title="component.tsx"
import styled from "styled-components"; // [!code --]
import { styled } from "@yak/react"; // [!code ++]
import { Button } from "./button";

const Container = styled.div`
  ${Button} {
    color: red;
    margin: 0.5em;

    &:hover {
      color: darkred;
    }
  }
`;
```

### Attrs [#attrs]

If you use `.attrs` in your styled components, you can just change the import and you're done.

```tsx title="component.tsx"
import styled from "styled-components"; // [!code --]
import { styled } from "@yak/react"; // [!code ++]

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.

```tsx title="component.tsx"
import styled from "styled-components"; // [!code --]
import { styled } from "@yak/react"; // [!code ++]
import { Button, type ButtonProps } from "./button";

const LocalButton = styled(Button).attrs<Partial<ButtonProps>>(() => ({
  tabIndex: 0,
}))`
  color: red;
`;
```

## Property 'as' does not exist [#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:

```tsx
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:

```tsx
<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 [#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 [#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.

```tsx title="component.tsx"
import styled from "styled-components"; // [!code --]
import { styled } from "@yak/react"; // [!code ++]

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.

```tsx title="transformed.tsx"
// 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.

```tsx title="component.tsx"
import styled, { css } from 'styled-components'; // [!code --]
import { css, styled } from '@yak/react'; // [!code ++]

const Button = styled.button<{ $primary: boolean }>`
  background: #BF4F74;
  ${props => props.$primary
  ? ` // [!code --]
  ? css` // [!code ++]
    color: white;
    font-size: 1rem;
    padding: 1em 2em;
  : ` // [!code --]
  : css` // [!code ++]
    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.

```tsx title="transformed.tsx"
// 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 [#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.

```tsx title="component.tsx"
import styled, { css } from "styled-components"; // [!code --]
import { css, styled } from "@yak/react"; // [!code ++]

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.

```tsx title="component.tsx"
import styled from "styled-components"; // [!code --]
import { css, styled } from "@yak/react"; // [!code ++]

import { mixin } from "./mixin"; // [!code --]
const mixin = css<{ $primary: boolean }>` // [!code ++]
  color: green; // [!code ++]
  ${(props) => // [!code ++]
    props.$primary // [!code ++]
      ? css` // [!code ++]
          background: white; // [!code ++]
        ` // [!code ++]
      : css` // [!code ++]
          background: black; // [!code ++]
        `} // [!code ++]
`; // [!code ++]

const MyComp = styled.div<{ $primary: boolean }>`
  background-color: yellow;
  ${mixin};
`;
```

```tsx title="mixin.ts"
import { css } from "styled-components"; // [!code --]

export const mixin = css<{ $primary: boolean }>` // [!code --]
  color: green; // [!code --]
  ${(props) => // [!code --]
    props.$primary // [!code --]
      ? `background: white;` // [!code --]
      : `background: black;`} // [!code --]
`; // [!code --]
```

### Global styles [#global-styles]

styled-components `createGlobalStyle` returns a component that you render. In yak global styles are a statement and [`globalStyle`](/docs/features#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:

```tsx title="globals.tsx"
import { createGlobalStyle } from "styled-components"; // [!code --]
import { globalStyle } from "@yak/react"; // [!code ++]

const GlobalStyle = createGlobalStyle` // [!code --]
globalStyle` // [!code ++]
  body {
    margin: 0;
  }
`;
```

```tsx title="layout.tsx"
import { GlobalStyle } from "./globals"; // [!code --]
import "./globals"; // [!code ++]

export default function Layout({ children }) {
  return (
    <html>
      <body>
        <GlobalStyle /> {/* [!code --] */}
        {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](/docs/features#globalstyle) for the patterns.

### Reference external styles [#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`:

```tsx title="component.tsx"
import styled from "styled-components"; // [!code --]
import { styled } from "@yak/react"; // [!code ++]

const Button = styled.button`
  color: blue;

  .myGlobalClass {
    color: red;
  }
`;
```


### Functions that generate styles [#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.

```tsx title="util.ts"
export const generateAllColors = (primaryColor: string) => { // [!code --]
  return { // [!code --]
    primary: primaryColor, // [!code --]
    secondary: "#F7B801", // [!code --]
  }; // [!code --]
}; // [!code --]
export const allColors = { // [!code ++]
  red: { // [!code ++]
    primary: "red", // [!code ++]
    secondary: "#F7B801", // [!code ++]
  }, // [!code ++]
  blue: { // [!code ++]
    primary: "blue", // [!code ++]
    secondary: "#F7B801", // [!code ++]
  }, // [!code ++]
  green: { // [!code ++]
    primary: "green", // [!code ++]
    secondary: "#F7B801", // [!code ++]
  }, // [!code ++]
}; // [!code ++]
```

or

```tsx title="component.tsx"
import styled from "styled-components"; // [!code --]
import { styled } from "@yak/react"; // [!code ++]
import { generateAllColors } from "./util"; // [!code --]

const Button = styled.button<{ $color: string }>`
  color: ${generateAllColors}; // [!code --]
  color: ${(props) => props.$color}; // [!code ++]
`;
```

### CSS prop (optional) [#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`.

```json title="tsconfig.json"
{
  "compilerOptions": {
    "jsxImportSource": "@yak/react" // [!code ++]
  }
}
```

To use the css prop in a single file only, add the following to the top of that file instead.

```tsx title="component.tsx"
/** @jsxImportSource @yak/react */ // [!code ++]
```

Then change the import:

```tsx title="component.tsx"
import styled, { css } from "styled-components"; // [!code --]
import { css, styled } from "@yak/react"; // [!code ++]

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.

```tsx title="component.tsx"
import { css } from "@yak/react";

const MyComponent = ({ color }: { color: string }) => {
  return (
    <div
      css={css`
        background: papayawhip;
        color: ${() => color};
      `}
    />
  );
};
```

## Move some code to yak files [#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.

> [!NOTE]
> **Important note on `.yak.tsx` files**: As yak files are evaluated on every code change, they will
> slow down your dev experience and your production build times. They should only be used as a last
> resort.

### Styles that use calculated values [#styles-that-use-calculated-values]

Move calculated values to `.yak.ts` files:

```tsx title="component.tsx"
import styled from "styled-components"; // [!code --]
import { styled } from "@yak/react"; // [!code ++]

const RADIUS = 5; // [!code --]
const CIRCUMFERENCE = 2 * Math.PI * RADIUS; // [!code --]
import { CIRCUMFERENCE } from "./constants.yak"; // [!code ++]

const Circle = styled.div`
  width: ${CIRCUMFERENCE}px;
  height: ${CIRCUMFERENCE}px;
  border-radius: 50%;
  border: 1px solid black;
`;
```

```ts title="constants.yak.ts"
const RADIUS = 5; // [!code ++]
export const CIRCUMFERENCE = 2 * Math.PI * RADIUS; // [!code ++]
```

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 [#troubleshooting]

For build errors, missing styles and how to see what yak generates, see [Troubleshooting](/docs/troubleshooting).

### Styles from styled-components win [#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:

```tsx title="component.tsx"
const Button = styled(StyledComponentsButton)`
  && { // [!code ++]
    color: red;
  } // [!code ++]
`;
```

Once you've fully migrated to yak, you can remove the `&&` again.
