Layouts & pages

Pages and layouts are just React components. The framework wraps your page with the nearest layout chain and renders the result.

A page

The simplest page is a default-exported component. It receives params and searchParams as props.

app/page.tsx
export default function HomePage() {
  return <h1>Hello, world</h1>;
}

A layout

A layout wraps one or more pages. It receives children as a prop. The root layout (in app/layout.tsx) is required and must render <html> and <body>.

app/layout.tsx
import type { ReactNode } from "react";

export default function RootLayout({ children }: { children: ReactNode }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}

Nested layouts

Add a layout.tsx to any segment to wrap that segment and its children. Layouts are composed — they don't replace parent layouts.

app/dashboard/layout.tsx
import type { ReactNode } from "react";
import { Sidebar } from "@/components/sidebar";

export default function DashboardLayout({ children }: { children: ReactNode }) {
  return (
    <div className="grid grid-cols-[14rem_1fr]">
      <Sidebar />
      <main>{children}</main>
    </div>
  );
}

Loading states

Export a loading.tsx to show a UI while the page is being prepared. In ssr mode, the framework will show this until the page is fully rendered.

app/dashboard/loading.tsx
export default function Loading() {
  return <div className="animate-pulse">Loading dashboard…</div>;
}

Error boundaries

Export an error.tsx to catch errors thrown by this segment and its children.

app/dashboard/error.tsx
"use client";

export default function ErrorBoundary({
  error,
  reset,
}: {
  error: Error;
  reset: () => void;
}) {
  return (
    <div>
      <h2>Something went wrong</h2>
      <p>{error.message}</p>
      <button onClick={reset}>Try again</button>
    </div>
  );
}

Not found

A not-found.tsx is rendered when no route matches. The closest one in the tree is used.

Next steps

Continue to Data fetching.