app-generator / docs

App Generator - The Official Documentation | AppSeed
https://docs.appseed.us
1 stars 1 forks source link

[React] Server Side Rendering #113

Open mahfujul-helios opened 1 month ago

mahfujul-helios commented 1 month ago

Server Side Rendering

What Is Server Side Rendering?

Server-Side Rendering (SSR) in React involves the process of rendering React components on the server before sending the generated HTML to the client's browser. Unlike traditional client-side rendering, where JavaScript in the browser handles rendering, SSR shifts this responsibility to the server, allowing it to send pre-rendered HTML to the client. This approach offers benefits such as improved performance by reducing initial render time and better SEO by providing search engines with pre-rendered content. Additionally, SSR ensures accessibility by enabling content delivery even when JavaScript is disabled or unsupported. However, implementing SSR can introduce complexities and increased server load compared to client-side rendering, requiring careful consideration based on project requirements and constraints. Overall, SSR in React serves as a valuable technique for enhancing user experience, particularly in scenarios where performance and SEO are critical considerations.

Implementing Server Side Rendering in React

Let’s now dive a bit deeper into how server-side rendering works in Next.js and Express.js by exploring a use case where it can be particularly useful.

Use Case: An E-Commerce Website

Let’s consider a use case where server-side rendering can be particularly useful.

An e-commerce website typically has many pages. Each page displays a product or product category. In addition, these pages are usually dynamic and frequently updated, so it’s important to make sure they are easily discoverable by search engines and accessible to all users. To achieve this, you can build your e-commerce website with server-side rendering in Next.js or Express.js. This approach would allow you to generate an HTML markup for each page on the server. Thus, making it easy for search engines to crawl and index the content.

Implementing Server Side Rendering using Next.js

Let’s now look into how we can implement server-side rendering in Next.js for an e-commerce website.

Step 1: Create a new Next.js project

To get started, you will have to create a new Next.js project by running the following commands in your terminal:

npx create-next-app my-ecommerce-app
cd my-ecommerce-app

Step 2: Add required dependencies

Next, you’ll need to add the following dependencies to your project:

npm install react react-dom next

This is how the package.json looks right now.

{
"name": "my-ecommerce-app",
"version": "0.1.0",
"private": true,
"scripts": {
  "dev": "next dev",
  "build": "next build",
  "start": "next start",
  "lint": "next lint"
},
"dependencies": {
  "@next/font": "13.1.6",
  "eslint": "8.34.0",
  "eslint-config-next": "13.1.6",
  "next": "13.1.6",
  "react": "18.2.0",
  "react-dom": "18.2.0"
}
}

Please note the versions of the packages mentioned above, as they were used when this guide was created.

Step 3: Set up the environment configuration

We will now define an environment variable to be stored in a .env.local file, which stores configuration settings that can be used across different environments (e.g. development, staging, production).

To set an environment variable in your project. You will have to create a file called .env.local in the root of your project directory and add a line like the following:

API_URL=http://localhost:3000 It is important to note that you should not check this file into source control, as it may contain sensitive information such as database credentials or API keys.

Instead, you can create a template file called .env.example that contains placeholder values for your environment variables and check that file into source control. Other developers can then copy this file and fill in the values for the environment variables.

Step 4: Create a new page

Next.js uses a file-based routing system, which means that a file in the pages directory represents each page in your application. To create a new page, simply create a new file in the page’s directory with the desired URL path. For example, to create a page displaying a product list, you could create a file called pages/products/index.js.

In this file, you can define a React component that will be rendered when the user visits the /products URL path. Here’s an example component that fetches a list of products from an API and displays them in a list:

function ProductsPage() {
  const [products, setProducts] = useState([])

  useEffect(() => {
    async function fetchProducts() {
      const res = await fetch('/api/products')
      const products = await res.json()
      setProducts(products)
    }
    fetchProducts()
  }, [])

  return (
    <div>
      <h1>Products</h1>
      <ul>
        {products.map((product) => (
          <li key={product.id}>{product.name}</li>
        ))}
      </ul>
    </div>
  )
}

export default ProductsPage

Step 5: Create an API endpoint

To fetch the list of products, we’ve used an API endpoint at /api/products. Next.js provides a built-in API routing system that makes it easy to create serverless API endpoints.

To create an API endpoint, create a new file in the pages/api directory. For example, to create an API endpoint that returns a list of products, you could create a file called pages/api/products.js.

In this file, you can define a function that will be executed when the user requests the API endpoint. For the purpose of this guide, we will use an example function that fetches a list of products from a mock API:

const products = [  { id: 1, name: 'Product 1' },  { id: 2, name: 'Product 2' },  { id: 3, name: 'Product 3' },]

export default function handler(req, res) {
  res.status(200).json(products)
}

Step 6: Update the page to use server-side rendering

By default, Next.js uses client-side rendering (CSR) to render pages, which means that the JavaScript code is executed in the user’s browser. To switch to server-side rendering (SSR), you’ll need to update your page component to use a getServerSideProps function.

The getServerSideProps function is a special function that runs on the server before the page is rendered. It can be used to fetch data from an API or database, and return it as props to the page component.

Here’s an updated version of the pages/products/index.js file that uses getServerSideProps to fetch the list of products on the server:

import { useState } from 'react'

function ProductsPage({ products }) {
  const [loading, setLoading] = useState(false)

  return (
    <div>
      <h1>Products</h1>
      <ul>
        {products.map((product) => (
          <li key={product.id}>{product.name}</li>
        ))}
      </ul>
    </div>
  )
}

export async function getServerSideProps() {
  const res = await fetch(`${process.env.API_URL}/api/products`)
  const products = await res.json()
  return { props: { products } }
}

export default ProductsPage

Note that we’ve moved the useState hook for the loading state outside of the getServerSideProps function, since it needs to be initialized on the client as well.

Step 7: Start the development server You can now start the development server by running the following command in your terminal:

npm run dev This will start a local development server at http://localhost:3000.

Step 8: Test the application You can now test the application by visiting the /products URL path in your web browser. You should see a list of products displayed on the page.

If you view the page source in your browser, you’ll also see that the list of products is included in the HTML markup, meaning the page was rendered on the server.

Congratulations, you now know how to implement server-side rendering in a Next.js application!

Implementing Server Side Rendering using Express.js Let’s now look into how we can implement the same use case in an Express.js application:

Step 1: Create a new Express.js application

To get started, you will have to create a new directory for your project and run the following command in your terminal:

npm init

You can now see a package.json file in your project directory.

Next, install Express.js and the necessary dependencies by running the following command:

npm install express react react-dom next

This is how the package.json looks right now.


{
"name": "express-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
  "express": "^4.18.2",
  "next": "^13.1.6",
  "react": "^18.2.0",
  "react-dom": "^18.2.0"
}
}

Please note the versions of the packages mentioned above, as they were used when this guide was created.

Step 2: Set up the environment configuration

We will now define an environment variable to be stored in a .env.local file, which stores configuration settings that can be used across different environments (e.g. development, staging, production).

To set an environment variable in your project. You will have to create a file called .env.local in the root of your project directory and add a line like the following:

API_URL=http://localhost:3000

It is important to note that you should not check this file into source control, as it may contain sensitive information such as database credentials or API keys.

Instead, you can create a template file called .env.example that contains placeholder values for your environment variables and check that file into source control. Other developers can then copy this file and fill in the values for the environment variables.

Step 3: Set up the server

Now create a new file called server.js in the root of your project directory, and add the following code:

const express = require('express')
const next = require('next')

const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare().then(() => {
  const server = express()

  server.get('/', (req, res) => {
    return app.render(req, res, '/home')
  })

  server.get('/products', (req, res) => {
    return app.render(req, res, '/products')
  })

  server.all('*', (req, res) => {
    return handle(req, res)
  })

  server.listen(3000, (err) => {
    if (err) throw err
    console.log('> Ready on http://localhost:3000')
  })
})

This code sets up an Express.js server that listens for incoming requests on port 3000. The app object is a Next.js application instance, which we use to render pages in response to requests.

In this example, we’ve set up two routes: / and /products. When a request comes in for either of these routes, the server calls app.render() to render the corresponding page. If the requested route doesn’t match either of these, the server falls back to the handle function, which serves the appropriate page using client-side rendering.

Step 4: Create the home page Create a new file called pages/home.js in a directory called pages in the root of your project directory, and add the following code:


import Link from 'next/link'

function HomePage() {
  return (
    <div>
      <h1>Welcome to our e-commerce website!</h1>
      <Link href="/products">
        <a>View our products</a>
      </Link>
    </div>
  )
}

export default HomePage

This code defines a simple home page that displays a welcome message and a link to view the products page.

Step 5: Create the products page

Create a new file called pages/products.js in the pages directory, and add the following code:

import { useEffect, useState } from 'react';

function ProductsPage() {
const [products, setProducts] = useState([]);

useEffect(() => {
  async function fetchProducts() {
    const response = await fetch('/api/products');
    const data = await response.json();
    setProducts(data.products);
  }

  fetchProducts();
}, []);

return (
  <div>
    <h1>Products</h1>
    <ul>
      {products.map(product => (
        <li key={product.id}>{product.name}</li>
      ))}
    </ul>
  </div>
);
}

export default ProductsPage;

This code defines a products page that displays a list of products fetched from an API endpoint. The useEffect hook is used to manage state and fetch data from the server. When the component mounts, the useEffect hook calls the fetchProducts function to retrieve the products from the API.

Step 6: Create the API endpoint

You will now have to add an API endpoint for products in server.js file present in the root of your project directory, and add the following code:


  server.get('/api/products', (req, res) => {
    const products = [
      { id: 1, name: 'Product 1' },
      { id: 2, name: 'Product 2' },
      { id: 3, name: 'Product 3' },
    ];

    res.status(200).json({ products });
  });

This code defines an API endpoint that returns a list of products. This endpoint would fetch data from a database or other data source in a real-world scenario.

The updated server.js should look as follows:

const express = require('express')
const next = require('next')

const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare().then(() => {
  const server = express()

  server.get('/', (req, res) => {
    return app.render(req, res, '/home')
  })

  server.get('/products', (req, res) => {
    return app.render(req, res, '/products')
  })

  server.get('/api/products', (req, res) => {
    const products = [
      { id: 1, name: 'Product 1' },
      { id: 2, name: 'Product 2' },
      { id: 3, name: 'Product 3' },
    ];

    res.status(200).json({ products });
  });

  server.all('*', (req, res) => {
    return handle(req, res)
  })

  server.listen(3000, (err) => {
    if (err) throw err
    console.log('> Ready on http://localhost:3000')
  })
})

Step 7: Start the server

Start the Express.js server by running the following command in your terminal:

node server.js

This will start the server and make it available at http://localhost:3000.

When you navigate to http://localhost:3000/, you should see the home page with a link to the products page.