Create an SSR Application with Vite, React, React Query and React Router
In this tutorial, we will create a server-side rendered (SSR) application using Vite, React, React Query, React Bootstrap, and React Router. We'll also configure the vite-plugin-ssr-config
plugin to handle SSR rendering.
Prerequisites
Before starting, ensure you have:
- Node.js installed (version 20 or higher)
- Yarn package manager
- Basic knowledge of React and vite ecosystem
1. Create the Application
First, create a new Vite project with the React template:
yarn create vite my-ssr-app --template react
cd my-ssr-app
2. Install Required Libraries
Install the following dependencies:
yarn add react-router-dom@^6.28.0 react-bootstrap react-query vite-plugin-ssr-config vite-plugin-pages
Let's understand what each package does:
-
react-router-dom@^6.28.0: Handles routing in React applications. We use version 6.28.0 specifically because version 7.x requires Remix framework.
- Provides components like
Route
,Link
, and routing hooks - Enables client-side navigation
- Manages URL parameters and query strings
- Provides components like
-
react-bootstrap: React components that implement Bootstrap's design system.
- Provides pre-built, responsive UI components
- Includes navigation, forms, cards, and other UI elements
- No need to write Bootstrap classes manually
-
react-query: Powerful data synchronization library for React.
- Manages server state in React applications
- Provides hooks for data fetching, caching, and updates
- Handles loading and error states automatically
-
vite-plugin-ssr-config: Plugin that enables server-side rendering in Vite applications.
- Handles SSR configuration
- Manages client/server code splitting
-
vite-plugin-pages: File system based routing plugin for Vite.
- Creates routes based on file structure
- Supports dynamic routes
- Integrates with react-router-dom
3. Configure Project Structure
3.1 Move the Index File
Move the /index.html
file to /spa/index.html
. This separation allows us to maintain both SSR and SPA versions of the application:
mkdir spa
mv index.html spa/
Note: The /spa/index.html file will serve as the entry point for the SPA version, while SSR will use a different entry point.
3.2 Create SSR Directory Structure
Create the necessary directories for SSR:
mkdir -p ssr/pages/posts/$id
4. Create the Root Document
Create /ssr/root.jsx
to define the root document structure for server-side rendering:
import { LiveReload } from "@ssr/liveReload.jsx";
import { ViteScripts } from "@ssr/viteScripts.jsx";
import { Container, Nav, Navbar } from "react-bootstrap";
import { Link, Outlet } from "react-router-dom";
export const RootDocument = () => {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React SSR</title>
<link rel="icon" href="vite.svg" type="image/svg" />
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
rel="stylesheet"
/>
<LiveReload />
</head>
<body>
<Container>
<Navbar bg="light" expand="lg">
<Navbar.Brand href="/myapp/">Vite SSR</Navbar.Brand>
<Navbar.Toggle aria-controls="navbarNav" />
<Navbar.Collapse id="navbarNav">
<Nav className="me-auto">
<Nav.Link to="/" as={Link}>
Home
</Nav.Link>
<Nav.Link to="/posts" as={Link}>
Posts
</Nav.Link>
<Nav.Link href="/myapp/spa">Vite SPA Entry</Nav.Link>
</Nav>
</Navbar.Collapse>
</Navbar>
</Container>
<Container>
<Outlet />
</Container>
<ViteScripts />
</body>
</html>
);
};
Important Note: PageServer uses
suspense: true
in all requests to ensure proper SSR rendering. On the other hand, PageBrowser usessuspense: false
to allow smooth client-side navigation. This setup guarantees correct SSR rendering while preventing flickering and inconsistencies between the server-rendered content and the client-side state during hydration.
5. Create Application Pages
5.1 Home Page
Create /ssr/pages/index.jsx
:
import { Container, Button } from "react-bootstrap";
import { Link } from "react-router-dom";
export default function HomePage() {
return (
<Container>
<h1>Welcome to the Vite + React SSR App</h1>
<Button as={Link} to="/posts">
Go to Posts
</Button>
</Container>
);
}
5.2 Posts List Page
Create /ssr/pages/posts/index.jsx
:
import { Card, Col, Row } from "react-bootstrap";
import { useQuery } from "react-query";
import { Link } from "react-router-dom";
const getPosts = () =>
fetch("https://jsonplaceholder.typicode.com/posts")
.then((r) => r.json())
.catch((e) => {
throw e;
});
export default function PostsPage() {
const { data = [] } = useQuery("posts", getPosts);
return (
<div>
<Row>
{data.map((post) => (
<Col key={post.id} sm={12} md={6} lg={4} className="mb-4">
<Card>
<Card.Body>
<Card.Title>{post.title}</Card.Title>
<Card.Text>{post.body}</Card.Text>
<Card.Link as={Link} to={`/posts/${post.id}`}>
Read More
</Card.Link>
</Card.Body>
</Card>
</Col>
))}
</Row>
</div>
);
}
5.3 Single Post Page
Create /ssr/pages/posts/$id/index.jsx
: (Note: Using $id for Remix-style routing)
import { Card } from "react-bootstrap";
import { useQuery } from "react-query";
import { Link, useParams } from "react-router-dom";
const getPost = (id) =>
fetch("https://jsonplaceholder.typicode.com/posts/" + id)
.then((r) => r.json())
.catch((e) => {
throw e;
});
export default function PostPage() {
const { id } = useParams();
const { data = {} } = useQuery(["posts", id], () => getPost(id));
return (
<Card>
<Card.Header>{data.title}</Card.Header>
<Card.Body>
<Card.Text>{data.body}</Card.Text>
<Card.Link as={Link} to="/posts">
Back to Posts
</Card.Link>
</Card.Body>
</Card>
);
}
6. Configure Vite
Modify the existing vite.config.js
file:
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
import pages from "vite-plugin-pages";
import ssr from "vite-plugin-ssr-config";
export default defineConfig({
base: "/myapp",
plugins: [
react(),
pages({
routeStyle: "remix", // Important: This enables Remix-style routing ($id instead of [id])
dirs: "ssr/pages",
}),
ssr({
rootDocument: "ssr/root.jsx",
}),
],
build: {
rollupOptions: {
input: {
spa: "spa/index.html", // Include the original React SPA entry point
},
},
},
});
7. Development and Production
7.1 Development Server
Run the development server:
yarn dev
Access your application at:
- SSR version:
http://localhost:5173/myapp
- SPA version:
http://localhost:5173/myapp/spa
7.2 Production Build
Build the application:
yarn build
You should see output similar to this:
yarn run v1.22.22
$ vite build
vite v6.0.7 building for production...
CLIENT BUILD
vite v4.5.5 building for production...
✓ 396 modules transformed.
dist/client/spa/index.html 0.64 kB │ gzip: 0.35 kB
dist/client/manifest.json 1.49 kB │ gzip: 0.38 kB
dist/client/assets/react-35ef61ed.svg 4.13 kB │ gzip: 2.05 kB
dist/client/assets/index-a36d8b68.css 1.39 kB │ gzip: 0.72 kB
dist/client/chunks/index-600303c7.js 0.46 kB │ gzip: 0.32 kB
dist/client/chunks/index-ef84de97.js 0.55 kB │ gzip: 0.37 kB
dist/client/assets/spa-dc418d3e.js 0.94 kB │ gzip: 0.50 kB
dist/client/chunks/preload-753c2a40.js 1.70 kB │ gzip: 0.89 kB
dist/client/assets/main-79767691.js 2.74 kB │ gzip: 1.16 kB
dist/client/chunks/vendor-d8646257.js 240.50 kB │ gzip: 75.98 kB
✓ built in 1.33s
SERVER BUILD
vite v4.5.5 building SSR bundle for production...
✓ 13 modules transformed.
dist/bin/index-066b4199.js 0.81 kB
dist/bin/index-b8b66606.js 0.92 kB
dist/bin/virtual-6df6f849.js 0.98 kB
dist/app.js 1.22 kB
dist/bin/ssr-27500c0d.js 4.52 kB
✓ built in 52ms
✓ 31 modules transformed.
✓ built in 1.92s
Done in 2.43s.
The build will generate:
/dist/
├── app.js # Application runtime
├── client/ # SSR public assets
└── client/spa/ # SPA public assets
Enter the 'dist' directory and run the server
cd dist
node app.js
7.3 Sanbox Production Server Configuration
For production deployment, you can configure sanbox server settings using a private directory:
- Create
/private/package.json
for server-specific dependencies
{
"name": "app",
"private": true,
"type": "module",
"scripts": {
"start": "node app.js"
},
"dependencies": {
"react": "^18.3.1",
"react-bootstrap": "^2.10.7",
"react-dom": "^18.3.1",
"react-query": "^3.39.3",
"react-router-dom": "^6.28.0"
}
}
- Create
/private/.env
for server configuration:
SERVER_HOST=127.0.0.1
SERVER_PORT=4000
Start the production server for the Sandbox project
cd dist
yarn install
yarn start
Note: The server will be running at: http://127.0.0.1:4000/myapp
Additional Resources
For more detailed information and resources related to vite-plugin-ssr-config
, please refer to the following:
- npm Package: vite-plugin-ssr-config
GitHub Repository: yracnet/vite-plugin-ssr-config
For a complete working example, visit the GitHub example repository and check the
packages/examples/my-ssr-app
directory.For more information about the SSR plugin, refer to the vite-plugin-ssr-config documentation.
Summary
This tutorial has shown you how to:
- Set up a Vite project with SSR capabilities
- Integrate React Query, React Bootstrap, and React Router
- Create both SSR and SPA versions of your application
- Configure development and production environments
- Handle server-side rendering of React components
The resulting application provides a solid foundation for building performant, server-rendered React applications while maintaining the option to serve a traditional SPA version when needed.
Top comments (1)
The plugin was renamed from
vite-plugin-ssr-kit
tovite-plugin-ssr-config
at 2025-01-16.