๐ Vite + SSR (Server-Side Rendering) Custom Setup Guide
Vite supports SSR, but unlike Next.js, it requires a manual Express (or Fastify) server to handle rendering. Below is a custom SSR setup using Vite + React + Express.
๐ ๏ธ Step 1: Create a Vite SSR Project
Run the following command to create a new Vite app:
npm create vite@latest my-ssr-app --template react
Then install dependencies:
cd my-ssr-app
npm install
npm install express @vitejs/plugin-react
๐ Step 2: Configure Vite for SSR
Modify vite.config.ts
:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
ssr: {
noExternal: ['react', 'react-dom'], // Ensure dependencies are bundled for SSR
},
});
๐ Step 3: Create an Express Server for SSR
Inside your project root, create a server
folder. Inside it, create server.ts
:
import express from 'express';
import { createServer as createViteServer } from 'vite';
import path from 'path';
async function startServer() {
const app = express();
const vite = await createViteServer({
server: { middlewareMode: true },
});
app.use(vite.middlewares);
app.use('*', async (req, res, next) => {
try {
const url = req.originalUrl;
let template = await vite.transformIndexHtml(url, `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite SSR</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/entry-client.tsx"></script>
</body>
</html>
`);
const { render } = await vite.ssrLoadModule('/src/entry-server.tsx');
const appHtml = await render(url);
res.status(200).set({ 'Content-Type': 'text/html' }).end(template.replace(`<div id="root"></div>`, `<div id="root">${appHtml}</div>`));
} catch (error) {
vite.ssrFixStacktrace(error);
next(error);
}
});
app.listen(3000, () => console.log('Server running at http://localhost:3000'));
}
startServer();
๐ Step 4: Create SSR Entry Files
Client Entry: src/entry-client.tsx
import React from 'react';
import { hydrateRoot } from 'react-dom/client';
import App from './App';
hydrateRoot(document.getElementById('root')!, <App />);
Server Entry: src/entry-server.tsx
import React from 'react';
import { renderToString } from 'react-dom/server';
import App from './App';
export function render(url: string) {
return renderToString(<App />);
}
๐ Step 5: Update package.json
Scripts
Modify package.json
:
"scripts": {
"dev": "node server/server.ts",
"build": "vite build",
"preview": "vite preview"
}
๐ Step 6: Run the SSR Server
npm run dev
Now visit localhost:3000 โ Your React app will be rendered server-side first, then hydrated on the client!
๐ Extra Enhancements
โ
Data Fetching with fetch()
inside entry-server.tsx
โ
Express API Routes for dynamic content
โ
Fastify instead of Express for better performance
ย