Next.js

NextAuth: Property 'session' does not exist on type '{}'

Reproduced on Next.js 12.3.4, 13.0.0 and 16.4.0: why the session prop broke in _app.tsx, the one-line generic that fixes it, and why the d.ts override only seems to work.

Mahdi Ben RhoumaMahdi Ben Rhouma
8 min read

A Next.js app that wires NextAuth.js into pages/_app.tsx exactly as the NextAuth getting-started page shows can stop compiling with a type error on the session prop. The cause is a change to the AppProps type in Next.js, not NextAuth, and the fix is one generic argument.

TL;DR#

  • Cause: from Next.js 12.3.0 to 12.3.4, AppProps defaulted its generic to {} and typed pageProps with it. Reading pageProps.session (or destructuring session out of it) is then a property access on an empty object type.
  • Fix: tell AppProps what pageProps holds: AppProps<{ session?: Session | null }>, with Session imported from next-auth.
  • Not the fix: adding session to the Component type. That changes the wrong prop.
  • From Next.js 13.0.0 the default is any and the error disappears, but so does the type check on session.

The error as next build prints it, reproduced on Next.js 12.3.4 with TypeScript 4.8.4:

text
info  - Linting and checking validity of types...
Failed to compile.
 
./pages/_app.tsx:6:41
Type error: Property 'session' does not exist on type '{}'.

Running the TypeScript compiler on its own gives the same error with its code:

text
pages/_app.tsx(6,41): error TS2339: Property 'session' does not exist on type '{}'.

This is the error behind the Stack Overflow question "(NextAuth) Type error: Property 'session' does not exist on type ''", asked in September 2022 and viewed more than 24,000 times.

Why it happens#

The NextAuth.js v4 example exposes the session context by destructuring it out of pageProps in pages/_app.jsx:

jsx
import { SessionProvider } from "next-auth/react"
 
export default function App({
  Component,
  pageProps: { session, ...pageProps },
}) {
  return (
    <SessionProvider session={session}>
      <Component {...pageProps} />
    </SessionProvider>
  )
}

That is JavaScript. In a .tsx file the props are annotated with AppProps from next/app, and what pageProps is typed as depends on the Next.js version:

  • In Next.js 12.2.5, pageProps was hard-coded to any, so any property access compiled.
  • PR #38867, "fix(ts): use AppProps's generic for pageProps", merged on 26 August 2022, made pageProps take the type of the AppProps generic. The generic's default stayed {}. The merge commit is in the v12.3.0 tag and not in v12.2.5, which matches the release the accepted answer names.
  • PR #41264, "fix(ts): relax AppProps to not require generic", merged on 7 October 2022, changed the default to any. Its description says #38867 had made the pageProps shape "always require" a generic, "since the default {} will throw a TS error if you try accessing any property". That commit is in the v13.0.0 tag; the published 12.3.4 package still ships the {} default.

So with Next.js 12.3.x, AppProps with no argument means pageProps: {}, and session is not a property of {}. It makes no difference whether the code reads pageProps.session or destructures { session, ...pageProps }: both were reproduced, and both fail with the same TS2339 error.

The Next.js custom App documentation states what pageProps is at runtime: "pageProps is an object with the initial props that were preloaded for your page by one of our data fetching methods, otherwise it's an empty object." TypeScript cannot know which pages return a session prop, so the shape has to come from you.

Why extending the Component type does not help#

The question's first attempt added session to the Component type:

tsx
export interface CustomAppProps extends AppProps {
  Component: NextComponentType & { auth?: boolean; session?: Session };
}

On Next.js 12.3.4 this still fails with Property 'session' does not exist on type '{}', at the destructuring of pageProps. The error is about pageProps; Component is a different prop.

Fix: give AppProps the shape of pageProps#

  1. Import the Session type from next-auth and pass the shape of pageProps as the generic of AppProps. The destructuring from the NextAuth example now type-checks unchanged:
tsx
// pages/_app.tsx
import type { Session } from "next-auth";
import { SessionProvider } from "next-auth/react";
import type { AppProps } from "next/app";
 
export default function MyApp({
  Component,
  pageProps: { session, ...pageProps },
}: AppProps<{ session?: Session | null }>) {
  return (
    <SessionProvider session={session}>
      <Component {...pageProps} />
    </SessionProvider>
  );
}

Both the ? and the | null are deliberate. In next-auth v4, getServerSession is declared to return a promise of the session or null (source), so a page that returns its result as a prop can pass null. And pages that return no session prop at all leave it undefined in _app.tsx. SessionProvider accepts both. The NextAuth client documentation says that passing the session page prop lets you "avoid checking the session twice", and that "this only works on pages where you provide the correct pageProps" (client docs).

  1. Return the session from the pages that need it. The NextAuth.js v4 Next.js configuration page recommends getServerSession in getServerSideProps and shows a page returning it as a prop:
tsx
// pages/dashboard.tsx
import type { GetServerSideProps } from "next";
import { getServerSession } from "next-auth/next";
import { authOptions } from "./api/auth/[...nextauth]";
 
export const getServerSideProps: GetServerSideProps = async (context) => {
  const session = await getServerSession(context.req, context.res, authOptions);
 
  if (!session) {
    return { redirect: { destination: "/", permanent: false } };
  }
 
  return { props: { session } };
};
 
export default function Dashboard() {
  return <p>Signed in</p>;
}

This page, the API route exporting authOptions and the _app.tsx above type-check together and build on Next.js 12.3.4.

  1. If _app.tsx also needs other page props, add them to the same object type, for example AppProps<{ session?: Session | null; title?: string }>. Keep everything that pages pass through pageProps in that one type.

Verify the fix#

Run the type checker on its own, then the build:

bash
npx tsc --noEmit
npx next build

On Next.js 12.3.4, the version with the error, the file above passes tsc with exit code 0, and next build gets past the type step to the compiled output:

text
info  - Linting and checking validity of types...
info  - Creating an optimized production build...
info  - Compiled successfully

One email a month — the latest explained errors

What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.

The types/next.d.ts override only seems to work#

A second answer on the Stack Overflow thread copies a types/next.d.ts file from the NextAuth TypeScript example repository, which redeclares AppProps inside declare module "next/app" with a session field on pageProps. Copied into a Next.js 12.3.4 project, it does make the original _app.tsx compile. Turning library checking back on shows why:

text
types/next.d.ts(6,8): error TS2300: Duplicate identifier 'AppProps'.

The file declares a second AppProps type alias, which TypeScript reports as a duplicate. The error only stays hidden because skipLibCheck skips checking every .d.ts file, and the tsconfig that Next.js writes for a project suggests skipLibCheck: true (writeConfigurationDefaults.ts). That example repository has also been archived on GitHub. The generic in the fix needs no declaration file and no compiler option to stay quiet.

After upgrading to Next.js 13 or later#

On Next.js 13.0.0 and on 16.4.0 (with TypeScript 7.0.2), all three failing versions of _app.tsx compile without the generic, because pageProps is now any. The current Next.js source still declares AppProps<P = any>.

That any turns the type check off rather than satisfying it. A deliberately wrong line shows the difference on Next.js 16.4.0:

tsx
const probe: number = pageProps.session; // deliberately wrong type

With plain AppProps, tsc exits 0. With AppProps<{ session?: Session | null }>, it catches the mistake:

text
pages/_app.tsx(5,9): error TS2322: Type 'Session | null | undefined' is not assignable to type 'number'.

So the generic is worth keeping after an upgrade. If the upgrade also moves the project to a newer TypeScript major, other errors can show up at the same time; the TypeScript migration notes for Next.js and Supabase projects list what changed in the compiler defaults.

When the project uses the App Router#

All of the above concerns the Pages Router, where _app.tsx and pageProps exist. The App Router has no pageProps. The NextAuth example page notes that SessionProvider "requires a client component and therefore cannot be put inside the root layout", so it goes in a separate file marked "use client" that the layout renders. If the boundary between server and client files is unclear, use client vs use server in Next.js explains what each directive actually marks.

How this was reproduced#

Each version of pages/_app.tsx was compiled in a throwaway node:22-bookworm container on 7 October 2026, with next-auth 4.24.15: on Next.js 12.3.4 with TypeScript 4.8.4 (the plain AppProps, the destructured version and the Component-type attempt all fail with TS2339; the generic passes, under tsc and next build, alongside a getServerSideProps page and the auth API route), on Next.js 13.0.0 (all pass), and on Next.js 16.4.0 with React 19.3.0 and TypeScript 7.0.2 (all pass; the probe above). The AppProps defaults were also read from the published type definitions of each version.

If the build now gets past the types but fails while collecting page data, look at what getServerSideProps returns next to the session: props must be JSON-serialisable, and a Date object breaks that, as covered in the "[object Date]" cannot be serialized as JSON fix. And if the team is weighing whether to stay on NextAuth at all, Clerk vs Better Auth vs Supabase vs Auth.js compares the options for a Next.js project.

Frequently Asked Questions

|

Have more questions? Contact us

Still stuck? Paste your error.

Paste the error message or stack trace — we check it against every fix and tool on the site.

Matching happens in your browser. When you search, a cleaned error signature is sent so we can spot errors we do not cover yet — never the raw text, your IP address or your e-mail. Do not paste passwords or keys. Details

Written by

Mahdi Ben Rhouma
Mahdi Ben Rhouma

Full-Stack Dev — Next.js & Supabase

Solo developer building SaaS products with Next.js and Supabase. Writing about production patterns the official docs skip.

Remote