Using Server-Side Rendering (SSR) with React in Next.js: A Complete Example
- Mark Kendall
- Feb 11
- 3 min read
Using Server-Side Rendering (SSR) with React in Next.js: A Complete Example
This article provides a complete example of implementing Server-Side Rendering (SSR) with React in Next.js using TypeScript, including both server-side and client-side code. We'll build a simple product details page, demonstrating how data fetched on the server is used to render the component on the client.
Why SSR?
SSR offers improved SEO, faster FCP, and better performance, especially on slower devices.
Project Setup (if you haven't already):
```bash
npx create-next-app@latest my-next-app --typescript
cd my-next-app
```
File Structure:
```
my-next-app/
├── pages/
│ └── products/
│ └── [id].tsx // Product details page (SSR logic here)
├── types/
│ └── product.d.ts // Product interface
├── components/ // Reusable React components (optional)
│ └── ProductDisplay.tsx // Component to display product details
├── public/ // Static assets
├── styles/ // CSS or other styling
├── ... other files ...
```
1. Define the Product Interface (`types/product.d.ts`):
```typescript
interface Product {
id: number;
name: string;
description: string;
price: number;
image: string;
}
```
2. Product Display Component (`components/ProductDisplay.tsx`):
```typescript
import Image from 'next/image';
import React from 'react';
interface ProductDisplayProps {
product: Product;
}
const ProductDisplay: React.FC<ProductDisplayProps> = ({ product }) => {
return (
<div>
<h1>{product.name}</h1>
<Image src={product.image} alt={product.name} width={500} height={500} />
<p>{product.description}</p>
<p>Price: ${product.price}</p>
</div>
);
};
export default ProductDisplay;
```
3. Product Details Page (`pages/products/[id].tsx`):
```typescript
import { GetServerSideProps } from 'next';
import React from 'react';
import ProductDisplay from '../../components/ProductDisplay'; // Import the component
interface ProductDetailsProps {
product: Product | null;
error: string | null;
}
const ProductDetails: React.FC<ProductDetailsProps> = ({ product, error }) => {
if (error) {
return <div>Error: {error}</div>;
}
if (!product) {
return <div>Loading...</div>;
}
return (
<div>
<ProductDisplay product={product} /> {/* Use the component */}
</div>
);
};
export const getServerSideProps: GetServerSideProps<ProductDetailsProps> = async (context) => {
const { id } = context.params;
try {
const res = await fetch(`https://fakestoreapi.com/products/${id}`); // Replace with your API
if (!res.ok) {
throw new Error(`HTTP error! status: ${res.status}`);
}
const product: Product = await res.json();
return {
props: {
product,
error: null,
},
};
} catch (err: any) {
console.error("Error fetching product:", err);
return {
props: {
product: null,
error: err.message,
},
};
}
};
export default ProductDetails;
```
4. Explanation:
*`pages/products/[id].tsx`:** This is our product details page. The `[id]` part makes it a dynamic route (e.g., `/products/1`, `/products/2`, etc.).
*`ProductDisplay` Component:** This reusable component displays the product details. Separating this logic makes the code more organized.
*`getServerSideProps`:**
Fetches product data from the API on the server*.
* Handles errors during the fetch.
* Passes the fetched data as props to the `ProductDetails` component.
*`ProductDetails` Component:**
* Receives the `product` and `error` props.
* Renders the `ProductDisplay` component, passing the `product` data. It also handles the loading and error states.
*TypeScript Interfaces:** `Product` and `ProductDetailsProps` provide type safety.
5. How it Works (Client-Server Interaction):
1. User requests `/products/1`.
2. Next.js calls `getServerSideProps` on the server.
3. `getServerSideProps` fetches product data from the API.
4. Next.js renders the `ProductDetails` component (and the nested `ProductDisplay` component) on the server, generating HTML.
5. The server sends the HTML to the client's browser.
6. The browser displays the HTML immediately (fast FCP).
7. React hydrates the HTML on the client, making it interactive. If the user navigates to another product page, the process repeats from step 2.
6. Running the Application:
```bash
npm run dev
```
Visit `http://localhost:3000/products/1` (or other product IDs).
Key Improvements:
*Reusable Component:** The `ProductDisplay` component makes the code more modular and maintainable.
*Clearer Data Flow:** Data fetching is separated from data display.
*Complete Example:** Shows the full interaction between server-side and client-side code.
*Organized File Structure:** Follows best practices for organizing Next.js projects.
This complete example provides a solid foundation for building more complex SSR applications in Next.js with React and TypeScript. Remember to replace the example API URL with your actual API endpoint. You can extend this example by adding more features like error handling, loading indicators, and styling.
Commentaires