Client side rendering
EffCSS is already running on the client side without any effort on your part. However, the style hydration mechanism, which is commonly used in server rendering, can also be used for client side rendering (CSR) using the vite-plugin-effcss.
The plugin captures EffCSS-generated CSS via Vite SSR pipeline and inlines it into HTML. It allows to eliminate FOUC and speed up the first rendering due to style hydration. Works in both dev and build modes with zero additional dependencies (no esbuild, no tsx, no extra loaders).
Zero-config — works with no options. Just add it to your plugins array.
CSR-focused — for SSR/SSG, use manual
serialize()andserializeMeta(). See Server side rendering for details.
Plugin usage
First, you need to install the plugin in your project:
npm install vite-plugin-effcss --save-dev
EffCSSis declared as a peer dependency — make sure it's installed in your project:bashnpm install effcss
Then add it to the Vite config:
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import effcss from 'vite-plugin-effcss';
export default defineConfig({
plugins: [react(), effcss()],
});Add a placeholder in your HTML (optional — falls back to </head>):
<head>
<!-- EFFCSS_INJECT -->
</head>That's it. The plugin captures all EffCSS-generated CSS and inlines it into your HTML.
Note, that the plugin does not manage prefix, minify, and other EffCSS settings - they should be configured in your own source code via configure():
// src/effcss-config.ts
import { configure } from 'effcss';
configure({
prefix: 'my',
minify: true,
});Options
The plugin supports the following options:
| Option | Type | Default | Description |
|---|---|---|---|
verbose | boolean | false | Verbose logging |
entry | string | — | Explicit entry point (e.g. /src/main.tsx). Auto-detected from <script type="module" src="..."> in index.html if omitted |
placeholder | string | '<!-- EFFCSS_INJECT -->' | HTML placeholder to replace with <style> |
injectTo | 'head' | 'head-prepend' | 'body' | 'head' | Fallback injection position if placeholder not found |
injectMeta | boolean | true | Inject serializeMeta() as <script> with JSON metadata |
How it works
buildStart()
│
┌─────────┴─────────┐
│ │
▼ ▼
Server exists? Server null?
(dev mode) (build mode)
│ │
▼ ▼
write tmp .ts file createServer() + virtual module
→ ssrLoadModule → ssrLoadModule('effcss:capture')
│ │
└───────┬───────────┘
▼
mod.css + mod.meta
│
▼
transformIndexHtml
→ inject <style> + <script>The plugin creates a capture module that:
- Imports EffCSS (to access
serialize()/serializeMeta()) - Imports your entry point — to execute and register styles
- Calls
serialize()— captures the final CSS + metadata as strings - Inlines results into your HTML via
transformIndexHtml
Dev mode — uses the existing ViteDevServer with a temporary .ts file, then cleans it up.
Build mode — creates a temporary Vite dev server via createServer(), registers a virtual 'effcss:capture' module with resolveId/load hooks, loads it via ssrLoadModule(), and closes the server. User plugins (React, Vue, etc.) are included so JSX, SVG, CSS imports are properly transformed. The plugin excludes itself from the nested server to prevent infinite recursion.
Technical notes
- Vite: ^5.0.0 || ^6.0.0
- EffCSS: ^5.4.0
- Runtime:
EffCSSstays in the browser bundle