vite-plugin-ssg-spa
v1.4.9
Published
A Vite plugin for automatic Static Site Generation (SSG) with SPA fallback support for React applications
Downloads
822
Maintainers
Readme
vite-plugin-ssg-spa
A powerful Vite plugin for automatic Static Site Generation (SSG) with built-in SPA fallback support for React applications. Pre-render your static routes for optimal SEO and performance, while maintaining full client-side routing capabilities.
Features
- ✨ Zero Configuration: Automatically discovers and pre-renders all routes - no manual setup required
- 🕷️ Intelligent Auto-Crawling: Follows links in your app to discover dynamic routes (blog posts, products, etc.)
- 🎯 Static Route Detection: Automatically extracts routes from your React Router configuration
- 🚀 SPA Fallback: Built-in SPA fallback for development and production servers
- 🔄 SSR Server: Generates a production-ready Node.js server with SPA fallback
- 🎨 Puppeteer/Playwright: Uses headless browsers for accurate pre-rendering
- ⚡ SEO Optimized: Pre-rendered HTML for better search engine indexing
- � Platform Config Generation: Automatically creates
vercel.json,netlify.toml,.htaccessfor deployment - 🛠️ Smart Defaults: Works out of the box with sensible defaults
- 📦 TypeScript Support: Fully typed API
Installation
npm install -D vite-plugin-ssg-spa
# Required peer dependencies
npm install puppeteer happy-dom
# Optional: Use Playwright instead of Puppeteer
npm install -D playwrightQuick Start
1. Add to Vite Config
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { viteSSG } from 'vite-plugin-ssg-spa';
export default defineConfig({
plugins: [
react(),
viteSSG(), // Add the plugin
],
});2. Build Your App
npm run buildThe plugin will:
- Extract all static routes from your
src/App.tsx - Automatically crawl your app to discover dynamic routes (blog posts, products, etc.)
- Pre-render each route to static HTML
- Generate a Node.js server with SPA fallback support
- Create platform-specific config files (
vercel.json,netlify.toml,.htaccess)
That's it! No manual route exposure needed. 🎉
3. Run the Production Server
node dist/server/index.jsYour app is now running with:
- Pre-rendered static HTML for all routes (fast initial load, SEO-friendly)
- SPA fallback for dynamic routes (smooth client-side navigation)
Configuration
interface SSGPluginOptions {
/**
* Manually specify routes to pre-render
* If not provided, routes will be automatically extracted from App.tsx
* and discovered via auto-crawling
*/
routes?: string[];
/**
* Timeout for route discovery and crawling in milliseconds
* Increase this if your pages have many images or heavy loading
* @default 60000 (60 seconds)
*/
discoveryTimeout?: number;
/**
* Use Playwright instead of Puppeteer
* @default false
*/
usePlaywright?: boolean;
/**
* Automatically generate platform-specific config files
* Creates: vercel.json, netlify.toml, _redirects, .htaccess
* @default true
*/
generatePlatformConfig?: boolean;
/**
* Enable automatic route crawling - plugin will navigate your app and discover all routes
* @default true
*/
autoCrawl?: boolean;
/**
* Maximum depth for route crawling
* @default 3
*/
crawlDepth?: number;
/**
* Patterns to exclude from crawling (regex strings)
* @default ['/admin', '/login', '/auth']
*/
excludePatterns?: string[];
}Example with Options
// vite.config.ts
export default defineConfig({
plugins: [
react(),
viteSSG({
// Optional: Manually specify additional routes (rarely needed)
routes: ['/custom-route'],
// Site URL for sitemap generation
siteUrl: 'https://yourdomain.com',
// Increase timeout for pages with many images or heavy loading
discoveryTimeout: 90000, // 90 seconds
// Use Playwright instead of Puppeteer
usePlaywright: true,
// Disable automatic config generation if you have custom setup
generatePlatformConfig: false,
// Disable auto-crawling if you only want static routes
autoCrawl: false,
// Increase crawl depth to discover deeply nested routes
crawlDepth: 5,
// Exclude certain routes from being crawled/pre-rendered
excludePatterns: ['/admin', '/dashboard', '/api'],
// Enable debug mode for troubleshooting
debug: true, // Verbose logging with timing and errors
saveScreenshots: true, // Save screenshots of failed pages
}),
],
});Debugging
The plugin includes comprehensive debugging features enabled by default:
viteSSG({
debug: true, // Enabled by default - shows verbose logging
saveScreenshots: true, // Optional - save screenshots of failed renders
})To disable debug mode:
viteSSG({
debug: false, // Disable verbose output
})Debug mode shows:
- ⏱️ Detailed timing for each rendering phase
- 📝 Browser console logs and errors
- 🌐 Network request failures
- 🖼️ Image loading information
- 📸 Screenshots of failed pages (when
saveScreenshots: true)
Example debug output:
🔍 Rendering /about...
URL: http://localhost:5175/about
⏱️ Navigation completed in 1523ms
🖼️ Loaded 12 images
✅ Rendered in 3847ms → about/index.html
📝 Console logs (2):
[log] React app initialized
[info] Page readySee DEBUG.md for complete debugging guide.
Platform Configuration
The plugin automatically generates configuration files for popular deployment platforms:
Generated Files
When you run npm run build, the plugin creates:
vercel.json- For Vercel deploymentsnetlify.toml- For Netlify deploymentspublic/_redirects- Alternative Netlify configpublic/.htaccess- For Apache servers
These files ensure that SPA routing works correctly when users hard refresh or directly access routes.
Disable Auto-Generation
If you want to manage these files yourself:
viteSSG({
generatePlatformConfig: false
})SPA Fallback
The plugin automatically adds SPA fallback support in development and production.
Development Server
All non-static routes are rewritten to /index.html, allowing React Router to handle routing.
npm run dev
# Navigate to /blog/my-post and hard refresh - works! ✅Production Server
The generated Node.js server includes SPA fallback logic:
node dist/server/index.js
# /blog/my-post → Serves pre-rendered HTML or falls back to index.html ✅How Auto-Crawling Works
- Starting from the homepage (
/) and all static routes defined in yourApp.tsx - Following all internal links (
<a href="/...">) in your rendered pages - Recursively crawling discovered pages up to a configurable depth (default: 3 levels)
- Pre-rendering everything it finds to static HTML
Example
If your app has this structure:
/ (homepage)
├── /blog (links to individual posts)
│ ├── /blog/post-1
│ ├── /blog/post-2
│ └── /blog/post-3
└── /aboutThe plugin will:
- Start at
/and find links to/blogand/about - Visit
/blogand discover/blog/post-1,/blog/post-2,/blog/post-3 - Pre-render all 6 pages automatically
No manual route configuration needed! Just make sure your pages link to each other.
Excluding Routes
You can exclude routes from crawling (e.g., admin pages, auth pages):
viteSSG({
excludePatterns: ['/admin', '/dashboard', '/api', '/auth'],
})SEO: Sitemap & Robots.txt
The plugin automatically handles SEO files during the build process:
Automatic Generation
By default, the plugin generates both sitemap.xml and robots.txt:
viteSSG({
siteUrl: 'https://yourdomain.com', // Required for proper sitemap URLs
generateSitemap: true, // Default: true
generateRobotsTxt: true, // Default: true
})Using Your Own Files
If you already have sitemap.xml or robots.txt in your public/ folder:
- The plugin will preserve and copy them to
dist/ - Auto-generation is skipped for existing files
- Your custom files take priority
public/
├── sitemap.xml ← Your custom sitemap
├── robots.txt ← Your custom robots.txt
└── favicon.icoCustom Robots.txt Rules
Configure custom rules for search engine crawlers:
viteSSG({
siteUrl: 'https://yourdomain.com',
robotsTxtOptions: {
policies: [
{
userAgent: '*',
allow: ['/'],
disallow: ['/admin', '/api'],
},
{
userAgent: 'Googlebot',
allow: ['/'],
crawlDelay: 2,
},
],
additionalSitemaps: [
'https://yourdomain.com/sitemap-images.xml',
],
},
})Generated Files
After build, your dist/ folder will contain:
dist/
├── sitemap.xml ← All discovered routes
├── robots.txt ← Search engine instructions
├── index.html
└── ...sitemap.xml includes:
- All crawled routes
- Last modification dates
- Change frequency hints
- Priority values
robots.txt includes:
- Crawler permissions
- Sitemap reference
- Custom policies (if configured)
Deployment
The plugin automatically generates configuration files for all major platforms during build. Just deploy your dist folder!
Option 1: Node.js Server (Recommended)
Deploy the dist folder with the included server:
npm run build
node dist/server/index.jsSet the PORT environment variable:
PORT=8080 node dist/server/index.jsOption 2: Vercel
The plugin automatically creates vercel.json. Just deploy:
vercel deployOption 3: Netlify
The plugin creates both netlify.toml and public/_redirects:
netlify deploy --prod --dir=distOption 4: Other Static Hosts
For GitHub Pages, Cloudflare Pages, etc.:
# Copy index.html as 404.html for SPA fallback
cp dist/index.html dist/404.htmlHow It Works
- Route Extraction: Scans your
src/App.tsxfor React Router routes - Auto-Crawling: Launches a preview server and uses Puppeteer/Playwright to:
- Navigate through your app starting from
/ - Extract all internal links from each page
- Follow links recursively up to the configured depth
- Navigate through your app starting from
- Pre-rendering: Renders each discovered route to static HTML with all client-side code executed
- SSR Server Generation: Creates a Node.js server that:
- Serves pre-rendered HTML for static routes (SEO + fast initial load)
- Serves static assets (CSS, JS, images)
- Falls back to
index.htmlfor client-side routing
- Platform Config: Generates
vercel.json,netlify.toml,.htaccessfor easy deployment
Supported Route Formats
The plugin automatically detects routes in this format:
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/blog/:slug" element={<BlogPost />} />
</Routes>Dynamic routes (:slug, :id) are discovered through auto-crawling by following links in your pages.
Troubleshooting
Routes not being discovered
- Ensure your pages link to each other with
<a href="/path">or<Link to="/path"> - Check that routes are defined in
src/App.tsx - Increase
crawlDepthif routes are deeply nested - Check build logs for crawler output
404 errors on hard refresh
- Verify platform config files were generated (
vercel.json,netlify.toml) - For static hosts, ensure
404.htmlexists:cp dist/index.html dist/404.html - Check server logs for "SPA Fallback" messages
Pre-rendering fails
- Check that pages can render without browser-only APIs in initial render
- Increase
discoveryTimeoutif pages take long to load - Check for console errors in build output
Some routes not crawled
- Add them to excluded patterns if intentional:
excludePatterns: ['/admin'] - Or manually specify:
routes: ['/missing-route'] - Check if pages are reachable through links from the homepage
License
MIT
Contributing
Contributions are welcome! Please open an issue or pull request on GitHub.
Repository
https://github.com/Remote-Skills/vite-plugin-ssg-spa
