A Vite plugin for static site generation with React and island architecture (partial hydration).
- Static Site Generation - Pre-render React pages to static HTML at build time
- Island Architecture - Only hydrate interactive components, reducing JavaScript payload
- Image Optimization - Automatic image compression and modern format conversion (WebP/AVIF)
- CSS Extraction - Per-page CSS bundles with Tailwind CSS support
- Firebase Hosting - Automatic rewrite configuration for Firebase
- Dev Middleware - Simulates hosting rewrites during development
npm install vite-plugin-ssg// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { ssgPlugin } from 'vite-plugin-ssg';
export default defineConfig({
plugins: [
react(),
ssgPlugin({
pages: ['src/pages/HomePage.tsx', 'src/pages/blog'],
config: {
outDir: 'dist/static',
baseUrl: '/static',
},
hosting: {
firebaseJson: './firebase.json',
},
}),
],
});// src/pages/HomePage.tsx
import type { SsgOptions } from 'vite-plugin-ssg';
import { Island } from 'vite-plugin-ssg';
export const ssgOptions: SsgOptions = {
slug: 'home',
routeUrl: '/',
Head: () => (
<>
<title>My Home Page</title>
<meta name="description" content="Welcome to my site" />
</>
),
context: async (children) => {
const { StaticRouter } = await import('react-router-dom/server');
return <StaticRouter location="/">{children}</StaticRouter>;
},
};
export default function HomePage() {
return (
<div>
<h1>Welcome</h1>
<Island component="components/InteractiveWidget" props={{ count: 0 }} />
</div>
);
}// src/components/InteractiveWidget.tsx
'use island';
import { useState } from 'react';
export default function InteractiveWidget({ count }: { count: number }) {
const [value, setValue] = useState(count);
return <button onClick={() => setValue(v => v + 1)}>{value}</button>;
}| Option | Type | Default | Description |
|---|---|---|---|
pages |
string | string[] |
Required | Page files or folders to generate |
config.outDir |
string |
'dist/static' |
Output directory |
config.baseUrl |
string |
'/static' |
Base URL for assets |
config.srcDir |
string |
'src' |
Source directory |
config.images.enabled |
boolean |
true |
Enable image optimization |
config.images.formats |
string[] |
['webp'] |
Output formats |
config.images.quality |
number |
80 |
Image quality (1-100) |
config.css.minify |
string |
'lightningcss' |
CSS minifier |
config.js.minify |
string |
'terser' |
JS minifier |
hosting.firebaseJson |
string |
- | Path to firebase.json |
verbose |
boolean |
false |
Enable verbose logging |
Export this from page components to enable static generation:
| Property | Type | Required | Description |
|---|---|---|---|
slug |
string |
Yes | Output filename (e.g., 'home' → home.html) |
routeUrl |
string |
No | Route URL for react-router |
Head |
ComponentType |
No | Component for <head> content |
context |
ContextWrapper |
No | Wrap page with providers |
Component for marking interactive islands:
<Island
component="path/to/Component" // Relative to src/
props={{ key: 'value' }} // Must be JSON-serializable
/>MIT