πŸ”§ Error Fixes
Β· 5 min read
Last updated on

Next.js: getServerSideProps Error β€” How to Fix It


Error: `getServerSideProps` is not supported in app/ directory
Error: `getStaticProps` is not supported in app/ directory

You’re using Pages Router data-fetching functions (getServerSideProps, getStaticProps, getStaticPaths) inside the App Router’s app/ directory. These APIs don’t exist in the App Router.

Why this happens

Next.js has two routing systems that coexist but use completely different patterns:

Pages Router (pages/)App Router (app/)
Data fetchinggetServerSideProps, getStaticPropsasync Server Components, fetch()
ComponentsClient Components by defaultServer Components by default
Layouts_app.tsx, _document.tsxlayout.tsx (nested)
API routespages/api/*.tsapp/api/*/route.ts
Available sinceNext.js 1Next.js 13.4+

When you place a file in app/ and export getServerSideProps, Next.js throws an error because that function has no meaning in the App Router context.

Fix 1: Convert to async Server Component (most common)

In the App Router, components are Server Components by default. You can await data directly in the component β€” no special function needed.

// ❌ Pages Router pattern β€” doesn't work in app/
export async function getServerSideProps() {
  const res = await fetch('https://api.example.com/posts');
  const posts = await res.json();
  return { props: { posts } };
}

export default function PostsPage({ posts }) {
  return <ul>{posts.map(p => <li key={p.id}>{p.title}</li>)}</ul>;
}
// βœ… App Router equivalent β€” app/posts/page.tsx
export default async function PostsPage() {
  const res = await fetch('https://api.example.com/posts');
  const posts = await res.json();

  return <ul>{posts.map(p => <li key={p.id}>{p.title}</li>)}</ul>;
}

No wrapper function, no props object. The component itself is async and fetches its own data on the server.

Fix 2: Convert getStaticProps to cached fetch

getStaticProps generated static pages at build time. In the App Router, you achieve the same with fetch() and caching options.

// ❌ Pages Router
export async function getStaticProps() {
  const posts = await getPosts();
  return { props: { posts }, revalidate: 60 };
}

// βœ… App Router β€” static by default, revalidate with next.revalidate
export default async function PostsPage() {
  const res = await fetch('https://api.example.com/posts', {
    next: { revalidate: 60 },  // ISR: revalidate every 60 seconds
  });
  const posts = await res.json();

  return <ul>{posts.map(p => <li key={p.id}>{p.title}</li>)}</ul>;
}

Caching behavior:

  • fetch(url) β€” cached indefinitely (like getStaticProps without revalidate)
  • fetch(url, { next: { revalidate: 60 } }) β€” ISR, revalidates every 60s
  • fetch(url, { cache: 'no-store' }) β€” never cached (like getServerSideProps)

Fix 3: Convert getStaticPaths to generateStaticParams

// ❌ Pages Router
export async function getStaticPaths() {
  const posts = await getPosts();
  return {
    paths: posts.map(p => ({ params: { slug: p.slug } })),
    fallback: 'blocking',
  };
}

// βœ… App Router β€” app/posts/[slug]/page.tsx
export async function generateStaticParams() {
  const posts = await getPosts();
  return posts.map(p => ({ slug: p.slug }));
}

export default async function PostPage({ params }: { params: { slug: string } }) {
  const post = await getPost(params.slug);
  return <article><h1>{post.title}</h1><p>{post.content}</p></article>;
}

Fix 4: Move the file to pages/ if you want to keep using Pages Router

If you’re not ready to migrate, simply keep the file in pages/:

pages/posts/index.tsx  ← getServerSideProps works here
app/posts/page.tsx     ← use Server Components here (don't have both!)

Important: Don’t have the same route in both pages/ and app/. If both exist, Next.js uses the app/ version and ignores pages/.

Fix 5: Convert metadata (head/SEO)

If you used getServerSideProps primarily for SEO metadata:

// ❌ Pages Router β€” fetching data just for <Head>
export async function getServerSideProps({ params }) {
  const post = await getPost(params.slug);
  return { props: { post } };
}
// Then using <Head><title>{post.title}</title></Head>

// βœ… App Router β€” use generateMetadata
export async function generateMetadata({ params }: { params: { slug: string } }) {
  const post = await getPost(params.slug);
  return {
    title: post.title,
    description: post.excerpt,
    openGraph: { title: post.title, images: [post.coverImage] },
  };
}

export default async function PostPage({ params }: { params: { slug: string } }) {
  const post = await getPost(params.slug);
  return <article>{post.content}</article>;
}

Next.js deduplicates the getPost() call β€” if both generateMetadata and the page component call it with the same arguments, it only executes once.

Fix 6: Convert API routes

// ❌ Pages Router β€” pages/api/users.ts
export default function handler(req, res) {
  if (req.method === 'GET') {
    const users = getUsers();
    res.json(users);
  }
}

// βœ… App Router β€” app/api/users/route.ts
export async function GET() {
  const users = await getUsers();
  return Response.json(users);
}

export async function POST(request: Request) {
  const body = await request.json();
  const user = await createUser(body);
  return Response.json(user, { status: 201 });
}

Migration strategy

Don’t migrate everything at once. Next.js supports both routers simultaneously:

  1. Keep existing pages/ routes working
  2. Create new routes in app/
  3. Migrate one route at a time: create in app/, test, then delete from pages/
  4. Migrate layouts last (_app.tsx β†’ app/layout.tsx)

Common mistakes during migration

Forgetting 'use client' for interactive components:

// ❌ useState doesn't work in Server Components
export default function Counter() {
  const [count, setCount] = useState(0);  // Error!
  return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}

// βœ… Add 'use client' directive
'use client';
export default function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}

Trying to pass functions as props from Server to Client Components:

// ❌ Can't pass functions across the server/client boundary
<ClientComponent onSubmit={async (data) => { await saveToDb(data) }} />

// βœ… Use Server Actions instead
'use server';
async function submitForm(data: FormData) {
  await saveToDb(Object.fromEntries(data));
}

FAQ

Can I use getServerSideProps and App Router in the same project?

Yes, but not for the same route. pages/about.tsx can use getServerSideProps while app/dashboard/page.tsx uses Server Components. They coexist at the project level.

Is the App Router faster than getServerSideProps?

For most cases, yes. Server Components stream HTML progressively (with Suspense), while getServerSideProps blocks the entire page until all data is fetched. The App Router also deduplicates fetch requests automatically.

How do I access cookies/headers in the App Router?

import { cookies, headers } from 'next/headers';

export default async function Page() {
  const cookieStore = cookies();
  const token = cookieStore.get('session')?.value;
  const headersList = headers();
  const userAgent = headersList.get('user-agent');
  // ...
}

What replaces context.req and context.res from getServerSideProps?

In the App Router, use cookies(), headers(), and redirect() from next/headers and next/navigation. For full request/response access, use Route Handlers (route.ts).

πŸ“˜