default.js

Renders a fallback in a parallel route slot when a hard load cannot recover the slot's active state.

Signature
export default function Default({
  params,
}: {
  params: Promise<{ artist: string }>
}): React.ReactNode

Parameters

ParameterTypeDescription
$paramsoptionalPromise<{ [key: string]: string | string[] }>Resolves to the dynamic route parameters from the root segment down to the slot's subpages. Must be awaited, or read with React's use() in a Client Component.

Return value

React.ReactNode | Promise<React.ReactNode>The fallback UI for the slot on a hard navigation. Returning null renders nothing; calling notFound() reproduces the old behaviour of returning a 404 for an unmatched named slot.

Errors

When a named slot has no matching subpage and no default.js on a hard navigation, Next.js returns an error and requires you to add one. A missing default.js for the implicit children slot returns a 404 page instead.

Changelog

  • PHP v15.0.0-RCparams became a Promise. Synchronous access still works in 15 for backwards compatibility but is deprecated; a codemod is available.

default exists for one situation. In parallel routes, Next.js tracks each slot's active subpage during soft navigation — but a hard load (refresh, pasted URL, back from an external site) has no client state to read, so a slot whose subpage does not match the current URL has nothing to render. default.js is what it renders instead.

You need one in every named slot (@team, @analytics) that will not match every URL under its layout — and one for the implicit children slot too, otherwise a refresh returns a 404 for the whole route.

What trips people up

  • Everything works until you press refresh. That is the classic symptom: soft navigation restores slot state, a hard load cannot, and the missing default.js surfaces only then. Always test parallel routes by reloading, not just clicking.
  • A missing named-slot default.js is an error, not a silent blank — Next.js requires you to add one. To restore the old 404 behaviour, write a default.js that calls notFound().
  • children is a slot as well, and is the one people forget.
  • params is a Promise and must be awaited.
  • Modal slots usually want return null — a modal should not be open on a fresh load.

Example

Example · typescript
export default async function Default({
  params,
}: {
  params: Promise<{ artist: string }>
}) {
  const { artist } = await params

  return <p>Select an album by {artist}.</p>
}

When to use it

  • Return null from a @modal slot so no dialog appears after a page refresh
  • Render a dashboard's default analytics panel when the URL only matches the @team slot
  • Reproduce the old 404 behaviour for an unmatched slot by calling notFound()
  • Give the implicit children slot a fallback so refreshing a parallel route does not 404

Frequently asked questions

Why does my parallel route break on refresh but work when clicking?

Because soft navigation keeps track of each slot's active subpage in client state, while a hard load has no such state to recover. Any slot whose subpage does not match the current URL then has nothing to render, and Next.js falls back to default.js. Add a default.js to every named slot — and to the app directory itself, for the implicit children slot.

Do I need a default.js for the children slot?

Yes, whenever the parent page's active state may be unrecoverable on a hard load. children is an implicit slot just like @team or @analytics, and without a default.js for it the route returns a 404 page on refresh. It is the most commonly missed file when parallel routes are added to an existing layout.

What happens if a slot has no default.js?

For named slots such as @team or @analytics, Next.js returns an error and requires you to define one before it will continue — it no longer silently 404s. If you actually want the old 404, create a default.js whose body calls notFound(). For the implicit children slot, a missing default.js produces a 404 page for the route.

More examples

A modal slot that closes on hard load

This is app/@modal/default.tsx. On a soft navigation the intercepting route fills the @modal slot and the dialog appears over the page behind it; on a refresh there is no active state to recover, so this renders instead. Returning null means the underlying page shows on its own — which is what a user reloading a URL expects, rather than a dialog with nothing behind it.

Example · typescript
export default function Default() {
  return null
}

Restoring the old 404 behaviour

Before Next.js required a default.js, an unmatched named slot produced a 404. This reproduces that: the file exists so Next.js does not error, but rendering it throws to the nearest not-found.js. Use it when a URL that leaves the slot unmatched genuinely is not a valid page, rather than papering over it with an empty fallback.

Example · typescript
import { notFound } from 'next/navigation'

export default function Default() {
  notFound()
}

Slot layout and the fallbacks it needs

@team has a settings subpage and @analytics does not. Clicking through to /settings keeps @analytics on whatever it was showing, but refreshing that URL cannot recover it, so app/@analytics/default.tsx renders. The third file is the one most often missed: children is an implicit slot, and without app/default.tsx a hard load of an unmatched route 404s the whole page.

Example · typescript
// app/layout.tsx
export default function Layout({
  children,
  team,
  analytics,
}: {
  children: React.ReactNode
  team: React.ReactNode
  analytics: React.ReactNode
}) {
  return (
    <>
      {children}
      <section>{team}</section>
      <section>{analytics}</section>
    </>
  )
}

// Files needed alongside it:
//   app/@team/default.tsx
//   app/@analytics/default.tsx
//   app/default.tsx        <- for the implicit `children` slot

Discussion

  • Be the first to comment on this lesson.