How to Use Getserversideprops
How to Use GetServerSideProps When building modern web applications with React and Next.js, performance, SEO, and user experience are non-negotiable. One of the most powerful tools in the Next.js arsenal for achieving these goals is getServerSideProps . Unlike static generation methods that pre-render pages at build time, getServerSideProps enables server-side rendering (SSR) for every request, en
How to Use GetServerSideProps
When building modern web applications with React and Next.js, performance, SEO, and user experience are non-negotiable. One of the most powerful tools in the Next.js arsenal for achieving these goals is getServerSideProps. Unlike static generation methods that pre-render pages at build time, getServerSideProps enables server-side rendering (SSR) for every request, ensuring that your content is dynamically generated on the server before being sent to the client. This makes it ideal for pages that require real-time data, personalized user content, or frequent updates—such as dashboards, e-commerce product pages, or authenticated user profiles.
In this comprehensive guide, you’ll learn exactly how to use getServerSideProps effectively—from basic implementation to advanced optimization techniques. Whether you’re new to Next.js or looking to refine your SSR strategy, this tutorial will equip you with the knowledge to build faster, more SEO-friendly, and highly responsive web applications.
Step-by-Step Guide
Understanding the Role of getServerSideProps
getServerSideProps is an async function exported from a page component in Next.js. It runs on the server for every incoming request, allowing you to fetch data from APIs, databases, or other external sources and pass it as props to your page. Unlike getStaticProps, which runs at build time, getServerSideProps executes on every request, making it perfect for content that changes frequently or depends on user-specific data like authentication tokens, cookies, or query parameters.
When getServerSideProps is used, Next.js renders the page on the server with the fetched data, then sends the fully rendered HTML to the client. This results in faster perceived load times and better SEO because search engine crawlers receive fully populated content immediately—without waiting for JavaScript to execute.
Setting Up Your Next.js Project
Before you begin using getServerSideProps, ensure you have a Next.js project set up. If you haven’t created one yet, open your terminal and run:
npx create-next-app@latest my-ssr-app
cd my-ssr-app
npm run dev
This creates a new Next.js project with default configurations. The project structure will include a pages directory (in Next.js 13 and earlier) or an app directory (in Next.js 13+ with App Router). Note that getServerSideProps is only supported in the pages directory. If you’re using the App Router, you’ll need to use Server Components or the async function with fetch directly inside components instead.
For this tutorial, we’ll work within the pages directory. If you’re using the App Router, consider migrating to the pages directory temporarily for learning purposes, or refer to the equivalent patterns in Server Components later.
Creating Your First Page with getServerSideProps
Let’s create a simple page that fetches the current time from an external API. Inside your pages folder, create a file named currentTime.js:
import React from 'react';
export async function getServerSideProps() {
const res = await fetch('https://worldtimeapi.org/api/ip');
const data = await res.json();
return {
props: {
currentTime: data.datetime,
timezone: data.timezone,
},
};
}
export default function CurrentTime({ currentTime, timezone }) {
return (
<div>
<h2>Current Time</h2>
<p>Time: {currentTime}</p>
<p>Timezone: {timezone}</p>
</div>
);
}
When you visit http://localhost:3000/currentTime, Next.js will execute getServerSideProps on the server, fetch the current time from the API, and pass the data as props to the component. The page will render with the real-time data immediately upon load.
Working with Query Parameters
One of the most common use cases for getServerSideProps is handling dynamic URL parameters. For example, imagine you’re building a blog where each post has a unique ID. You can access query parameters using the context object passed to getServerSideProps.
Create a file named post/[id].js inside the pages directory:
import React from 'react';
export async function getServerSideProps(context) {
const { id } = context.params;
const res = await fetch(https://jsonplaceholder.typicode.com/posts/${id});
const post = await res.json();
if (!post.id) {
return {
notFound: true,
};
}
return {
props: {
post,
},
};
}
export default function Post({ post }) {
return (
<div>
<h1>{post.title}</h1>
<p>{post.body}</p>
</div>
);
}
Now, visiting http://localhost:3000/post/1 will load the post with ID 1. If the post doesn’t exist, getServerSideProps returns { notFound: true }, and Next.js automatically renders a 404 page.
Handling Authentication and Cookies
Another powerful use case is server-side authentication. You can access cookies, headers, and the request object via the context.req property.
Let’s create a protected dashboard page:
import React from 'react';
export async function getServerSideProps(context) {
const { req } = context;
const token = req.cookies.authToken;
if (!token) {
return {
redirect: {
destination: '/login',
permanent: false,
},
};
}
const res = await fetch('https://api.example.com/user', {
headers: {
Authorization: Bearer ${token},
},
});
if (!res.ok) {
return {
redirect: {
destination: '/login',
permanent: false,
},
};
}
const user = await res.json();
return {
props: {
user,
},
};
}
export default function Dashboard({ user }) {
return (
<div>
<h1>Welcome, {user.name}</h1>
<p>Email: {user.email}</p>
</div>
);
}
In this example, the server checks for an authentication token in the cookies. If it’s missing or invalid, the user is redirected to the login page. Otherwise, the user’s profile data is fetched and rendered.
Using Environment Variables
For security and flexibility, always use environment variables to store API keys and base URLs. Create a .env.local file in your project root:
NEXT_PUBLIC_API_URL=https://api.example.com
API_SECRET_KEY=your-secret-key-here
Then use them in getServerSideProps:
export async function getServerSideProps() {
const res = await fetch(${process.env.NEXT_PUBLIC_API_URL}/products, {
headers: {
'Authorization': Bearer ${process.env.API_SECRET_KEY},
},
});
const products = await res.json();
return {
props: {
products,
},
};
}
Note: Only variables prefixed with NEXT_PUBLIC_ are exposed to the browser. Secrets like API keys should NOT be prefixed and remain server-side only.
Fetching Multiple Data Sources
Often, you’ll need to fetch data from multiple APIs or databases. You can use Promise.all to parallelize requests and reduce overall load time:
export async function getServerSideProps() {
const [userRes, ordersRes, productsRes] = await Promise.all([
fetch('https://api.example.com/user'),
fetch('https://api.example.com/orders'),
fetch('https://api.example.com/products'),
]);
const [user, orders, products] = await Promise.all([
userRes.json(),
ordersRes.json(),
productsRes.json(),
]);
return {
props: {
user,
orders,
products,
},
};
}
This approach ensures that all data is fetched concurrently, improving performance compared to sequential fetching.
Handling Errors Gracefully
Network failures, API downtime, or malformed responses can break your page. Always wrap your fetch calls in try-catch blocks:
export async function getServerSideProps() {
try {
const res = await fetch('https://api.example.com/data');
if (!res.ok) {
throw new Error(HTTP error! status: ${res.status});
}
const data = await res.json();
return { props: { data } };
} catch (error) {
console.error('Failed to fetch data:', error);
return {
props: {
data: null,
error: 'Failed to load data. Please try again later.',
},
};
}
}
export default function DataPage({ data, error }) {
if (error) {
return <p>{error}</p>;
}
if (!data) {
return <p>Loading...</p>;
}
return (
<div>
<h1>Data Loaded Successfully</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
This ensures your application remains stable even when external services fail.
Best Practices
Minimize Server-Side Data Fetching
While getServerSideProps is powerful, overusing it can lead to slower page loads and increased server load. Only use it when you need fresh, user-specific, or frequently changing data. For static content like blog posts or product categories that don’t change often, prefer getStaticProps with revalidation (revalidate option) to reduce server strain and improve scalability.
Optimize Fetch Requests
Use caching headers, CDN layers, and request deduplication where possible. If you’re fetching from a third-party API, consider using a caching layer like Redis or implementing client-side caching with SWR or React Query for subsequent interactions.
Also, avoid making unnecessary requests. For example, if you’re fetching user data and their orders, check whether the orders can be derived from the user object (e.g., via a nested relationship) to reduce the number of API calls.
Use Proper HTTP Status Codes
When data is not found, return { notFound: true } to trigger Next.js’s built-in 404 page. When authentication fails, use { redirect: { destination: '/login', permanent: false } } to guide users appropriately. Avoid returning 500 errors unless there’s a true server-side failure—handle user-facing errors gracefully with fallback UIs.
Avoid Blocking the Server
Never perform heavy computations or synchronous operations inside getServerSideProps. All operations must be asynchronous. Avoid using fs.readFileSync or blocking loops. If you need to process large datasets, offload that to a background job or use a microservice.
Test with Realistic Data
During development, use mock data or tools like MSW (Mock Service Worker) to simulate API responses. This allows you to test edge cases—like slow networks, missing fields, or invalid tokens—without relying on external services.
Monitor Performance
Use Next.js’s built-in performance monitoring tools. Enable the next telemetry and use tools like Lighthouse, Web Vitals, and New Relic to track server response times. Aim for server-side rendering times under 200ms. If your SSR time exceeds 500ms, investigate bottlenecks in your data fetching logic or consider caching strategies.
Secure Your Endpoints
Never expose sensitive environment variables or API keys in client-side code. Always keep secrets in .env.local and never prefix them with NEXT_PUBLIC_. Also, validate and sanitize all inputs from context.params and context.query to prevent injection attacks.
Use TypeScript for Type Safety
If you’re using TypeScript, define interfaces for your props to catch errors early:
interface User {
id: number;
name: string;
email: string;
}
export async function getServerSideProps(): Promise<{
props: {
user: User;
};
}> {
const res = await fetch('https://api.example.com/user');
const user: User = await res.json();
return { props: { user } };
}
This improves code maintainability and reduces runtime errors.
Tools and Resources
Next.js Documentation
The official Next.js documentation is the most authoritative source for understanding getServerSideProps. Visit https://nextjs.org/docs/pages/building-your-application/data-fetching/get-server-side-props for detailed API references, examples, and updates.
MSW (Mock Service Worker)
MSW allows you to mock API responses in development and testing environments without changing your code. It’s invaluable for simulating slow APIs, errors, or edge cases. Install it via:
npm install msw
Then create a worker file to intercept fetch calls and return mock data.
Postman and Insomnia
Use these tools to test your backend endpoints independently before integrating them into getServerSideProps. This helps isolate whether issues are in your API or your SSR logic.
Netlify and Vercel
Both platforms offer excellent support for Next.js SSR. Vercel, in particular, is optimized for Next.js and provides built-in analytics, edge functions, and automatic scaling. Deploy your app to Vercel with a single command:
npm install -g vercel
vercel
React Query and SWR
While getServerSideProps handles initial data fetching, client-side data needs (like real-time updates or user interactions) are better handled by libraries like SWR or React Query. Use them to refetch data after the page loads, without re-rendering the entire page.
Chrome DevTools and Lighthouse
Use the Network tab in Chrome DevTools to inspect the timing of your server-side requests. Look for long TTFB (Time to First Byte) values—these indicate slow server responses. Run Lighthouse audits to check SEO scores, performance, and accessibility. Aim for scores above 90.
LogRocket and Sentry
For production monitoring, integrate LogRocket or Sentry to capture client-side errors, server-side exceptions, and user sessions. This helps you detect issues with getServerSideProps that may not be visible during development.
Real Examples
Example 1: E-Commerce Product Page
Consider an online store where each product page must display real-time inventory, pricing, and customer reviews. Here’s how you’d structure it:
import React from 'react';
export async function getServerSideProps(context) {
const { id } = context.params;
const [product, inventory, reviews] = await Promise.all([
fetch(https://api.store.com/products/${id}),
fetch(https://api.store.com/inventory/${id}),
fetch(https://api.store.com/reviews?productId=${id}),
]);
const productData = await product.json();
const inventoryData = await inventory.json();
const reviewsData = await reviews.json();
if (!productData.id) {
return { notFound: true };
}
return {
props: {
product: productData,
inventory: inventoryData.available,
reviews: reviewsData,
},
};
}
export default function ProductPage({ product, inventory, reviews }) {
return (
<div>
<h1>{product.name}</h1>
<p>${product.price}</p>
<p>Stock: {inventory > 0 ? 'In stock' : 'Out of stock'}</p>
<h3>Reviews</h3>
<ul>
{reviews.map(review => (
<li key={review.id}>{review.comment}</li>
))}
</ul>
</div>
);
}
This page ensures that every visitor sees accurate, up-to-date information—critical for conversion rates and trust.
Example 2: User Dashboard with Role-Based Content
Many applications serve different content based on user roles. Here’s how to handle it:
export async function getServerSideProps(context) {
const { req } = context;
const token = req.cookies.token;
if (!token) {
return { redirect: { destination: '/login', permanent: false } };
}
const userRes = await fetch('https://api.example.com/me', {
headers: { Authorization: Bearer ${token} },
});
if (!userRes.ok) {
return { redirect: { destination: '/login', permanent: false } };
}
const user = await userRes.json();
const roles = user.roles || [];
let dashboardData = {};
if (roles.includes('admin')) {
dashboardData = await fetch('https://api.example.com/admin/stats').then(r => r.json());
} else if (roles.includes('editor')) {
dashboardData = await fetch('https://api.example.com/editor/stats').then(r => r.json());
} else {
dashboardData = await fetch('https://api.example.com/user/stats').then(r => r.json());
}
return {
props: {
user,
dashboardData,
},
};
}
export default function Dashboard({ user, dashboardData }) {
return (
<div>
<h1>Welcome, {user.name}</h1>
<pre>{JSON.stringify(dashboardData, null, 2)}</pre>
</div>
);
}
This approach ensures role-specific content is rendered server-side, preventing client-side logic from exposing hidden data.
Example 3: Internationalized Content
For multilingual sites, use getServerSideProps to serve content based on the user’s locale:
export async function getServerSideProps(context) {
const { locale } = context;
const res = await fetch(https://api.example.com/content?lang=${locale});
const content = await res.json();
return {
props: {
content,
locale,
},
};
}
export default function HomePage({ content, locale }) {
return (
<div>
<h1>{content.title}</h1>
<p>{content.body}</p>
</div>
);
}
export function getStaticPaths() {
return {
paths: ['en', 'es', 'fr'].map(lang => ({ params: { lang } })),
fallback: 'blocking',
};
}
Combine this with Next.js’s built-in internationalized routing for a fully localized experience.
FAQs
What is the difference between getServerSideProps and getStaticProps?
getServerSideProps runs on every request and renders the page dynamically on the server. It’s ideal for data that changes frequently or is user-specific. getStaticProps runs at build time and generates static HTML files. It’s best for content that doesn’t change often, like blog posts or marketing pages. Use getStaticProps with revalidate for near-real-time updates without full SSR overhead.
Can I use getServerSideProps in the App Router?
No. getServerSideProps is only available in the pages directory. In the App Router (Next.js 13+), you should use Server Components with direct fetch() calls inside components. Server Components automatically render on the server and support async/await natively.
Does getServerSideProps affect SEO?
Yes, positively. Since the page is rendered on the server with full content, search engines receive HTML with all text, links, and metadata immediately. This improves crawlability and indexing compared to client-side rendered pages that rely on JavaScript to populate content.
Can I use getServerSideProps with API routes?
Not directly. getServerSideProps is used in page components, not API routes. However, you can fetch data from your own API routes inside getServerSideProps—just treat them like any other external endpoint.
Why is my getServerSideProps slow?
Slow SSR is usually caused by inefficient data fetching: multiple sequential API calls, unoptimized database queries, or slow third-party services. Use Promise.all to parallelize requests, implement caching (Redis, CDN), or consider pre-fetching data with background jobs.
Can I use getServerSideProps with authentication providers like NextAuth?
Yes. NextAuth provides the getServerSideProps helper function auth to easily access session data:
import { auth } from '@/auth';
export async function getServerSideProps(context) {
const session = await auth(context);
if (!session) {
return { redirect: { destination: '/login', permanent: false } };
}
return { props: { session } };
}
Does getServerSideProps work with middleware?
Yes. Middleware runs before getServerSideProps, so you can modify requests, set cookies, or redirect users before data fetching begins. This is useful for language detection, A/B testing, or request logging.
Is getServerSideProps expensive to run?
It can be. Since it runs on every request, it consumes more server resources than static generation. For high-traffic sites, combine it with caching (e.g., Redis), edge computing, or use it only for critical pages. Consider hybrid approaches: use static generation for most pages and SSR only for personalized or real-time content.
Conclusion
getServerSideProps is a cornerstone of modern Next.js development, enabling dynamic, SEO-friendly, and user-specific content delivery with server-side rendering. By fetching data on every request, it ensures your users always see the most accurate and relevant information—whether it’s real-time inventory, personalized dashboards, or authenticated content.
Throughout this guide, we’ve covered everything from basic implementation to advanced patterns like authentication, error handling, and performance optimization. You now understand when and how to use getServerSideProps effectively, and how to avoid common pitfalls that can degrade performance or compromise security.
Remember: SSR is powerful, but not always necessary. Use it wisely—only where real-time or personalized data is critical. For static content, lean on getStaticProps and incremental static regeneration. Combine both approaches to build fast, scalable, and user-centric applications.
As you continue developing with Next.js, keep experimenting with data-fetching strategies, monitor performance metrics, and stay updated with Next.js releases. The ecosystem evolves rapidly, and mastering getServerSideProps is just the beginning of building truly exceptional web experiences.