This error occurs when attempting to use getStaticProps in Next.js App Router. The getStaticProps function is exclusive to the Pages Router and cannot be used in the app directory structure.
This error indicates that you're trying to use the `getStaticProps` data fetching method in a location where it's not supported. In Next.js, `getStaticProps` is a Pages Router feature that was used to fetch data at build time for static generation. With the introduction of the App Router in Next.js 13+, the data fetching paradigm has fundamentally changed. The App Router uses React Server Components by default, which fetch data directly within components rather than using separate data fetching functions like `getStaticProps`. The error most commonly appears when developers try to use `getStaticProps` in the app directory, or when attempting to export it from non-page files like `_app.js`, `_document.js`, or custom error pages in the pages directory.
Check whether you're using the Pages Router (pages directory) or App Router (app directory):
# Pages Router structure
pages/
index.js
about.js
_app.js
# App Router structure
app/
page.js
about/
page.js
layout.jsIf you have an app directory, you're using the App Router where getStaticProps is not supported.
Replace getStaticProps with direct data fetching in an async Server Component:
Before (Pages Router):
// pages/posts.js
export async function getStaticProps() {
const res = await fetch('https://api.example.com/posts');
const posts = await res.json();
return {
props: { posts },
revalidate: 60
};
}
export default function Posts({ posts }) {
return <div>{/* render posts */}</div>;
}After (App Router):
// app/posts/page.tsx
async function getPosts() {
const res = await fetch('https://api.example.com/posts', {
next: { revalidate: 60 } // ISR equivalent
});
return res.json();
}
export default async function Posts() {
const posts = await getPosts();
return <div>{/* render posts */}</div>;
}For static generation, use cache: 'force-cache' (default behavior):
const res = await fetch('https://api.example.com/posts', {
cache: 'force-cache' // Static generation
});If you're intentionally using the Pages Router, make sure getStaticProps is exported from a page file, not from _app.js or other special files:
Correct:
// pages/blog/[slug].js
export async function getStaticProps({ params }) {
// Fetch data
return { props: { data } };
}
export default function BlogPost({ data }) {
return <div>{/* render */}</div>;
}Incorrect:
// pages/_app.js
export async function getStaticProps() { // ❌ Not allowed
return { props: {} };
}Also ensure it's a separate export, not a component member:
// ❌ Wrong
BlogPost.getStaticProps = async () => { };
// ✅ Correct
export async function getStaticProps() { }Map your Pages Router patterns to App Router equivalents:
Static generation with revalidation (ISR):
// Pages Router
export async function getStaticProps() {
return {
props: { data },
revalidate: 3600 // Revalidate every hour
};
}
// App Router equivalent
const res = await fetch('https://api.example.com/data', {
next: { revalidate: 3600 }
});On-demand revalidation:
// Use revalidatePath or revalidateTag
import { revalidatePath } from 'next/cache';
// In a server action or route handler
revalidatePath('/posts');Dynamic rendering (no caching):
const res = await fetch('https://api.example.com/data', {
cache: 'no-store'
});For dynamic routes, replace both getStaticProps and getStaticPaths:
Pages Router:
// pages/posts/[id].js
export async function getStaticPaths() {
return {
paths: [{ params: { id: '1' } }],
fallback: false
};
}
export async function getStaticProps({ params }) {
const post = await fetchPost(params.id);
return { props: { post } };
}App Router:
// app/posts/[id]/page.tsx
export async function generateStaticParams() {
return [{ id: '1' }, { id: '2' }];
}
export default async function Post({ params }: { params: { id: string } }) {
const post = await fetchPost(params.id);
return <div>{/* render post */}</div>;
}Incremental Migration Strategy:
You can run both Pages Router and App Router simultaneously during migration. Keep existing pages in the pages directory while gradually moving routes to app. The App Router takes precedence for conflicting routes.
Server Component Benefits:
Unlike getStaticProps, Server Components can fetch data at the component level, not just the page level. This enables more granular data fetching and better code organization.
Client Components:
If you need interactivity, mark components with 'use client' and fetch data using useEffect or libraries like SWR/React Query. However, prefer Server Components for initial data loading.
Edge Cases:
- getStaticProps cannot be used in API routes
- Cannot be exported from pages/api/* endpoints
- Must return an object with props, redirect, or notFound
Performance Considerations:
The App Router's fetch API includes automatic request deduplication, meaning multiple components can request the same data without duplicate network calls. This wasn't possible with getStaticProps.
React Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps
React Hook useCallback has a missing dependency
Cannot use private fields in class components without TS support
Cannot use private fields in class components without TS support
Cannot destructure property 'xxx' of 'undefined'
Cannot destructure property of undefined when accessing props
useNavigate() may be used only in the context of a <Router> component.
useNavigate() may be used only in the context of a Router component
Cannot find module or its corresponding type declarations
How to fix "Cannot find module or type declarations" in Vite