Solid

Markdown

Use yak with Solid 2 and Vite through @yak/solid. Static styles compile to classes, and dynamic styles become CSS variables driven by signals.

@yak/solid brings the yak API to Solid. The SWC plugin extracts your CSS at build time, as it does for React. The thin runtime that remains follows Solid's model, where components run once and dynamic styles follow signals instead of re-renders.

Install dependencies

Install @yak/solid together with the Solid 2 line of the Vite plugin (npm tag next):

npm i @yak/solid
npm i -D @solidjs/vite-plugin@next

@yak/solid needs solid-js and @solidjs/web 2.0.0-rc.15 or later, Vite 7 or later and Node.js 22.18 or later.

Configure Vite

Add the yak plugin before solid(), so that it sees your original source:

vite.config.ts
import { defineConfig } from "vite";
import solid from "@solidjs/vite-plugin";
import { yak } from "@yak/solid/vite";

export default defineConfig({
  plugins: [yak(), solid()],
});

yak() takes the same options as the React Vite plugin, such as minify, prefix, contextPath, basePath, foldStatic and debug. The API reference lists them all.

Write styled components

The API is the one you know from @yak/react. Interpolations that read props are tracked like any other reactive read in Solid:

App.tsx
import { styled, css } from "@yak/solid";
import { createSignal } from "solid-js";

const Button = styled.button<{ $primary?: boolean }>`
  color: #009688;
  ${(props) =>
    props.$primary &&
    css`
      border-width: 2px;
    `}
`;

const Progress = styled.div<{ $percent: number }>`
  height: 8px;
  background: #e5e5ef;

  &::before {
    content: "";
    display: block;
    height: 100%;
    width: ${(props) => props.$percent}%;
    background: #009688;
  }
`;

export function App() {
  const [percent, setPercent] = createSignal(30);
  return (
    <>
      <Progress $percent={percent()} />
      <Button $primary onClick={() => setPercent((p) => Math.min(p + 10, 100))}>
        More
      </Button>
    </>
  );
}

A styled component without interpolations compiles to a plain CSS class. Dynamic interpolations become CSS variables on the element's inline style, all computed in one memo per element, so a prop change updates the class and style bindings without re-creating the element.

The css prop works on every intrinsic element and maps to Solid's class attribute. Its types become available as soon as you import @yak/solid:

<p
  css={css`
    color: green;
  `}
/>

Theming

Create a yak.context.ts next to your vite.config.ts. The plugin picks it up automatically, and the module declaration gives the theme its type:

yak.context.ts
export function getYakThemeContext() {
  return { highContrast: false };
}

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

Wrap your app in YakThemeProvider. Its theme prop is reactive, so signal reads inside it stay live and a theme change only updates the themed styles:

main.tsx
import { render } from "@solidjs/web";
import { YakThemeProvider } from "@yak/solid";
import { createSignal } from "solid-js";
import { App } from "./App";

const [highContrast, setHighContrast] = createSignal(false);

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

useTheme() returns an accessor, like a signal getter, and so does props.theme inside an interpolation:

import { styled, useTheme } from "@yak/solid";

const Label = styled.span`
  color: ${(props) => (props.theme().highContrast ? "#000" : "#555")};
`;

function Toolbar() {
  const theme = useTheme();
  return <Label>{theme().highContrast ? "High contrast" : "Default"}</Label>;
}

SolidStart

The Solid 2 Vite plugin has SolidStart built in. Turn it on with the start option, and yak works with it like with any other Solid app:

vite.config.ts
import { defineConfig } from "vite";
import solid from "@solidjs/vite-plugin";
import { yak } from "@yak/solid/vite";

export default defineConfig({
  plugins: [yak(), solid({ start: true })],
});

Differences to @yak/react

If you come from @yak/react, the API stays the same apart from a few Solid-shaped details:

  • useTheme() and props.theme return accessors, so you read theme().highContrast instead of theme.highContrast.
  • Use class instead of className, also inside .attrs({ ... }).
  • Components run once, and Solid's reactivity updates the styles instead of a re-render.

Example project

examples/vite-solid shows a full setup with theming, keyframes, .attrs() and the css prop.

Next steps