Static Site Generation and Server-Side Rendering
Understanding how Rspress builds and renders your documentation is crucial for optimization and deployment. This chapter explores Static Site Generation (SSG), Server-Side Rendering (SSR), and the build process in Rspress.
Understanding SSG vs SSR
Static Site Generation (SSG)
SSG pre-renders all pages at build time, generating static HTML files:
Build Time:
Markdown/MDX → HTML + CSS + JS → Static Files
Runtime:
User Request → Server → Static HTML (Fast!)Advantages:
- Extremely fast page loads
- Better SEO (content immediately available)
- Lower server costs (just serve static files)
- Works on simple hosting (no server required)
- Can be cached on CDN
Best For:
- Documentation sites
- Blogs
- Marketing pages
- Content that doesn't change frequently
Server-Side Rendering (SSR)
SSR renders pages on each request:
Runtime:
User Request → Server Renders HTML → Response
Each request generates fresh HTMLAdvantages:
- Always up-to-date content
- Can personalize per user
- Access to request data
- Dynamic content generation
Best For:
- User dashboards
- Personalized content
- Frequently changing data
- Dynamic applications
Rspress's Approach
Rspress primarily uses SSG with optional SSR capabilities:
// Default: Pure SSG
npm run build // Generates static HTML files
// With SSR: Hybrid approach (advanced)
// Rspress can also do client-side hydrationBuild Process
Build Pipeline
Understanding Rspress's build pipeline:
graph LR
A[Source Files] --> B[Parse MDX/MD]
B --> C[Process Components]
C --> D[Generate Routes]
D --> E[Bundle Assets]
E --> F[Generate HTML]
F --> G[Optimize Output]
G --> H[Static Files]Build Command
# Development build (fast, no optimization)
npm run dev
# Production build (optimized)
npm run build
# Preview production build
npm run previewBuild Output
After building, Rspress generates:
doc_build/
├── index.html # Homepage
├── guide/
│ ├── index.html # /guide/
│ ├── installation.html # /guide/installation
│ └── configuration.html # /guide/configuration
├── assets/
│ ├── chunks/ # Code-split JS chunks
│ ├── css/ # Compiled CSS
│ └── images/ # Optimized images
└── _rspress/
└── metadata.json # Build metadataBuild Configuration
Basic Build Settings
Configure the build process:
// rspress.config.ts
import { defineConfig } from 'rspress/config';
export default defineConfig({
root: 'docs',
outDir: 'doc_build', // Output directory
// Build configuration
builderConfig: {
// Output format
output: {
cleanDistPath: true, // Clean output before build
distPath: {
root: 'doc_build',
},
},
// Performance
performance: {
chunkSplit: {
strategy: 'split-by-experience',
},
},
},
});Optimization Settings
Optimize build output:
export default defineConfig({
builderConfig: {
// Minification
output: {
minify: {
js: true,
css: true,
html: true,
},
},
// Code splitting
performance: {
chunkSplit: {
strategy: 'split-by-experience',
forceSplitting: [
/node_modules/,
],
},
},
// Source maps (for debugging)
output: {
sourceMap: {
js: 'source-map', // or false for production
css: true,
},
},
},
});Pre-rendering
How Pre-rendering Works
Rspress pre-renders pages using React SSR:
- Parse Content: Read and parse Markdown/MDX
- Execute React: Run React components to generate HTML
- Serialize State: Capture application state
- Generate HTML: Create complete HTML with inlined state
- Optimize: Minify and optimize output
Pre-render Configuration
Control pre-rendering behavior:
export default defineConfig({
// Pre-render all routes
ssg: {
strict: true, // Fail build on errors
},
// Route-specific configuration
route: {
// Routes to pre-render
include: ['/', '/guide/**', '/api/**'],
// Routes to skip
exclude: ['**/draft/**'],
},
});Hydration
What is Hydration?
Hydration makes static HTML interactive:
1. Server sends static HTML (fast initial load)
2. Browser downloads JavaScript
3. React "hydrates" the HTML (attaches event listeners)
4. Page becomes fully interactiveHydration in Rspress
// During build (SSG)
<button>Click Me</button>
// After hydration (interactive)
<button onClick={handleClick}>Click Me</button>Optimizing Hydration
// Lazy load heavy components
import { lazy, Suspense } from 'react';
const HeavyComponent = lazy(() => import('./HeavyComponent'));
export function Page() {
return (
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
);
}Code Splitting
Automatic Code Splitting
Rspress automatically splits code by route:
Homepage: homepage.js (50KB)
Guide Page: guide.js (30KB)
API Page: api.js (40KB)
Shared: vendor.js (100KB)Manual Code Splitting
Split large components:
// Instead of this (loads everything immediately)
import { Chart } from './Chart';
import { Table } from './Table';
import { Graph } from './Graph';
// Do this (loads on demand)
const Chart = lazy(() => import('./Chart'));
const Table = lazy(() => import('./Table'));
const Graph = lazy(() => import('./Graph'));Route-based Splitting
Rspress automatically splits by route:
// Each route gets its own bundle
/guide/ → guide-route.js
/guide/start → guide-start-route.js
/api/config → api-config-route.jsBuild Performance
Measuring Build Time
# Measure build time
time npm run build
# Enable verbose logging
npm run build -- --verbose
# Analyze bundle size
npm run build -- --analyzeOptimization Strategies
- Reduce Dependencies
// Heavy library (100KB)
import _ from 'lodash';
// Lighter alternative (10KB)
import debounce from 'lodash/debounce';- Lazy Load Heavy Content
import { lazy } from 'react';
const VideoPlayer = lazy(() => import('./VideoPlayer'));
<Suspense fallback="Loading...">
<VideoPlayer />
</Suspense>- Optimize Images
# Before adding to docs
imagemin docs/images/* --out-dir=docs/images/optimizedCaching
Enable build caching for faster rebuilds:
export default defineConfig({
builderConfig: {
// Enable caching
cache: true,
},
});Build Modes
Development Mode
Fast builds with hot reload:
npm run dev
# Features:
- Fast incremental builds
- Hot module replacement
- Source maps enabled
- No minification
- Verbose error messagesProduction Mode
Optimized builds for deployment:
npm run build
# Features:
- Full optimization
- Minification enabled
- Tree shaking
- Dead code elimination
- Asset optimizationPreview Mode
Test production build locally:
# Build first
npm run build
# Then preview
npm run preview
# Access at http://localhost:4173Advanced Build Features
Custom Build Scripts
Add custom build scripts:
// package.json
{
"scripts": {
"build": "rspress build",
"build:analyze": "rspress build --analyze",
"build:staging": "NODE_ENV=staging rspress build",
"build:production": "NODE_ENV=production rspress build"
}
}Environment Variables
Use environment variables in builds:
// rspress.config.ts
export default defineConfig({
define: {
'process.env.API_URL': JSON.stringify(
process.env.NODE_ENV === 'production'
? 'https://api.production.com'
: 'https://api.staging.com'
),
},
});// Use in components
const apiUrl = process.env.API_URL;Build Hooks
Run custom logic during build:
export default defineConfig({
plugins: [
{
name: 'custom-build-plugin',
async buildStart() {
console.log('Build starting...');
},
async buildEnd() {
console.log('Build complete!');
// Post-build tasks
},
},
],
});Incremental Builds
Understanding Incremental Builds
Only rebuild changed pages:
# First build (all pages)
npm run build # 30 seconds
# Change one file
# Second build (only changed page)
npm run build # 5 secondsEnabling Incremental Builds
export default defineConfig({
builderConfig: {
cache: {
buildDependencies: {
config: [__filename],
},
},
},
});SEO Optimization
Meta Tags
Add meta tags during build:
export default defineConfig({
head: [
['meta', { name: 'description', content: 'Site description' }],
['meta', { name: 'keywords', content: 'keywords, here' }],
['meta', { property: 'og:title', content: 'Site Title' }],
['meta', { property: 'og:description', content: 'Description' }],
],
});Sitemap Generation
Generate sitemap during build:
export default defineConfig({
plugins: [
{
name: 'sitemap-generator',
async buildEnd(routes) {
const sitemap = generateSitemap(routes);
await fs.writeFile('doc_build/sitemap.xml', sitemap);
},
},
],
});Robots.txt
Add robots.txt:
# public/robots.txt
User-agent: *
Allow: /
Sitemap: https://example.com/sitemap.xmlPerformance Monitoring
Bundle Analysis
Analyze bundle size:
# Generate bundle analysis
npm run build -- --analyze
# Opens visualization in browser
# Shows:
# - Bundle sizes
# - Chunk composition
# - Dependency treeLighthouse Scores
Test your built site:
# Install Lighthouse
npm install -g lighthouse
# Build and preview
npm run build
npm run preview
# Run Lighthouse
lighthouse http://localhost:4173 --viewCore Web Vitals
Monitor performance metrics:
- LCP (Largest Contentful Paint): < 2.5s
- FID (First Input Delay): < 100ms
- CLS (Cumulative Layout Shift): < 0.1
Troubleshooting
Build Failures
- Memory Issues
# Increase Node memory
NODE_OPTIONS=--max_old_space_size=4096 npm run build- Missing Dependencies
# Clean install
rm -rf node_modules package-lock.json
npm install- Build Errors
# Verbose logging
npm run build -- --verbose
# Check specific file
npm run build -- --debugSlow Builds
- Disable source maps in production
- Reduce dependencies
- Use build caching
- Optimize images before building
- Split large pages into smaller ones
Large Bundle Sizes
# Analyze what's included
npm run build -- --analyze
# Common culprits:
# - Large libraries (lodash, moment.js)
# - Duplicate dependencies
# - Unoptimized images
# - Unused CSSBest Practices
Build Checklist
Before deploying:
- [ ] Run production build
- [ ] Test with preview
- [ ] Check bundle sizes
- [ ] Verify all pages load
- [ ] Test on mobile
- [ ] Check Lighthouse scores
- [ ] Validate HTML
- [ ] Test all links
CI/CD Integration
# .github/workflows/deploy.yml
name: Build and Deploy
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '18'
- run: npm ci
- run: npm run build
- run: npm run testNext Steps
- Learn about Deployment strategies
- Explore Internationalization
- Read about Static Assets optimization
Performance Tips
- Enable build caching for faster rebuilds
- Use code splitting for large sites
- Optimize images before adding them
- Monitor bundle sizes regularly
Production Builds
- Always test production builds before deploying
- Enable minification in production
- Disable source maps in production
- Use environment variables for configs