Skip to content

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() and serializeMeta(). See Server side rendering for details.

Plugin usage

First, you need to install the plugin in your project:

bash
npm install vite-plugin-effcss --save-dev

EffCSS is declared as a peer dependency — make sure it's installed in your project:

bash
npm install effcss

Then add it to the Vite config:

ts
// 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>):

html
<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():

ts
// src/effcss-config.ts
import { configure } from 'effcss';

configure({
  prefix: 'my',
  minify: true,
});

Options

The plugin supports the following options:

OptionTypeDefaultDescription
verbosebooleanfalseVerbose logging
entrystringExplicit entry point (e.g. /src/main.tsx). Auto-detected from <script type="module" src="..."> in index.html if omitted
placeholderstring'<!-- EFFCSS_INJECT -->'HTML placeholder to replace with <style>
injectTo'head' | 'head-prepend' | 'body''head'Fallback injection position if placeholder not found
injectMetabooleantrueInject 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:

  1. Imports EffCSS (to access serialize() / serializeMeta())
  2. Imports your entry point — to execute and register styles
  3. Calls serialize() — captures the final CSS + metadata as strings
  4. 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: EffCSS stays in the browser bundle

Released under the Apache-2.0 License.