How to Use Getserversideprops
How to Use GetServerSideProps When building modern web applications with Next.js, one of the most powerful tools at your disposal is GetServerSideProps . This function enables server-side rendering (SSR) for your pages, allowing you to fetch data on the server at request time and pass it as props to your React component. Unlike static generation (getStaticProps), GetServerSideProps runs on every r
How to Use GetServerSideProps
When building modern web applications with Next.js, one of the most powerful tools at your disposal is GetServerSideProps. This function enables server-side rendering (SSR) for your pages, allowing you to fetch data on the server at request time and pass it as props to your React component. Unlike static generation (getStaticProps), GetServerSideProps runs on every request, making it ideal for dynamic content that changes frequentlysuch as user-specific data, real-time analytics, or personalized feeds.
Understanding and correctly implementing GetServerSideProps is essential for developers aiming to deliver high-performance, SEO-friendly applications. Search engines prefer content that is rendered server-side because it ensures crawlers receive fully populated HTML rather than empty shells waiting for JavaScript to execute. This leads to better indexing, faster perceived load times, and improved user experienceespecially on low-end devices or slow networks.
In this comprehensive guide, youll learn exactly how to use GetServerSidePropsfrom basic syntax to advanced patterns, best practices, real-world examples, and common pitfalls to avoid. Whether youre new to Next.js or looking to deepen your SSR knowledge, this tutorial will equip you with everything you need to implement GetServerSideProps effectively in your projects.
Step-by-Step Guide
Understanding the Basic Syntax
GetServerSideProps is an exported async function that you define inside a page component in your Next.js application. It must be exported from the same file as the default React component. The function receives a context object containing information about the incoming request, such as query parameters, cookies, headers, and the URL pathname.
Heres the minimal structure:
export async function getServerSideProps(context) {
// Fetch data from external API
const res = await fetch('https://api.example.com/data')
const data = await res.json()
// Pass data to the page via props
return {
props: { data },
}
}
export default function Page({ data }) {
return <div>{JSON.stringify(data)}</div>
}
The function must return an object with a props key. This object contains the data that will be passed as props to your component. If an error occurs during data fetching, you can return an object with redirect or notFound keys to handle navigation or 404 scenarios.
Setting Up a Next.js Project
If you havent already created a Next.js project, begin by initializing one using the official CLI:
npx create-next-app@latest my-ssr-app
cd my-ssr-app
npm run dev
This creates a new Next.js application with default configurations. Inside the pages directory, create a new file called index.js (or any name you prefer). This will be your entry point for implementing GetServerSideProps.
Fetching External API Data
One of the most common use cases for GetServerSideProps is fetching data from an external API. For example, lets say you want to display a list of recent blog posts from a headless CMS like Strapi or Contentful.
Heres how to do it:
export async function getServerSideProps() {
const res = await fetch('https://jsonplaceholder.typicode.com/posts')
const posts = await res.json()
return {
props: { posts },
}
}
export default function Blog({ posts }) {
return (
<div>
<h1>Latest Blog Posts</h1>
<ul>
{posts.map(post => (
<li key={post.id}>
<h3>{post.title}</h3>
<p>{post.body}</p>
</li>
))}
</ul>
</div>
)
}
When you visit the page, Next.js will execute the getServerSideProps function on the server before rendering the page. The resulting HTML includes the full content of the blog posts, which is immediately visible to users and search engines.
Handling Query Parameters
GetServerSideProps is especially useful for dynamic routes that rely on URL parameters. For instance, if you have a blog with individual post pages like /posts/1, /posts/2, etc., you can extract the ID from the URL and fetch the corresponding data.
First, create a dynamic route file: pages/posts/[id].js.
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 the post doesn't exist, return a 404
if (!post.id) {
return {
notFound: true,
}
}
return {
props: { post },
}
}
export default function PostPage({ post }) {
return (
<div>
<h1>{post.title}</h1>
<p>{post.body}</p>
</div>
)
}
Now, visiting /posts/1 will load the post with ID 1. If the ID doesnt exist, Next.js will automatically render a 404 page.
Working with Cookies and Headers
GetServerSideProps has full access to HTTP headers and cookies from the incoming request. This makes it perfect for authentication workflows where you need to verify a users session before rendering content.
Example: Fetching a user profile based on an authentication token stored in a cookie:
export async function getServerSideProps(context) {
const { req } = context
const token = req.headers.cookie?.split('; ').find(row => row.startsWith('authToken='))?.split('=')[1]
if (!token) {
return {
redirect: {
destination: '/login',
permanent: false,
},
}
}
const res = await fetch('https://api.example.com/user/profile', {
headers: {
Authorization: Bearer ${token},
},
})
const user = await res.json()
if (!user.id) {
return {
redirect: {
destination: '/login',
permanent: false,
},
}
}
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 authToken cookie. If its missing or invalid, the user is redirected to the login page. Otherwise, their profile data is fetched and rendered.
Handling Errors Gracefully
Network requests can fail. Its crucial to handle these cases to prevent your application from crashing or displaying broken UIs.
Use try-catch blocks to wrap your fetch calls:
export async function getServerSideProps(context) {
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: {
error: 'Failed to load data. Please try again later.',
},
}
}
}
export default function DataPage({ data, error }) {
if (error) {
return <div>{error}</div>
}
return <div>{JSON.stringify(data)}</div>
}
This ensures your page remains usable even when external services are down. You can also enhance this by showing a loading state or retry mechanism on the client side using React hooks.
Passing Multiple Data Sources
Often, youll need to fetch data from multiple APIs or databases. You can use Promise.all to parallelize requests and reduce total load time.
export async function getServerSideProps() {
const [posts, users, categories] = await Promise.all([
fetch('https://jsonplaceholder.typicode.com/posts').then(r => r.json()),
fetch('https://jsonplaceholder.typicode.com/users').then(r => r.json()),
fetch('https://api.example.com/categories').then(r => r.json()),
])
return {
props: {
posts,
users,
categories,
},
}
}
export default function HomePage({ posts, users, categories }) {
return (
<div>
<h1>Home Page</h1>
<h2>Posts</h2>
{posts.map(post => <p key={post.id}>{post.title}</p>)}
<h2>Users</h2>
{users.map(user => <p key={user.id}>{user.name}</p>)}
<h2>Categories</h2>
{categories.map(cat => <p key={cat.id}>{cat.name}</p>)}
</div>
)
}
Using Promise.all ensures all requests are initiated simultaneously, improving performance compared to sequential fetching.
Best Practices
Minimize Server-Side Data Fetching
While GetServerSideProps is powerful, it comes at a cost: every request requires server-side computation. This can increase latency and put unnecessary load on your server, especially under high traffic.
Only use GetServerSideProps when the data is:
- Highly dynamic (changes per request)
- Personalized to the user (e.g., auth-dependent)
- Required for SEO (e.g., product pages, blog posts)
If data doesnt change frequently, consider using getStaticProps with revalidation (ISR) instead. For example, a product listing that updates once per hour should use ISRnot SSR on every request.
Optimize API Calls
Always optimize your external API calls. Use caching headers, reduce payload size, and avoid unnecessary fields. If youre using a GraphQL API, request only the fields you need.
Example: Instead of fetching all user data, request only whats necessary:
const res = await fetch('https://api.example.com/graphql', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query:
query GetUser($id: ID!) {
user(id: $id) {
name
email
avatar
}
}
,
variables: { id: userId },
}),
})
This reduces bandwidth usage and speeds up response times.
Use Environment Variables for API Keys
Never hardcode API keys, database credentials, or secrets in your server-side code. Use environment variables instead.
Create a .env.local file in your project root:
NEXT_PUBLIC_API_URL=https://api.example.com
API_SECRET_KEY=your-secret-key-here
Access them in GetServerSideProps:
export async function getServerSideProps() {
const res = await fetch(${process.env.NEXT_PUBLIC_API_URL}/data, {
headers: {
Authorization: Bearer ${process.env.API_SECRET_KEY},
},
})
const data = await res.json()
return { props: { data } }
}
Only environment variables prefixed with NEXT_PUBLIC_ are exposed to the browser. Secrets without this prefix remain server-side and are safe from client exposure.
Implement Caching Strategies
Even with server-side rendering, you can still benefit from caching. Use tools like Redis, Memcached, or even simple in-memory caches for frequently requested data.
Example using a basic in-memory cache:
const cache = new Map()
export async function getServerSideProps() {
const cacheKey = 'featured-products'
if (cache.has(cacheKey)) {
console.log('Serving from cache')
return { props: { products: cache.get(cacheKey) } }
}
const res = await fetch('https://api.example.com/products?featured=true')
const products = await res.json()
// Cache for 5 minutes
cache.set(cacheKey, products, Date.now() + 300000)
return { props: { products } }
}
For production, use a distributed cache like Redis so multiple server instances can share cached data.
Avoid Heavy Operations in GetServerSideProps
Do not perform CPU-intensive tasks inside GetServerSideProps, such as image processing, complex calculations, or large file parsing. These operations block the server thread and increase response time.
If you need to process data, do it in a background job or use a separate microservice. Let GetServerSideProps focus on fetching and preparing data for rendering.
Test Server-Side Behavior
Always test your pages with real HTTP requests. Use tools like curl or Postman to simulate requests and verify that:
- Data is rendered in the initial HTML
- Redirects and 404s work as expected
- Authentication headers are respected
You can also use Next.jss built-in testing utilities or integrate with Jest and React Testing Library to write unit tests for your server-side logic.
Monitor Performance Metrics
Use tools like Lighthouse, Web Vitals, or New Relic to monitor your server response times. A slow GetServerSideProps function can significantly impact your Core Web Vitals scoreespecially First Contentful Paint (FCP) and Largest Contentful Paint (LCP).
Target server response times under 500ms. If your API calls are slow, consider implementing fallback UIs or partial hydration strategies.
Tools and Resources
Official Next.js Documentation
The Next.js documentation on GetServerSideProps is the most authoritative source. It includes detailed examples, edge case handling, and updates on new features.
API Testing Tools
- Postman Test API endpoints and simulate headers/cookies
- curl Command-line tool for quick HTTP requests
- Insomnia Open-source alternative to Postman with excellent UI
Mocking APIs for Development
Use MSW (Mock Service Worker) to simulate API responses during development without hitting real endpoints. This improves speed and reliability.
Install MSW:
npm install msw --save-dev
Create a mock handler in mocks/browser.js:
import { rest } from 'msw'
export const handlers = [
rest.get('/api/posts', (req, res, ctx) => {
return res(
ctx.json([
{ id: 1, title: 'Mock Post 1', body: 'This is a mock.' },
])
)
}),
]
Then enable it in your test environment to ensure consistent behavior during development and testing.
Performance Monitoring
- Google Lighthouse Built into Chrome DevTools, analyzes performance, accessibility, and SEO
- Web Vitals Extension Real-time monitoring of Core Web Vitals
- Vercel Analytics If deployed on Vercel, get detailed insights into SSR performance
- New Relic Full-stack observability with server-side tracing
Code Linting and Formatting
Use ESLint and Prettier to maintain clean, consistent code. Install Next.jss recommended ESLint config:
npx next lint
This helps catch common mistakes like forgetting to export getServerSideProps or returning invalid structures.
Deployment Platforms
While GetServerSideProps works on any Node.js server, platforms like Vercel and Netlify offer optimized serverless functions for SSR. Vercel, in particular, provides automatic scaling and edge caching for server-side rendered pages.
Real Examples
Example 1: E-commerce Product Page
On an e-commerce site, product pages must display real-time inventory, pricing, and customer reviews. These values change frequently and are personalized based on user location or loyalty status.
export async function getServerSideProps(context) {
const { id } = context.params
const { req } = context
// Extract user locale from headers
const locale = req.headers['accept-language']?.split(',')[0] || 'en-US'
// Fetch product details
const productRes = await fetch(https://api.shop.com/products/${id})
const product = await productRes.json()
// Fetch inventory
const inventoryRes = await fetch(https://api.shop.com/inventory/${id})
const inventory = await inventoryRes.json()
// Fetch reviews
const reviewsRes = await fetch(https://api.shop.com/reviews?productId=${id})
const reviews = await reviewsRes.json()
// Fetch localized pricing
const priceRes = await fetch(https://api.shop.com/pricing?productId=${id}&locale=${locale})
const price = await priceRes.json()
return {
props: {
product,
inventory,
reviews,
price,
},
}
}
export default function ProductPage({ product, inventory, reviews, price }) {
return (
<div>
<h1>{product.name}</h1>
<p>${price.amount} {price.currency}</p>
<p>In stock: {inventory.quantity}</p>
<h2>Reviews</h2>
{reviews.map(review => (
<div key={review.id}>
<p>{review.comment}</p>
<span>{review.rating}/5</span>
</div>
))}
</div>
)
}
This page is fully rendered on the server, ensuring search engines index accurate product details and users see real-time data immediately.
Example 2: User Dashboard with Authentication
A dashboard that shows personalized analytics, recent activity, and notifications based on the logged-in user.
export async function getServerSideProps(context) {
const { req } = context
const token = req.cookies.authToken
if (!token) {
return {
redirect: {
destination: '/auth/login',
permanent: false,
},
}
}
// Validate token with backend
const userRes = await fetch('https://api.example.com/me', {
headers: { Authorization: Bearer ${token} },
})
if (!userRes.ok) {
return {
redirect: {
destination: '/auth/login',
permanent: false,
},
}
}
const user = await userRes.json()
// Fetch dashboard data
const analyticsRes = await fetch('https://api.example.com/analytics', {
headers: { Authorization: Bearer ${token} },
})
const analytics = await analyticsRes.json()
const notificationsRes = await fetch('https://api.example.com/notifications', {
headers: { Authorization: Bearer ${token} },
})
const notifications = await notificationsRes.json()
return {
props: {
user,
analytics,
notifications,
},
}
}
export default function Dashboard({ user, analytics, notifications }) {
return (
<div>
<h1>Welcome, {user.name}</h1>
<h2>Analytics</h2>
<p>Visitors: {analytics.visitors}</p>
<h2>Notifications</h2>
{notifications.map(n => <p key={n.id}>{n.message}</p>)}
</div>
)
}
This example demonstrates secure, authenticated SSR with proper redirection and error handling.
Example 3: News Site with Real-Time Updates
A news portal that fetches breaking headlines every time a user visits. Since news changes constantly, static generation is unsuitable.
export async function getServerSideProps() {
const res = await fetch('https://newsapi.org/v2/top-headlines?country=us&apiKey=YOUR_KEY')
const data = await res.json()
if (data.status !== 'ok') {
return {
props: {
error: 'Failed to load news. Please try again later.',
},
}
}
return {
props: {
articles: data.articles,
},
}
}
export default function NewsPage({ articles, error }) {
if (error) return <div>{error}</div>
return (
<div>
<h1>Latest News</h1>
{articles.map(article => (
<article key={article.url}>
<h2>{article.title}</h2>
<p>{article.description}</p>
<a href={article.url}>Read more</a>
</article>
))}
</div>
)
}
Each visit fetches the latest headlines, ensuring users always see up-to-date content.
FAQs
What is the difference between GetServerSideProps and GetStaticProps?
GetServerSideProps runs on every request and renders the page dynamically on the server. GetStaticProps runs at build time and generates static HTML files. Use GetServerSideProps for data that changes frequently or is user-specific. Use GetStaticProps for content that remains unchanged between builds.
Can I use GetServerSideProps with API routes?
No. GetServerSideProps is only available in page components, not in API routes. API routes are used to create backend endpoints, while GetServerSideProps is used to fetch data for rendering pages.
Does GetServerSideProps work with static hosting platforms?
Yes, but only if the platform supports server-side rendering. Platforms like Vercel, Netlify (with Serverless Functions), and Render support SSR. Traditional static hosts like GitHub Pages do not.
Can I use GetServerSideProps with TypeScript?
Absolutely. You can strongly type the context and return values for better developer experience:
import { GetServerSideProps } from 'next'
export const getServerSideProps: GetServerSideProps = async (context) => {
const res = await fetch('https://api.example.com/data')
const data: Data[] = await res.json()
return {
props: { data },
}
}
How does GetServerSideProps affect SEO?
It significantly improves SEO because search engine crawlers receive fully rendered HTML with all content included. This avoids the blank page problem common in client-side rendered apps where content loads after JavaScript execution.
Can I use GetServerSideProps in a custom _app.js file?
No. GetServerSideProps can only be used in page components. For global data, consider using context, state management libraries, or fetching data in individual pages.
Is GetServerSideProps slower than static generation?
Yes, because it runs on every request, it adds server-side processing time. However, this trade-off is often worth it for dynamic or personalized content. For non-changing content, prefer static generation with ISR.
How do I handle authentication with GetServerSideProps?
Read authentication tokens from cookies or headers in the request context. Validate them against your backend. If invalid, return a redirect to the login page. Never store sensitive tokens in client-side storage when using SSR.
What happens if the server fails to fetch data?
If you dont handle errors, the page may crash or show a blank screen. Always wrap fetch calls in try-catch blocks and return a fallback props object with an error message or redirect.
Can I use GetServerSideProps with external databases like MongoDB or PostgreSQL?
Yes. You can connect to databases directly from GetServerSideProps. Just ensure you use environment variables for credentials and close connections properly. For production, consider using connection pooling.
Conclusion
GetServerSideProps is a cornerstone of modern Next.js applications that require dynamic, real-time, or user-specific content. By rendering pages on the server for every request, it delivers SEO-optimized, fast-loading, and secure experiences that client-side rendering alone cannot match.
When used correctlywith proper error handling, optimized API calls, and thoughtful cachingit becomes a powerful tool for building high-performance web applications. However, its not a one-size-fits-all solution. Choose GetServerSideProps when you need fresh data per request; otherwise, leverage static generation with revalidation for better scalability.
As you implement GetServerSideProps in your projects, always prioritize performance, security, and maintainability. Test thoroughly, monitor metrics, and iterate based on real user data. The goal is not just to render contentbut to render it well.
Mastering GetServerSideProps puts you ahead of the curve in web development. It bridges the gap between static speed and dynamic flexibilitymaking your applications not just functional, but exceptional.