DEV Community

Willyams Yujra
Willyams Yujra

Posted on • Edited on

Create an SSR Application with Vite, React, React Query and React Router

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
  • 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/
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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>
  );
};
Enter fullscreen mode Exit fullscreen mode

Important Note: PageServer uses suspense: true in all requests to ensure proper SSR rendering. On the other hand, PageBrowser uses suspense: 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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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
      },
    },
  },
});
Enter fullscreen mode Exit fullscreen mode

7. Development and Production

7.1 Development Server

Run the development server:

yarn dev
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

The build will generate:

/dist/
  ├── app.js           # Application runtime
  ├── client/          # SSR public assets
  └── client/spa/      # SPA public assets
Enter fullscreen mode Exit fullscreen mode

Enter the 'dist' directory and run the server

cd dist
node app.js
Enter fullscreen mode Exit fullscreen mode

7.3 Sanbox Production Server Configuration

For production deployment, you can configure sanbox server settings using a private directory:

  1. 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"
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. Create /private/.env for server configuration:
SERVER_HOST=127.0.0.1
SERVER_PORT=4000
Enter fullscreen mode Exit fullscreen mode

Start the production server for the Sandbox project

cd dist
yarn install
yarn start
Enter fullscreen mode Exit fullscreen mode

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:

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)

Collapse
 
yracnet profile image
Willyams Yujra

The plugin was renamed from vite-plugin-ssr-kit to vite-plugin-ssr-config at 2025-01-16.