This error occurs when trying to use the legacy getInitialProps data fetching method in Next.js App Router. The App Router introduced in Next.js 13+ uses a different data fetching paradigm based on Server Components and the native fetch API, making getInitialProps incompatible.
This error indicates that you are attempting to use the getInitialProps method in a Next.js application using the App Router (app directory), which was introduced in Next.js 13. getInitialProps is a legacy data fetching API that only works with the Pages Router (pages directory). The App Router fundamentally changed how data fetching works in Next.js. Instead of using special functions like getInitialProps, getServerSideProps, or getStaticProps, the App Router uses Server Components that can directly fetch data using async/await syntax within the component itself. This provides better performance, smaller JavaScript bundles, and more intuitive data fetching patterns. getInitialProps was designed to run on the server for the initial page load and then on the client for subsequent navigations. However, this pattern is incompatible with the App Router's architecture, which separates Server Components (server-only) from Client Components (interactive). When you see this error, it means you need to migrate your data fetching approach to match the App Router's paradigm.
First, confirm you are using the App Router by checking if you have an app directory in your project. If you have both app and pages directories, Next.js allows them to coexist, but each directory uses different data fetching methods.
# Check your project structure
ls -la
# Look for app/ directoryIf you only have a pages directory, you can continue using getInitialProps. If you have an app directory and want to use App Router features, you need to migrate your data fetching.
In the App Router, Server Components can be async by default and fetch data directly. Replace your getInitialProps function with an async Server Component.
Before (Pages Router):
// pages/posts.tsx
import { NextPage } from 'next';
interface Props {
posts: Post[];
}
const PostsPage: NextPage<Props> = ({ posts }) => {
return <div>{posts.map(post => <div key={post.id}>{post.title}</div>)}</div>;
};
PostsPage.getInitialProps = async () => {
const res = await fetch('https://api.example.com/posts');
const posts = await res.json();
return { posts };
};
export default PostsPage;After (App Router):
// app/posts/page.tsx
interface Post {
id: number;
title: string;
}
async function getPosts(): Promise<Post[]> {
const res = await fetch('https://api.example.com/posts', {
cache: 'no-store' // Equivalent to getServerSideProps behavior
});
if (!res.ok) throw new Error('Failed to fetch posts');
return res.json();
}
export default async function PostsPage() {
const posts = await getPosts();
return (
<div>
{posts.map(post => (
<div key={post.id}>{post.title}</div>
))}
</div>
);
}The App Router uses the native fetch API with caching options to replace getServerSideProps and getStaticProps behavior. Choose the appropriate caching strategy:
For server-side rendering on every request (like getServerSideProps):
const res = await fetch(url, { cache: 'no-store' });For static generation with revalidation (like getStaticProps with revalidate):
const res = await fetch(url, { next: { revalidate: 3600 } }); // Revalidate every hourFor static generation (like getStaticProps):
const res = await fetch(url, { cache: 'force-cache' }); // Default behaviorIf you don't use fetch, you can configure the route segment:
// app/posts/page.tsx
export const dynamic = 'force-dynamic'; // Equivalent to getServerSideProps
// or
export const revalidate = 3600; // Revalidate every hourIf you were using getInitialProps in _app.js for global data (like user authentication), move this logic to a root layout in the App Router.
Before (Pages Router with _app.tsx):
// pages/_app.tsx
App.getInitialProps = async (appContext) => {
const user = await fetchUser();
return { user };
};After (App Router with root layout):
// app/layout.tsx
import { cookies } from 'next/headers';
async function getUser() {
const cookieStore = cookies();
const token = cookieStore.get('token');
if (!token) return null;
const res = await fetch('https://api.example.com/user', {
headers: { Authorization: `Bearer ${token.value}` },
cache: 'no-store'
});
return res.ok ? res.json() : null;
}
export default async function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
const user = await getUser();
return (
<html lang="en">
<body>
<UserProvider user={user}>
{children}
</UserProvider>
</body>
</html>
);
}If your getInitialProps included client-side logic that needs access to browser APIs or React hooks, separate it into a Client Component.
// app/posts/page.tsx (Server Component)
async function getPosts() {
const res = await fetch('https://api.example.com/posts');
return res.json();
}
export default async function PostsPage() {
const posts = await getPosts();
return <PostsList initialPosts={posts} />;
}// app/posts/posts-list.tsx (Client Component)
'use client';
import { useState, useEffect } from 'react';
export default function PostsList({ initialPosts }) {
const [posts, setPosts] = useState(initialPosts);
// Client-side logic, hooks, event handlers
useEffect(() => {
// Client-side effects
}, []);
return (
<div>
{posts.map(post => <div key={post.id}>{post.title}</div>)}
</div>
);
}After migrating from getInitialProps to App Router data fetching, verify that:
# Build the application to check for errors
npm run build
# Run in development mode and test all pages
npm run devTest checklist:
- Data loads correctly on initial page load
- Navigation between pages works as expected
- Authentication state persists across pages
- SEO metadata is correctly generated
- Performance metrics (check Network tab for data fetching)
- Error handling works when API calls fail
For incremental migration, Next.js allows you to run both Pages Router and App Router simultaneously. You can keep getInitialProps in your pages directory while gradually migrating routes to the app directory. However, be aware that using getInitialProps in _app.js will disable Automatic Static Optimization for all pages in the pages directory.
If you need the specific behavior of getInitialProps (running server-side on first load, then client-side on navigation), you can achieve this with a combination of Server Components for initial load and client-side fetching with useEffect for subsequent navigations. However, this pattern is generally discouraged in App Router as it goes against the framework's design principles.
For complex applications with authentication, consider using middleware for route protection rather than data fetching in getInitialProps. Middleware runs before the request is completed and can redirect unauthenticated users without loading the page.
When migrating from getInitialProps, pay special attention to context object properties you were using (req, res, pathname, query, etc.). In App Router, these are accessed differently: use cookies() and headers() from next/headers for request data, and searchParams prop for query parameters.
Some third-party libraries and authentication providers may have outdated examples using getInitialProps. Always check for App Router-specific documentation or migration guides from the library maintainers.
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