export default — That's a Page
page.tsx — The Route's UI
A page.tsx file exports the React component rendered at that route's URL. It can be async and receive params and searchParams.
What you'll learn
- Create `app/about/page.tsx` with a default export
- Use async server components for data fetching
- Receive `params` and `searchParams` (both are Promises in Next 15)
A page.tsx is the leaf of a route. Whatever you export default is what users see at
that URL.
The Simplest Page
// app/about/page.tsx → /about
export default function AboutPage() {
return <h1>About Us</h1>;
} No imports, no router config. The file exists, so the route exists.
Pages Can Be Async
Server components — which is what pages are by default — can be async functions.
That lets you fetch data inline, without useEffect or loaders.
// app/users/page.tsx
type User = { id: string; name: string };
export default async function UsersPage() {
const users: User[] = await fetch('https://api.example.com/users', {
next: { revalidate: 60 },
}).then((r) => r.json());
return (
<ul>
{users.map((u) => (
<li key={u.id}>{u.name}</li>
))}
</ul>
);
} params and searchParams
Dynamic routes pass the captured params in. In Next.js 15 both params and
searchParams are Promises — you await them before reading.
// app/blog/[slug]/page.tsx → /blog/hello?ref=twitter
type PageProps = {
params: Promise<{ slug: string }>;
searchParams: Promise<{ ref?: string }>;
};
export default async function PostPage({ params, searchParams }: PageProps) {
const { slug } = await params;
const { ref } = await searchParams;
return (
<article>
<h1>{slug}</h1>
{ref && <p>Referred by {ref}</p>}
</article>
);
} That is the whole API for pages. Next we look at layouts, the shared wrappers that sit above them.
layout.tsx — Shared UI Across Segments →