- ⚡ True Zero Runtime: styles are extracted at compile-time, no JS in production
- 💎 Native CSS: write regular CSS with all modern features
- 📦 Modern Bundlers: first-class support for Next.js and Vite
- 🔥 Hot Reload: instant style updates during development
- 🌐 Global Types: no need to import
cssin every file - 🧩 VS Code Extension: syntax highlighting, autocomplete, validation, and more
- 🧹 ESLint Plugin: auto-fixable formatting for CSS in template literals
Scaffold a demo project for your platform (Next.js/Vite/tsdown):
bun create rawstylebun add -D rawstyle @rawstyle/next # for Next.js
bun add -D rawstyle @rawstyle/vite # for Vite// next.config.ts
import { rawstyleTurboRule } from '@rawstyle/next'
import type { NextConfig } from 'next'
export default {
turbopack: { rules: { ...rawstyleTurboRule } },
} satisfies NextConfigThe loader extracts CSS and injects it as a base64 CSS import.
// vite.config.ts
import react from '@vitejs/plugin-react'
import rawstyle from '@rawstyle/vite'
import type { UserConfig } from 'vite'
export default {
plugins: [react(), rawstyle()],
} satisfies UserConfigThe plugin emits a virtual
.cssmodule and imports it as a side effect.
Rawstyle exposes two core primitives: css and cn:
// src/module.tsx
export const Component = ({ theme }: { theme: string }) => (
// `cn` - class names merging utility
<div className={cn('class', theme === 'dark' && card)}>
Hello, World!
</div>
)
// `css` assigned to a variable - generates scoped CSS
const card = css`
padding: 1rem;
color: var(--primary);
&:hover { box-shadow: 0 4px 12px black; }
`
// `css` as a standalone expression - generates global CSS
void css`
:root { --primary: #303030; }
body { margin: 0; background: #ebebeb; }
`This compiles to:
import '\0virtual.css'
export const Component = ({ theme }: { theme: string }) => (
<div className={['class', theme === 'dark' && card].filter(Boolean).join(' ')}>
Hello, World!
</div>
)
const card = 'card_hash5'The css template literal is replaced with a hashed class name, cn is transformed into a conditional string joiner, and the CSS is extracted into a separate virtual .css file:
/* virtual.css */
:root { --primary: #303030; }
body { margin: 0; background: #ebebeb; }
.card_hash5 {
padding: 1rem;
color: var(--primary);
&:hover { box-shadow: 0 4px 12px black; }
}Rawstyle offers a suite of tools to enhance your workflow:
