Next.js Lesson 12 – getStaticProps | Dataplexa
LESSON 12

getStaticProps

Build lightning-fast article pages with static site generation that pre-renders content at build time for maximum performance.

Every developer faces the same challenge: how to make websites blazingly fast while still showing dynamic content. NewsWave needs to display thousands of articles, but loading them from a database on every page visit would be slow. Users expect instant loading, especially for content that doesn't change frequently. That's where getStaticProps comes in. Think of it as a chef preparing all the meals before the restaurant opens. Instead of cooking when customers arrive, everything is ready to serve immediately. Unlike React applications that fetch data after components mount, Next.js can fetch data during the build process. Your articles get rendered into HTML files ahead of time. When users visit your site, they receive pre-built pages instantly — no waiting for API calls or loading spinners.

Understanding Static Generation

Static generation means converting your dynamic React components into static HTML files during the build process. Picture a printing press that creates thousands of identical newspapers at once, rather than handwriting each copy when someone wants to read it. When you run npm run build, Next.js calls your getStaticProps functions and generates HTML files for every page. These files sit on your server or CDN, ready to be delivered instantly. No database queries during page visits. No API calls from the browser. Just pure HTML served at lightning speed. This approach works perfectly for content that doesn't change constantly. Blog posts, documentation, product pages, and news articles are ideal candidates. They update occasionally, but most visitors see the same content. Why regenerate the same HTML repeatedly when you can build it once? The performance benefits are enormous. Static files load faster than dynamic pages. Search engines can crawl and index them easily. CDNs can cache them globally. Your server handles less load because it's serving files instead of processing requests.
Static Generation
HTML built once at build time. Lightning fast delivery. Perfect for content that changes infrequently.
Client-Side Rendering
HTML built in browser after loading. Slower initial load. Good for personalized content.

Basic getStaticProps Syntax

The getStaticProps function lives in your page components and runs exclusively on the server during build time. Never in the browser. It fetches data and passes it to your component as props. Here's the fundamental structure every NewsWave article page needs:
// pages/articles/[slug].js - Individual article page
export async function getStaticProps() {
  // This code runs at BUILD TIME, never in browser
  return {
    props: {
      // Data passed to your component
    }
  }
}
The function must be named exactly getStaticProps and must be exported from a page component. Next.js recognizes this function name and calls it during the build process. Your component receives the returned props as if they were passed from a parent component. Let's build a real article page that fetches content before rendering:
// Mock database function - simulates fetching from CMS
const getArticleData = (slug) => {
  // In real apps, this queries your database or CMS
  return {
    title: "Next.js 14 Performance Optimization Guide",
    author: "Sarah Chen",
    publishedAt: "2024-01-15",
    content: "Modern web applications demand exceptional performance...",
    category: "Tech",
    viewCount: 2840
  }
}
export default function ArticlePage({ article }) {
  // This component receives article data as props
  return (
    <div style={{ maxWidth: '800px', margin: '0 auto', padding: '20px' }}>
      <h1>{article.title}</h1>
      <p>By {article.author} • {article.publishedAt}</p>
      <div>{article.content}</div>
      <span>{article.viewCount} views</span>
    </div>
  )
}
localhost:3000/articles/nextjs-performance — NewsWave
What just happened?
The component rendered with pre-fetched data as props. No loading states needed because the content was ready before the page loaded. Try this: Check the page source — you'll see the full HTML content, not empty divs waiting for JavaScript.

Fetching Real Data

Real applications fetch data from APIs, databases, or content management systems. The NewsWave team needs to pull articles from their CMS and generate static pages for each one. Here's how getStaticProps handles external data sources. The function runs in a Node.js environment, so you can use any server-side code. File system access, database queries, API calls to private endpoints — everything works because this code never reaches the browser.
// pages/articles/[slug].js
export async function getStaticProps({ params }) {
  // params.slug contains the dynamic route parameter
  const articleSlug = params.slug // "nextjs-performance" from URL
  
  // Fetch from your CMS API (Strapi, Contentful, etc)
  const response = await fetch(`https://api.newswave.com/articles/${articleSlug}`)
  const article = await response.json()
  
  return {
    props: {
      article // Passed to your component
    }
  }
}
The params object contains dynamic route parameters. For a file named [slug].js, params.slug gives you the actual slug from the URL. This lets you fetch specific content for each page. Here's a more complete example that handles errors and provides fallback content:
export async function getStaticProps({ params }) {
  try {
    // Attempt to fetch article data
    const response = await fetch(`https://api.newswave.com/articles/${params.slug}`)
    
    if (!response.ok) {
      // Article not found or API error
      return {
        notFound: true // Shows 404 page
      }
    }
    
    const article = await response.json()
    
    return {
      props: { article },
      revalidate: 3600 // Revalidate every hour
    }
  } catch (error) {
    console.error('Failed to fetch article:', error)
    return {
      notFound: true
    }
  }
}
Build Process
> next build
Running getStaticProps for /articles/[slug]...
✓ Fetching nextjs-performance
✓ Fetching react-18-features
✓ Fetching typescript-best-practices
✓ Generated 3 static pages in /articles
What just happened?
Next.js called getStaticProps for each article during build time. Failed fetches return 404 pages instead of breaking the build. The revalidate option enables Incremental Static Regeneration. Try this: Deploy your site and watch build logs to see each page being generated.

Working with Dynamic Routes

Dynamic routes like [slug].js create a challenge for static generation. Next.js needs to know which pages to build ahead of time, but your slugs come from dynamic data. How can it generate pages for articles that don't exist yet at build time? This is where getStaticPaths comes in. Think of it as providing a guest list to a party planner. You tell Next.js exactly which pages to pre-render by providing all possible parameter values. The function returns an array of paths to generate. Each path object contains the parameters needed for that page. For NewsWave articles, you'd return all the article slugs from your CMS:
// pages/articles/[slug].js
export async function getStaticPaths() {
  // Fetch all article slugs from your CMS
  const response = await fetch('https://api.newswave.com/articles')
  const articles = await response.json()
  
  // Generate paths for each article
  const paths = articles.map((article) => ({
    params: { slug: article.slug }
  }))
  
  return {
    paths, // Array of pages to pre-render
    fallback: false // Show 404 for unknown paths
  }
}
The fallback property controls what happens when someone visits a path not returned by getStaticPaths. Setting it to false means Next.js shows a 404 page for unknown routes. This works well when you know all possible paths at build time. Here's a complete example showing how both functions work together:
// Static paths generation - runs at build time
export async function getStaticPaths() {
  // Mock function - replace with your CMS query
  const articles = [
    { slug: 'nextjs-14-features' },
    { slug: 'react-server-components' },
    { slug: 'typescript-migration-guide' }
  ]
  
  const paths = articles.map(article => ({
    params: { slug: article.slug }
  }))
  
  return { paths, fallback: false }
}
// Data fetching for each path - runs at build time
export async function getStaticProps({ params }) {
  // Mock article data - replace with CMS fetch
  const articles = {
    'nextjs-14-features': {
      title: 'Next.js 14: Revolutionary New Features',
      author: 'Alex Rodriguez',
      content: 'Next.js 14 introduces groundbreaking improvements...',
      publishedAt: '2024-01-20',
      category: 'Tech'
    }
  }
  
  const article = articles[params.slug]
  
  return {
    props: { article }
  }
}
Static Generation Process
1. getStaticPaths() → returns array of slugs to build
2. getStaticProps() → called once for each slug
3. Component renders → generates static HTML file
✓ Result: Pre-built pages ready to serve instantly
What just happened?
Next.js generated three static HTML files during build time. Each file contains pre-rendered content with no JavaScript required to display the initial content. Users get instant page loads with full SEO benefits. Try this: Build and examine the .next/server/pages directory to see the generated files.

Return Options and Configuration

The object returned by getStaticProps has several powerful options beyond just props. These options control caching, regeneration, and error handling. Understanding them helps you build more robust applications. The revalidate option enables Incremental Static Regeneration (ISR). Instead of rebuilding your entire site when content changes, ISR updates individual pages in the background. It's like having a smart cache that refreshes automatically.
export async function getStaticProps() {
  const article = await fetchArticleData()
  
  return {
    props: { article },
    revalidate: 60 // Regenerate page every 60 seconds if requested
  }
}
The notFound option tells Next.js to show a 404 page instead of rendering your component. This prevents broken pages when data doesn't exist:
export async function getStaticProps({ params }) {
  const article = await fetchArticle(params.slug)
  
  // Article doesn't exist in CMS
  if (!article) {
    return {
      notFound: true // Shows 404 page
    }
  }
  
  return {
    props: { article }
  }
}
The redirect option handles URL changes and content reorganization. Maybe NewsWave moved some articles to new paths:
export async function getStaticProps({ params }) {
  const article = await fetchArticle(params.slug)
  
  // Article moved to new URL structure  
  if (article.redirectTo) {
    return {
      redirect: {
        destination: article.redirectTo,
        permanent: true // HTTP 301 redirect
      }
    }
  }
  
  return {
    props: { article }
  }
}
Here's a comprehensive example showing all return options working together:
export async function getStaticProps({ params, preview = false }) {
  try {
    // Fetch article with preview support
    const article = await fetchArticle(params.slug, { preview })
    
    // Handle missing content
    if (!article) {
      return { notFound: true }
    }
    
    // Handle redirected content
    if (article.status === 'moved') {
      return {
        redirect: {
          destination: `/articles/${article.newSlug}`,
          permanent: true
        }
      }
    }
    
    // Return successful response
    return {
      props: {
        article,
        preview
      },
      revalidate: preview ? 1 : 3600, // Faster revalidation in preview
    }
  } catch (error) {
    console.error('getStaticProps error:', error)
    return { notFound: true }
  }
}
ISR Request Flow
User visits page → Serve cached static page instantly
If revalidate time passed → Trigger background regeneration
New content ready → Next request gets updated page
✓ Users always get fast responses, content stays fresh
What just happened?
The function handles multiple scenarios gracefully. Missing content shows 404 pages instead of errors. Redirects preserve SEO when URLs change. ISR keeps content fresh without slow rebuilds. Try this: Add console.log statements to see when getStaticProps runs during development vs production.

Best Practices and Performance Tips

Successful static site generation requires following proven patterns. The NewsWave team learned these lessons while scaling to thousands of articles. Small mistakes in getStaticProps can cause slow builds or runtime errors. Keep your data fetching focused and efficient. Don't fetch unnecessary data just because you can. Each API call during build time slows down your deployment. Fetch exactly what your component needs for that specific page:
// ❌ Bad: Fetches too much data
export async function getStaticProps({ params }) {
  const allArticles = await fetchAllArticles() // Unnecessary
  const categories = await fetchAllCategories() // Not needed for article page
  const article = allArticles.find(a => a.slug === params.slug)
  
  return { props: { article, categories, allArticles } }
}

// ✅ Good: Fetches only what's needed
export async function getStaticProps({ params }) {
  const article = await fetchArticle(params.slug)
  return { props: { article } }
}
Handle errors gracefully to prevent build failures. One broken API call shouldn't stop your entire site from building. Use try-catch blocks and provide fallbacks:
export async function getStaticProps({ params }) {
  try {
    const article = await fetchArticle(params.slug)
    const relatedArticles = await fetchRelatedArticles(article.categoryId)
      .catch(() => []) // Fallback to empty array if related articles fail
    
    return {
      props: {
        article,
        relatedArticles
      }
    }
  } catch (error) {
    console.error(`Failed to build article ${params.slug}:`, error)
    return { notFound: true }
  }
}
Optimize your revalidate intervals based on content update frequency. News articles might need hourly updates, while documentation pages can revalidate daily:
export async function getStaticProps({ params }) {
  const article = await fetchArticle(params.slug)
  
  // Different revalidation based on content type
  const revalidateInterval = {
    'breaking-news': 300,    // 5 minutes
    'tech': 3600,           // 1 hour  
    'documentation': 86400, // 24 hours
    'tutorial': 604800      // 1 week
  }
  
  return {
    props: { article },
    revalidate: revalidateInterval[article.category] || 3600
  }
}
Here's a production-ready template that combines all best practices:
// Production-ready getStaticProps for NewsWave
export async function getStaticProps({ params, preview = false }) {
  const startTime = Date.now()
  
  try {
    // Parallel data fetching for better performance
    const [article, metadata] = await Promise.all([
      fetchArticle(params.slug, { preview }),
      fetchSiteMetadata().catch(() => null) // Optional data
    ])
    
    if (!article) {
      console.log(`Article not found: ${params.slug}`)
      return { notFound: true }
    }
    
    // Log build performance in development
    if (process.env.NODE_ENV === 'development') {
      console.log(`Built ${params.slug} in ${Date.now() - startTime}ms`)
    }
    
    return {
      props: {
        article,
        metadata,
        buildTime: new Date().toISOString()
      },
      revalidate: preview ? 60 : 3600
    }
  } catch (error) {
    console.error(`getStaticProps error for ${params.slug}:`, error)
    
    // In development, show error details
    if (process.env.NODE_ENV === 'development') {
      throw error
    }
    
    // In production, fail gracefully
    return { notFound: true }
  }
}
Common Mistakes to Avoid
Never use browser-only APIs like localStorage in getStaticProps. Don't fetch user-specific data that should be personalized. Avoid massive data fetching that slows builds. Always handle API errors to prevent build failures.

Quiz

1. The NewsWave team wants to understand when getStaticProps runs. When does Next.js execute this function?


2. NewsWave has dynamic article routes like /articles/[slug]. Which function must you export alongside getStaticProps to tell Next.js which pages to pre-render?


3. What should getStaticProps return when NewsWave can't find an article in the CMS to prevent showing a broken page?


Up Next: getServerSideProps

Learn when to render pages on every request instead of at build time for dynamic, personalized content that changes frequently.