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
yracnet
Categories
4 categories in total
vitejs
open
ssr
open
react
open
reactquery
open
Author
7 person written this
yracnet
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
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-kit 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-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/
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/[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>
  );
};
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-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
      },
    },
  },
});
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

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

To start the production server:

cd dist
yarn start
Enter fullscreen mode Exit fullscreen mode

or

cd dist # The directory is a Sandbox project
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

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:

reactquery Article's
30 articles in total
Favicon
Create an SSR Application with Vite, React, React Query and React Router
Favicon
TanStack query or RTK query.
Favicon
Simplified State Management with useRQState: A Better Alternative to useState
Favicon
Master React API Management with TanStack React Query: Best Practices & Examples
Favicon
How to Fetch Data Using Axios and React Query in ReactJS
Favicon
Efficient Refresh Token Implementation with React Query and Axios
Favicon
react-query swrjs alova In-Depth Comparison
Favicon
Mastering React Query. Simplifying Data Management in React with Separation Patterns
Favicon
Mastering React Query. Structuring Your Code for Scalability and Reusability
Favicon
Why you should try React Query?
Favicon
How to used Suspense?
Favicon
Infinite list loading πŸ€”, with React Query - useInfiniteQuery hook !
Favicon
Building a CRUD app with React Query, TypeScript, and Axios
Favicon
TLDR; Suspense in react-query
Favicon
TLDR; gcTime & staleTime in react-query
Favicon
Building Infinite Scrolling in React Js with React Query v5
Favicon
Optimized Infinite Scroll with Next.js 14 Server Actions and React Query
Favicon
Building a Todo List with TypeScript and React Query: A Comprehensive Guide
Favicon
React Safe Query - A lightweight, type-safe wrapper built around React Query
Favicon
React Query Mutations Offline React-Native
Favicon
Improving UX by using cache
Favicon
How to organize your API layer in React using React Query
Favicon
Conociendo a React Query
Favicon
Build a CRUD App in React with Tanstack Query and Material UI [Final]
Favicon
Build a CRUD App in React with Tanstack Query and Material UI [Part 2]
Favicon
Integrating React Query with Next.js πŸš€
Favicon
Build a CRUD App in React with Tanstack Query and Material UI [Part 1]
Favicon
React query for data streaming
Favicon
Manual Fetch with Tanstack Query
Favicon
Setting up React Query in your ReactΒ project

Featured ones: