Logo

dev-resources.site

for different kinds of informations.

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

Published at
1/10/2025
Categories
vitejs
ssr
react
reactquery
Author
Willyams Yujra
Categories
4 categories in total
vitejs
open
ssr
open
react
open
reactquery
open
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-kit 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-kit 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
  • 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-kit: Plugin that enables server-side rendering in Vite applications.

    • Handles SSR configuration and setup
    • Provides utilities for SSR lifecycle management
    • 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/[email protected]/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 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>
  );
}

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-kit";

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

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

To start the production server:

cd dist
yarn start

or

cd dist # The directory is a Sandbox project
yarn install
yarn start

Note: The server will be running at: http://127.0.0.1:4000/myapp

Additional Resources

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.

Additional Resources

For more detailed information and resources related to vite-plugin-ssr-kit, please refer to the following:

Featured ones: