Skip to content

rawstylecss/rawstyle

Repository files navigation

logo
A lightweight compile-time CSS-in-JS library for React

npm version  bundle size  bugs

Features  •  Quick Start  •  Usage  •  Ecosystem

demo

🔥 Features

  • ⚡ 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 css in every file
  • 🧩 VS Code Extension: syntax highlighting, autocomplete, validation, and more
  • 🧹 ESLint Plugin: auto-fixable formatting for CSS in template literals

🏁 Quick Start

Scaffold a demo project for your platform (Next.js/Vite/tsdown):

bun create rawstyle

🕹️ Usage

1. Install rawstyle and the bundler plugin:

bun add -D rawstyle @rawstyle/next  # for Next.js
bun add -D rawstyle @rawstyle/vite  # for Vite

2. Configure the bundler to use the plugin:

Next.js

// next.config.ts
import { rawstyleTurboRule } from '@rawstyle/next'
import type { NextConfig } from 'next'

export default {
	turbopack: { rules: { ...rawstyleTurboRule } },
} satisfies NextConfig

The loader extracts CSS and injects it as a base64 CSS import.

Vite

// 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 UserConfig

The plugin emits a virtual .css module and imports it as a side effect.

3. Configure global types:

// tsconfig.json
"compilerOptions": {
	"types": ["rawstyle"]
}

4. Start styling:

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; }
}

🧩 Ecosystem

Rawstyle offers a suite of tools to enhance your workflow: