@yak/react: styled-components syntax with the CSS extracted at build time
🦀 Zero-runtime CSS-in-JS powered by Rust. Write styled-components syntax, get build-time CSS extraction and full RSC compatibility
At a glance
- What is it?
- @yak/react is a CSS-in-JS library for React that moves style extraction into a Rust SWC plugin, so styled-components syntax ends up as plain CSS with a tiny runtime that only swaps class names. It suits teams that want component-scoped styles without shipping a runtime style engine, and it is picky about bundler and React versions.
- Who is it for?
- @yak/react fits a team already writing styled-components style components that wants the output to be ordinary extracted CSS rather than a runtime style engine, particularly on Next.js with the App Router where Server Components and the App Router conventions push against most CSS-in-JS libraries.
- 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 received new commits within the last day.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The runtime is only a class name swap, and that is the whole design
@yak/react is described as a build-time CSS-in-JS library powered by a Rust SWC plugin: you write styled-components syntax and get zero-runtime CSS extraction plus full React Server Components compatibility. The runtime footprint is the part that defines the approach. The feature list describes it as lightweight, minimal, a set of code that just swaps CSS class names based on props. There is no style sheet being built in the browser, no hashing of a rules object at runtime, no re-serialisation when a prop changes. Instead the compiler walks your components, resolves the static parts of every template literal, and writes the resulting rules into a real CSS file that the browser downloads like any other stylesheet. The dependency count is quoted as only 8 dependencies in total, including transitive ones, and that number is the practical argument for the library, because CSS-in-JS runtimes tend to arrive with a dependency tree of their own.
A SWC plugin means the supported toolchain is narrower than it looks
Because the extraction happens inside a Rust SWC plugin, the versions of things you can use are constrained, and the project publishes a matrix instead of leaving you to discover the boundaries. The table maps each major line to a Next.js minimum, a Vite minimum, a React range and a swc_core version. The 10.x line asks for Next.js 16.4.0 or newer, Vite 7.0.0 or newer, React 19.x and swc_core 79.0.0. The 9.x line asks for Next.js 16.1.0 or newer, Vite 7.0.0 from 9.1 onwards, React 19.x and swc_core 56.0.0. Earlier lines are Next.js only, with the 3.x line the first to allow React 18.x, and the 2.x line down to Next.js 14.x and swc_core 0.279.0. That table is the real adoption decision. A plugin built against one swc_core version will not load in another, so the column is not advisory.
Next.js is the easiest path because withYak wraps the config
The Next.js setup is a wrapper function around your existing config, and the project says it works out of the box with both Webpack and Turbopack with no configuration changes needed. The import comes from a subpath of the package, and the config is exported through the wrapper.
import { withYak } from "@yak/react/withYak";
const nextConfig = {
// your next.js config
};
export default withYak(nextConfig);Styling then follows the styled-components pattern, with the template literal carrying ordinary CSS.
import { styled } from "@yak/react";
const StyledDiv = styled.div`
color: #333;
padding: 16px;
background-color: #f0f0f0;
`;
function HomePage() {
return <StyledDiv>Hello, @yak/react!</StyledDiv>;
}
export default HomePage;A stackblitz demo and a starter kit are linked, and the playground plus the documentation live on yak.js.org. The React Server Components claim is the headline for the App Router, where a component that is a Server Component cannot carry a runtime style context, so any library that needs one in the browser is a problem. Here there is no such context to carry.
Vite, Rsbuild and Storybook each get a plugin entry point
The other bundlers are supported through their own plugin exports, and the versions they require are stated in the same place the plugins are introduced. Vite support arrived with next-yak 9.1.0 and covers Vite 7 and later, including Vite 8 with OXC and Rolldown, and the project says any Vite-based framework works, naming vanilla Vite, react-router and TanStack Start.
// 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()],
});Rsbuild support came with next-yak 9.5.0 and covers Rsbuild 2 and later through a pluginYak plugin that composes with pluginReact and the built-in SWC.
// rsbuild.config.ts
import { defineConfig } from "@rsbuild/core";
import { pluginReact } from "@rsbuild/plugin-react";
import { pluginYak } from "@yak/react/rsbuild";
export default defineConfig({
plugins: [pluginReact(), pluginYak()],
});For Storybook, the @yak/storybook addon enables the library in Storybook 10 and later with both the Vite and Webpack builders, installed as a dev dependency. The repository carries a matching set of examples, with directories for next-js, rsbuild, storybook, tanstack-start, vite and vite-solid.
Dynamic styles split into class toggles and CSS custom properties
The interesting design decision in the library is how it handles a template literal that depends on props, because the compiler cannot statically extract a value it does not know. There are two mechanisms and they behave differently. Dynamic Styles only toggle the CSS class during runtime, so the extracted CSS contains both rules and the runtime swaps which class is applied.
import { styled, css } from "@yak/react";
const ToggleButton = styled.button`
${(props) =>
props.$active
? css`
background-color: green;
`
: css`
background-color: lime;
`};
color: white;
padding: 10px 20px;
`;Dynamic Properties take the other route and use custom properties, meaning CSS variables, under the hood, so the CSS is extracted at build time while the property value is modified at runtime.
import { styled } from "@yak/react";
const ProgressBar = styled.div`
width: ${(props) => `${props.$percent}%`};
height: 20px;
background-color: #3498db;
transition: width 0.3s ease-in-out;
`;The distinction is the one to internalise. A class toggle means the number of possible rules is known at build time and grows with the number of branches, while a custom property means one rule with a value that changes, which scales to continuous inputs such as a progress percentage.
Cross-file imports, component targeting and the limits of targeting
Several features are about how much you can share and reach across files. The library supports cross-file imports, meaning constants, mixins and selectors can come from .yak files and other modules and work across all bundlers. It also offers a theme context, all of it typed, since the project describes itself as TypeScript first with fully typed props, theme context and cross-file selectors. One limitation is explicit. You can target other components directly using CSS selectors as long as they are in the same file. The example builds an animation with keyframes and then applies it to a sibling component through a nested selector.
import { styled, keyframes } from "@yak/react";
const flip = keyframes`
from { transform: rotateY(0deg); }
to { transform: rotateY(360deg); }
`;
const Glow = styled.div`
/* Add your Glow component styles here */
`;
const Text = styled.span`
display: inline-block;
${Glow}:hover & {
animation: 1.5s ${flip} ease-out;
}
`;The same-file restriction is what makes build-time extraction tractable. A selector that points at a component in another file cannot be resolved without following the whole dependency graph of the style graph, so the library keeps the scope narrow instead. Full CSS syntax is otherwise supported, with nesting, keyframes and media queries, and it combines with utility-first frameworks such as Tailwind CSS.
A pnpm monorepo with a Rust package and a changeset release flow
The repository is a monorepo that shows how the Rust and TypeScript halves fit together. The root package.json declares pnpm workspaces over packages/*, uses pnpm 12.14.0 as the package manager, and requires Node 22.18.0 or newer. The build scripts are filtered per package, with the React and Solid builds in one command, the SWC package built separately, and the Rust side having its own format script. The linting stack is unusual and modern: oxfmt for formatting, oxlint for linting, knip for unused dependency detection, and a workspace eslint plugin that is built before the lint run, with an eslint-plugin package in the tree. Releases go through changesets, and the publish script builds every package before running changeset publish. The tree also holds a benchmarks directory, an e2e directory, scripts, a knip configuration and a compile flow image and a CSS extraction animation, both of which suggest the author documents the build behaviour visually. The published packages include next-yak, yak-swc and storybook-addon-yak, all at 9.10.3, released together two days before the last push, and the project notes that versions before 10.x are on npm under the next-yak name.
Editorial conclusion
@yak/react fits a team already writing styled-components style components that wants the output to be ordinary extracted CSS rather than a runtime style engine, particularly on Next.js with the App Router where Server Components and the App Router conventions push against most CSS-in-JS libraries. It does not fit a project pinned to an older bundler or React version, since the support matrix moves with every major release and the plugin ships a specific swc_core requirement per line. Before you install it, check your Next.js, Vite, React and swc_core versions against the compatibility table, confirm your bundler is on the supported list, and decide whether your dynamic styles will be handled as class toggles or as CSS custom properties, because that choice changes what the build can extract.
Frequently asked questions
What is @yak/react and how is it different from styled-components?
It is a CSS-in-JS library for React that extracts CSS at build time through a Rust SWC plugin rather than at runtime. You write styled-components syntax, and the runtime left in the bundle only swaps CSS class names based on props.
Which frameworks and bundlers does @yak/react support?
Next.js with both Webpack and Turbopack, Vite 7 and later including Vite 8 with OXC, Rsbuild 2 and later, and Storybook 10 and later through the @yak/storybook addon. Any Vite-based framework works, including react-router and TanStack Start.
How do I install @yak/react?
The package is installed with npm install @yak/react, and for Storybook you add the dev dependency with npm i -D @yak/storybook. Each bundler then needs its plugin wired into the config file, for example withYak for Next.js or viteYak for Vite.
How does @yak/react handle styles that depend on component props?
Two ways. Dynamic Styles toggle the CSS class at runtime, with all possible rules extracted at build time, while Dynamic Properties use CSS custom properties so one extracted rule carries a value that changes at runtime, which suits continuous values like a progress percentage.
What are the version requirements for @yak/react?
The compatibility table maps each major line to a Next.js minimum, a Vite minimum, a React range and a swc_core version, with 10.x requiring Next.js 16.4.0 or newer, Vite 7.0.0 or newer, React 19.x and swc_core 79.0.0. The swc_core column matters because the plugin is built against one version of that library.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/digitecgalaxus-next-yak)Was this useful?