Pages & Routes
In the App Router, a route is simply a folder inside the app directory, and a page is the UI that gets rendered for that route. The rule is deliberately simple: a folder only becomes a publicly visitable URL segment when it contains a file named page.tsx (or page.js). Everything else you put in that folder — components, styles, helper functions — stays private and is never directly reachable by a URL.
The page.tsx File
A page component is just a React component exported as the default export of page.tsx. Next.js takes care of wiring it up to the routing system, generating the route, and rendering it on the server (by default) whenever someone visits that URL.
// app/about/page.tsx
export default function AboutPage() {
return <h1>About Us</h1>
}
// This file alone makes /about a real, visitable route.Nested Folders Create Nested Segments
Because routes are derived from the folder structure, nesting folders inside app automatically nests URL segments. There is no separate routes config file to maintain — the file system is the router.
Folder Structure | Resulting Route |
|---|---|
app/page.tsx | / |
app/about/page.tsx | /about |
app/blog/page.tsx | /blog |
app/blog/first-post/page.tsx | /blog/first-post |
app/dashboard/settings/page.tsx | /dashboard/settings |
Only page.tsx Makes a Segment Public
This distinction matters a lot in practice. You can freely colocate non-route files — components, hooks, test files, styles — inside any route folder, and Next.js will ignore them for routing purposes as long as they aren't named page.tsx (or one of the other reserved file names like layout.tsx, loading.tsx, and error.tsx).
app/
dashboard/
page.tsx -> /dashboard (public route)
DashboardChart.tsx -> not a route, just a component
utils.ts -> not a route, just a helper
settings/
page.tsx -> /dashboard/settings (public route)Pages Receive params and searchParams
Every page component automatically receives two special props from Next.js: params for dynamic route segments (like [id]) and searchParams for the URL's query string. We'll go deep on dynamic routes in a dedicated lesson, but here's the shape you'll see:
// app/blog/[slug]/page.tsx
type PageProps = {
params: { slug: string }
searchParams: { [key: string]: string | string[] | undefined }
}
export default function BlogPostPage({ params, searchParams }: PageProps) {
return (
<article>
<h1>Post: {params.slug}</h1>
<p>Sort order: {searchParams.sort ?? 'default'}</p>
</article>
)
}What a Page Is Responsible For
Rendering the unique UI for its specific route segment.
Optionally fetching the data that UI needs (pages can be async Server Components).
Receiving params and searchParams for dynamic or query-driven behavior.
Being wrapped automatically by any layout.tsx files above it in the tree.
The mental model to keep: folders describe URL structure, page.tsx makes a segment public, and everything else in that folder is just supporting cast. Once that clicks, the rest of the App Router's file conventions — layouts, loading states, error boundaries — feel like natural extensions of the same idea.