Table of Contents
- What is Next.js?
- Setting Up a Next.js Project
- Understanding Next.js File-Based Routing
- Server-Side Rendering (SSR) in Next.js
- SSR vs. Other Rendering Methods in Next.js
- Deploying a Next.js SSR Application
- Troubleshooting Common SSR Issues
- Best Practices for Next.js SSR
- Conclusion
- References
What is Next.js?
Next.js is a React framework created by Vercel that simplifies building production-ready React applications. It abstracts complex configurations (like Webpack, Babel, and server setup) and provides built-in features to optimize performance, scalability, and developer experience.
Key features relevant to SSR:
- File-based routing: Pages are defined by files in the
pagesdirectory, eliminating the need for manual route configuration. - Built-in SSR: Use
getServerSidePropsto fetch data and render pages on the server for every request. - API routes: Create serverless backend endpoints directly in your Next.js app (under
pages/api). - Automatic code splitting: Reduces bundle sizes by splitting code per page.
- Image and font optimization: Built-in components (
next/image,next/font) to optimize media performance.
Setting Up a Next.js Project
To start using Next.js, ensure you have Node.js (v18.17 or later) installed. Follow these steps:
Step 1: Create a New Next.js App
Run the following command in your terminal to create a new project using create-next-app (Next.js’s official starter tool):
npx create-next-app@latest my-ssr-app
Step 2: Configure the Project
During setup, you’ll be prompted to configure options like TypeScript, ESLint, Tailwind CSS, etc. For simplicity, select:
- Would you like to use TypeScript? No (or Yes if you prefer TypeScript)
- Would you like to use ESLint? Yes
- Would you like to use Tailwind CSS? No (we’ll use basic CSS for this example)
- Would you like to use
src/directory? Yes - Would you like to use App Router? No (we’ll use the traditional Pages Router for SSR; App Router uses Server Components, which are beyond this guide)
- Would you like to customize the default import alias? No
Step 3: Run the Development Server
Navigate to your project directory and start the development server:
cd my-ssr-app
npm run dev
Visit http://localhost:3000 in your browser. You’ll see the default Next.js welcome page, confirming your setup is successful.
Understanding Next.js File-Based Routing
Next.js uses a file-based routing system where the structure of the src/pages directory directly maps to your app’s routes.
src/pages/index.js→ Route:/(homepage)src/pages/about.js→ Route:/aboutsrc/pages/posts/[id].js→ Dynamic route:/posts/1,/posts/2, etc. (where[id]is a parameter)
This eliminates the need for a separate routing library (e.g., React Router) and makes adding new pages as simple as creating a new file.
Server-Side Rendering (SSR) in Next.js
In Next.js, SSR is enabled by exporting an async function called getServerSideProps from a page component. This function runs on the server for every incoming request, fetches data, and passes it as props to the page component, which is then rendered to HTML and sent to the client.
How getServerSideProps Works
getServerSideProps is a special Next.js function that:
- Runs server-side only (never in the browser).
- Accepts a
contextobject containing request-specific data (e.g., URL parameters, HTTP headers, cookies). - Must return an object with a
propskey (the data to pass to the page component).
Syntax:
// src/pages/example.js
export default function ExamplePage(props) {
return <div>{props.data}</div>;
}
export async function getServerSideProps(context) {
// Fetch data from an API, database, etc.
const res = await fetch("https://api.example.com/data");
const data = await res.json();
// Pass data to the page via props
return {
props: { data }, // will be passed to the page component as props
};
}
Key Notes:
getServerSidePropsruns on every request, making it ideal for dynamic content that changes frequently (e.g., user-specific data, real-time metrics).- The
contextobject includes:params: For dynamic routes (e.g.,context.params.idfor/posts/[id]).req: The HTTP request object (Node.jsIncomingMessage).res: The HTTP response object (Node.jsServerResponse).query: An object containing query string parameters (e.g.,?page=1→context.query.page).
Practical Example: Dynamic SSR Page
Let’s build a dynamic page that fetches and displays a blog post by ID using SSR. We’ll use the JSONPlaceholder API (a free fake API for testing) to fetch post data.
Step 1: Create a Dynamic Route File
Create a file at src/pages/posts/[id].js. This will handle routes like /posts/1, /posts/2, etc.
Step 2: Add getServerSideProps to Fetch Data
In src/pages/posts/[id].js, add the following code:
// src/pages/posts/[id].js
export default function PostPage({ post }) {
if (!post) {
return <div>Loading...</div>;
}
return (
<div style={{ maxWidth: "800px", margin: "0 auto", padding: "2rem" }}>
<h1 style={{ fontSize: "2rem", marginBottom: "1rem" }}>{post.title}</h1>
<p style={{ fontSize: "1.2rem", lineHeight: "1.6" }}>{post.body}</p>
</div>
);
}
export async function getServerSideProps(context) {
// Extract the "id" parameter from the URL
const { id } = context.params;
try {
// Fetch post data from JSONPlaceholder API
const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`);
const post = await res.json();
// If the post doesn't exist (e.g., invalid ID), return 404
if (!post.id) {
return { notFound: true };
}
// Pass the post data to the page component
return {
props: { post },
};
} catch (error) {
// Handle errors (e.g., network issues)
return { notFound: true };
}
}
Step 3: Test the Dynamic Route
Visit http://localhost:3000/posts/1 in your browser. You’ll see the title and body of post 1 from the JSONPlaceholder API. Try changing the ID (e.g., /posts/42) to see different posts.
Why This Works:
getServerSidePropsruns on the server for every request to/posts/[id].- It extracts the
idparameter fromcontext.params, fetches the corresponding post, and passes it to thePostPagecomponent asprops.post. - If the post isn’t found (e.g.,
/posts/9999), Next.js returns a 404 page.
SSR vs. Other Rendering Methods in Next.js
Next.js offers multiple rendering strategies. Here’s how SSR compares to others:
| Method | How It Works | Use Case | Performance |
|---|---|---|---|
| Server-Side Rendering (SSR) | Renders on the server for every request. | Dynamic content (e.g., user dashboards, real-time data). | Slower than SSG (server load per request), but fresh data. |
| Static Site Generation (SSG) | Renders at build time; reuses cached HTML. | Static content (e.g., blogs, marketing pages). | Fastest (served from CDN), but stale data unless rebuilt. |
| Incremental Static Regeneration (ISR) | SSG + re-renders in the background after a timeout. | Static content that needs occasional updates (e.g., product pages). | Fast (CDN-cached) with fresh data after regeneration. |
| Client-Side Rendering (CSR) | Renders in the browser after JS loads. | Highly interactive apps (e.g., dashboards with client-side state). | Poor initial load (blank screen until JS runs). |
When to Use SSR:
- You need data that changes with every request (e.g., user-specific content like a personalized feed).
- SEO is critical, and content can’t be pre-rendered at build time (e.g., real-time stock prices).
Deploying a Next.js SSR Application
Next.js SSR apps require a server (or serverless functions) to render pages on request. The easiest way to deploy is with Vercel (Next.js’s creator), but other options include Netlify, AWS, or a custom Node.js server.
Deploying to Vercel
Vercel optimizes Next.js apps out of the box, with built-in support for SSR, API routes, and global CDN.
Step 1: Push Your Code to GitHub
Commit your project to a GitHub repository:
git init
git add .
git commit -m "Initial commit: SSR post page"
git branch -M main
git remote add origin https://github.com/your-username/my-ssr-app.git
git push -u origin main
Step 2: Connect to Vercel
- Go to Vercel and sign up with your GitHub account.
- Click New Project and import your
my-ssr-apprepository. - Vercel automatically detects Next.js and configures the build settings. Click Deploy.
Within minutes, your app will be live. Vercel provides a URL (e.g., my-ssr-app.vercel.app). Visit https://your-vercel-url/posts/1 to test your SSR page.
Troubleshooting Common SSR Issues
1. getServerSideProps Isn’t Running
- Cause: The page is being statically optimized (Next.js may statically render pages with no dynamic data).
- Fix: Ensure
getServerSidePropsis exported correctly (async function, named export). If using dynamic imports, mark the page as dynamic withssr: true.
2. Slow Page Loads with SSR
- Cause: SSR renders on every request, which can slow down if data fetching is unoptimized.
- Fix:
- Fetch only necessary data (avoid over-fetching).
- Use caching (e.g., Redis) for frequently accessed data.
- Consider ISR if data doesn’t need to be real-time.
3. “Window is Not Defined” Errors
- Cause: Code that references browser APIs (e.g.,
window,document) runs on the server, where these APIs don’t exist. - Fix: Use
typeof window !== 'undefined'to guard browser-specific code, or move it touseEffect.
Best Practices for Next.js SSR
1. Use SSR Sparingly
Only use SSR for pages that require dynamic, per-request data. For static content, prefer SSG or ISR to reduce server load and improve performance.
2. Optimize Data Fetching
- Fetch data as close to the source as possible (e.g., use a database directly instead of an external API if possible).
- Parallelize data requests (e.g.,
Promise.allfor multiple independent API calls).
3. Cache Aggressively
- Cache API responses on the server (e.g., with
fetch’scache: 'force-cache'or a tool like Redis). - Use client-side caching (e.g., SWR or React Query) for data that updates after the initial render.
4. Handle Errors Gracefully
- In
getServerSideProps, return{ notFound: true }for 404s or{ redirect: { destination: '/error' } }for redirects. - Use try/catch blocks to handle failed API calls and return fallback data.
Conclusion
Next.js simplifies Server-Side Rendering for React applications, allowing you to build fast, SEO-friendly apps with dynamic content. By leveraging getServerSideProps, you can fetch data on the server for every request and render fully populated pages, improving performance and user experience.
Key takeaways:
- Next.js’s file-based routing eliminates manual route configuration.
getServerSidePropsenables SSR by fetching data and passing it to page components.- Use SSR for dynamic, per-request content; prefer SSG/ISR for static or semi-static content.
- Deploy SSR apps easily with Vercel for optimal performance.