Vite

Markdown

Set up yak in a Vite app with the viteYak plugin and get styled-components syntax with build-time CSS extraction in React Router, TanStack Start and more.

yak supports Vite through the viteYak plugin, and any Vite-based framework works out of the box.

This page shows how to:

  • Configure Vite to use yak
  • Define a theme context for YakThemeProvider
  • Migrate from styled-components
  • Start from scratch if you don't use any CSS-in-JS yet

Install dependencies

Install @yak/react alongside Vite and React (if you haven't already):

npm i @yak/react
# or
pnpm add @yak/react
# or
yarn add @yak/react
# or
bun add @yak/react

Configure Vite

Add the viteYak plugin to your vite.config.ts (or .js). It works together with @vitejs/plugin-react-swc or @vitejs/plugin-react.

vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
import { viteYak } from "@yak/react/vite";

export default defineConfig({
  //...
  plugins: [viteYak(), react()], // ideally this is before the react plugin
});

You can pass the same options you would use in withYak:

viteYak({
  // compact class names in production
  minify: process.env.NODE_ENV === "production",
  // optional prefix for generated identifiers
  prefix: "my-app",
  // optional custom path to your yak context file
  // (relative to the project root)
  contextPath: "yak.context",
  // base path for CSS virtual module resolution (default: Vite's root)
  // useful in monorepo setups where source files live outside the Vite root
  // relative paths are resolved from Vite's project root
  basePath: "../..",
  // enable logging of transformed TS/CSS
  debug: false,
});

Theme context (yak.context.ts)

If you want to use YakThemeProvider / useTheme, create a yak.context.ts file in your project root. The Vite plugin automatically aliases @yak/react/context/baseContext to this file.

yak.context.ts
export function getYakThemeContext() {
  if (typeof document !== "undefined") {
    const cookies = document.cookie.split(";");
    const highContrastCookie = cookies.find((cookie) => cookie.trim().startsWith("highContrast="));

    const highContrast = highContrastCookie ? highContrastCookie.split("=")[1] === "true" : false;

    return { highContrast };
  }

  // server / SSR fallback
  return { highContrast: false };
}

declare module "@yak/react" {
  export interface YakTheme extends ReturnType<typeof getYakThemeContext> {}
}

Then wrap your app in YakThemeProvider and read the theme from @yak/react/context/baseContext:

src/main.tsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App";
import { YakThemeProvider } from "@yak/react";
import { getYakThemeContext } from "@yak/react/context/baseContext";
import "./globals.css";

createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <YakThemeProvider theme={getYakThemeContext()}>
      <App />
    </YakThemeProvider>
  </StrictMode>,
);

JSX runtime / css prop

If you use the css prop, set the JSX import source to @yak/react to get its types:

Per-file (quick start):

App.tsx
/** @jsxImportSource @yak/react */
import { styled, css } from "@yak/react";

const Title = styled.h1`
  ${({ $primary }: { $primary?: boolean }) =>
    $primary &&
    css`
      color: teal;
    `}
`;

Globally via tsconfig / jsconfig:

tsconfig.json
{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "@yak/react"
  }
}

Migrating from styled-components

If you already use styled-components in a Vite + React app, migration is mostly about imports (the styled API stays familiar). After adding the viteYak plugin:

src/Button.tsx
import styled, { css, keyframes } from "styled-components";
import { styled, css, keyframes } from "@yak/react";

const Button = styled.button<{ $primary?: boolean }>`
  background: #bf4f74;
  color: ${({ $primary }) => ($primary ? "white" : "#bf4f74")};
`;

Most common patterns (static styles, dynamic props, css blocks, keyframes, attrs, component references) work the same. For details and edge cases, see the Migration from styled-components guide.

Starting without any CSS-in-JS

If you don't use any CSS-in-JS today, you can:

  1. Install @yak/react and configure viteYak as shown above.
  2. Start using styled components alongside your existing CSS files.
src/Button.tsx
import { styled } from "@yak/react";

const Button = styled.button`
  padding: 0.5rem 1rem;
  border-radius: 9999px;
  border: 1px solid currentColor;
  background: white;

  &:hover {
    background: #f3f4f6;
  }
`;

export default Button;

You can mix yak with regular CSS / CSS Modules. yak styles are extracted at build time and end up in regular CSS files handled by Vite's CSS pipeline.

Yak constant files in Vite

yak supports build-time constants and mixins in special .yak.ts / .yak.tsx files. They run in an isolated context at build time.

Simple string constants don't need a .yak file. They can live in normal .ts modules and be imported as usual. .yak files are useful when you want to run a bit of TypeScript/JavaScript at build time (e.g. generate a design token scale, expand objects, or normalize data) and then inject the result into your styles.

Example: build-time design tokens

src/theme/tokens.yak.ts
// Base scale for spacing (in px)
const baseSpacing = [0, 4, 8, 12, 16, 24, 32, 40, 48];

export const spacing = Object.fromEntries(
  baseSpacing.map((value, index) => [`s${index}`, `${value}px`]),
);

// Generate a simple color ramp
const baseHue = 220;

export const colors = Array.from({ length: 9 }, (_, i) => {
  const lightness = 30 + i * 5;
  return `hsl(${baseHue}, 80%, ${lightness}%)`;
});

Usage in your components:

src/App.tsx
/** @jsxImportSource @yak/react */
import { styled } from "@yak/react";
import { spacing, colors } from "./theme/tokens.yak";

const Card = styled.div`
  padding: ${spacing.s3};
  background-color: ${colors[4]};
  border-radius: ${spacing.s2};
`;

Here, the loops and calculations in tokens.yak.ts run at build time, and only the final string values are used inside your CSS. This keeps your runtime lean while still letting you express token generation in regular TypeScript.

Example project

You can find a full Vite example in the repository:

Next steps