Skip to main content

Next.js getStaticProps

In modern web development, achieving the perfect balance between performance and dynamic content is crucial. Next.js, a popular React framework, provides powerful data fetching methods to help developers build fast and efficient websites. One of these methods is getStaticProps, which enables you to fetch data at build time and use it to generate static HTML pages.

What is getStaticProps?

getStaticProps is a Next.js function that allows you to fetch data at build time and pass it as props to a page component. This means the data is fetched when you build your application, not when a user requests the page. As a result, pages load faster because they're pre-rendered with the data already in place.

The function runs only on the server-side and never runs in the browser, making it ideal for accessing direct database queries, file system operations, or API calls without exposing sensitive information to the client.

When to Use getStaticProps

getStaticProps is perfect for:

  • Pages that can be pre-rendered ahead of time
  • Data that doesn't change frequently
  • SEO-critical pages where content needs to be indexed by search engines
  • Pages where performance is a priority

Basic Syntax

Here's the basic structure of getStaticProps:

jsx
// pages/example.js
function ExamplePage({ data }) {
return (
<div>
<h1>{data.title}</h1>
<p>{data.content}</p>
</div>
);
}

export async function getStaticProps() {
// Fetch data from an API, database, or file system
const data = { title: 'Hello', content: 'World' };

// Return the data as props
return {
props: {
data,
},
};
}

export default ExamplePage;

Step-by-Step Implementation

Let's walk through creating a page that fetches blog posts from an API at build time:

1. Create a Page Component

First, create a new page in your Next.js project:

jsx
// pages/blog.js
function BlogPage({ posts }) {
return (
<div>
<h1>My Blog</h1>
<ul>
{posts.map((post) => (
<li key={post.id}>
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
</li>
))}
</ul>
</div>
);
}

export default BlogPage;

2. Implement getStaticProps

Now, let's add the getStaticProps function to fetch the blog posts:

jsx
// Still in pages/blog.js
export async function getStaticProps() {
// In a real app, this would be an API call
const res = await fetch('https://jsonplaceholder.typicode.com/posts');
const posts = await res.json();

// Only keep the first 10 posts for this example
const limitedPosts = posts.slice(0, 10).map(post => ({
id: post.id,
title: post.title,
excerpt: post.body.substring(0, 100) + '...'
}));

return {
props: {
posts: limitedPosts,
},
};
}

3. Result

When you build this application, Next.js will:

  1. Execute the getStaticProps function
  2. Fetch the posts from the API
  3. Generate an HTML page with the pre-rendered content
  4. Serve this static HTML to users for fast page loads

Advanced getStaticProps Features

Revalidation with ISR (Incremental Static Regeneration)

One limitation of static generation is that the content is fixed until you rebuild the site. Next.js solves this with Incremental Static Regeneration (ISR):

jsx
export async function getStaticProps() {
const res = await fetch('https://api.example.com/posts');
const posts = await res.json();

return {
props: {
posts,
},
// Re-generate at most once every 10 seconds
revalidate: 10,
};
}

The revalidate property tells Next.js to regenerate the page after the specified number of seconds if a request comes in. This gives you the performance benefits of static pages with the freshness of server-rendered content.

Handling Errors or Redirects

You can also handle errors or redirect users when needed:

jsx
export async function getStaticProps() {
const res = await fetch('https://api.example.com/posts');

// If the request failed, you can redirect or return a 404
if (!res.ok) {
return {
notFound: true, // This will return a 404 page
// OR redirect:
// redirect: {
// destination: '/error',
// permanent: false,
// },
};
}

const posts = await res.json();

return {
props: {
posts,
},
};
}

Context Parameter

getStaticProps receives a context parameter which contains information like params for dynamic routes:

jsx
// pages/posts/[id].js
export async function getStaticProps(context) {
const { id } = context.params;
const res = await fetch(`https://api.example.com/posts/${id}`);
const post = await res.json();

return {
props: {
post,
},
};
}

// You need to define getStaticPaths for dynamic routes
export async function getStaticPaths() {
const res = await fetch('https://api.example.com/posts');
const posts = await res.json();

const paths = posts.map((post) => ({
params: { id: post.id.toString() },
}));

return { paths, fallback: false };
}

Real-world Example: Building a Product Catalog

Let's create a more practical example of a product catalog for an e-commerce website:

jsx
// pages/products.js
function ProductCatalog({ products, lastUpdate }) {
return (
<div className="container">
<h1>Our Products</h1>
<p>Last updated: {new Date(lastUpdate).toLocaleString()}</p>

<div className="product-grid">
{products.map((product) => (
<div key={product.id} className="product-card">
<img
src={product.image}
alt={product.name}
width={200}
height={200}
/>
<h2>{product.name}</h2>
<p className="price">${product.price.toFixed(2)}</p>
<p>{product.description}</p>
<button>Add to Cart</button>
</div>
))}
</div>
</div>
);
}

export async function getStaticProps() {
try {
// In a real app, this would be your actual API or CMS endpoint
const res = await fetch('https://fakestoreapi.com/products');

if (!res.ok) {
throw new Error('Failed to fetch products');
}

const productsData = await res.json();

// Format the data as needed
const products = productsData.map(item => ({
id: item.id,
name: item.title,
price: item.price,
description: item.description.substring(0, 100) + '...',
image: item.image
}));

return {
props: {
products,
lastUpdate: new Date().toISOString(),
},
// Re-generate every hour
revalidate: 3600,
};
} catch (error) {
console.error('Error fetching products:', error);

// Return empty products if there's an error
return {
props: {
products: [],
lastUpdate: new Date().toISOString(),
error: 'Failed to load products'
},
revalidate: 60, // Try again more quickly if there was an error
};
}
}

export default ProductCatalog;

Performance Considerations

When using getStaticProps, keep these tips in mind:

  1. Build time impact: More pages using getStaticProps will increase your build time.
  2. Data size: Be mindful of how much data you're passing as props—large data can increase page weight.
  3. Revalidation frequency: Set reasonable revalidation intervals based on how often your data changes.
  4. API rate limits: Be careful with API calls at build time, especially with many pages.

Summary

getStaticProps is a powerful feature in Next.js that allows you to fetch data at build time and generate static HTML pages with that data. This approach combines the performance benefits of static sites with the dynamic content capabilities normally associated with server rendering.

Key points to remember:

  • It runs only at build time (and during revalidation with ISR)
  • It's ideal for content that doesn't change frequently
  • It improves performance by pre-rendering pages with data
  • It never runs on the client, so you can safely access databases or APIs
  • With revalidate, you can update static content without rebuilding

Exercises

  1. Create a Next.js page that uses getStaticProps to display a list of books from a mock API.
  2. Implement getStaticProps with error handling that redirects to an error page if the API fetch fails.
  3. Build a dynamic route ([id].js) that uses both getStaticPaths and getStaticProps to generate individual pages for each item in a collection.
  4. Implement Incremental Static Regeneration for a news website homepage that updates every 5 minutes.

Additional Resources

Happy coding with Next.js and getStaticProps!



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)