Next.js App Router Files
Learn about the 9 special files in Next.js App Router and how to use File-system Routing to build modern web applications.
Note: This is theoretical Next.js content with minimal relation to NextDevKit. If you're already familiar with Next.js App Router, feel free to skip this section.
Learning this section will help you better understand NextDevKit's code structure.
After learning about Next.js project structure, let's dive deep into the core concept of Next.js App Router—the special file system.
Next.js App Router introduces 9 special files, each with unique roles and use cases. This tutorial will detail these files one by one, using real code examples from NextDevKit to help you understand and use them effectively.
Code Examples: The following code examples are partly from NextDevKit templates and partly from official Next.js examples.
What is Next.js App Router
Next.js App Router is the new routing system introduced in Next.js 13, based on File-system Routing, using the app
directory to define routes. Compared to the traditional pages
directory, App Router provides:
- File-system Routing: Define routes through folder and file structure
- Server Components & Client Components: Better performance and user experience
- Layouts System: Shared UI components and state management
- Loading & Error Handling: Built-in loading states and error handling
- Parallel Routes: Display multiple page contents simultaneously
The 9 Special Files in App Router
Let's learn these 9 special files one by one:
1. page.tsx - Page File
What is page.tsx
page.tsx
defines the unique UI content of a Route Segment, which is the page content users see when visiting a specific path.
Key Points
This is the fundamental file of App Router. Every accessible route requires a page.tsx
file. It directly corresponds to the folder structure, forming the application's routing system.
How to Use
Create a page.tsx
file in any folder within the app
directory:
import { DashboardHeader } from "@/components/dashboard/dashboard-header";
import GettingStarted from "@/components/examples/dashboard";
import { useTranslations } from "next-intl";
export default function DashboardPage() {
const t = useTranslations();
const breadcrumbs = [
{
label: t("menu.application.dashboard.title"),
isCurrentPage: true,
},
];
return (
<>
<DashboardHeader breadcrumbs={breadcrumbs} />
<div className="flex flex-1 flex-col">
<div className="@container/main flex flex-1 flex-col gap-2">
<div className="flex flex-col gap-4 py-4 md:gap-6 md:py-6">
<GettingStarted />
</div>
</div>
</div>
</>
);
}
Key Considerations
- Export Method: Must use
export default
, Named Export not supported - Directory Structure: Avoid mixing
pages/
andapp/
directories in the same project - Data Fetching: Can use
fetch
API andasync/await
directly in components - Component Type: Default to Server Component, add
'use client'
when client interaction is needed
2. layout.tsx - Layout File
What is layout.tsx
layout.tsx
creates shared Layout that wraps page content, defining common UI structure for multiple pages.
Key Points
Layout files allow you to create reusable UI structures, reduce code duplication, and ensure application consistency. Layouts don't re-render during page navigation, providing better performance.
How to Use
Root Layout Example (NextDevKit's root layout):
import type { PropsWithChildren } from "react";
import "./globals.css";
export default function RootLayout({ children }: PropsWithChildren) {
return children;
}
Internationalization Layout Example:
export default async function LocaleLayout({
children,
params,
}: Readonly<{
children: React.ReactNode;
params: Promise<{ locale: Locale }>;
}>) {
const { locale } = await params;
if (!hasLocale(routing.locales, locale)) {
notFound();
}
return (
<AppProviders locale={locale}>
<NextIntlClientProvider>{children}</NextIntlClientProvider>
</AppProviders>
);
}
Key Considerations
- Root Layout: Root layout (
app/layout.tsx
) must define<html>
and<body>
tags - Nested Layout: Be mindful of interactions between nested layouts
- Route Information: Layout cannot access current Route Segment, use
useSelectedLayoutSegment
Hook when needed - Rendering Behavior: Layout doesn't re-render during page navigation, avoid using route-specific data
3. template.tsx - Template File
What is template.tsx
template.tsx
creates reusable Template, similar to layout.tsx
, but re-renders on every navigation.
Key Points
Template files are useful when you need to reset state or trigger animations on every navigation, which Layout cannot do.
How to Use
"use client";
import Link from "next/link";
import { motion } from "framer-motion";
export default function BlogTemplate({
children,
}: {
children: React.ReactNode,
}) {
return (
<div>
<nav>
<ul>
<li>
<Link href="/blog/1">Post 1</Link>
</li>
<li>
<Link href="/blog/2">Post 2</Link>
</li>
</ul>
</nav>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5 }}
>
{children}
</motion.div>
</div>
);
}
Key Considerations
- Use Cases: Only use Template when you specifically need re-rendering on every navigation
- Performance Impact: Overuse may impact performance due to lack of Layout optimizations
- Work with Layout: Template works with Layout, not as a replacement
NextDevKit Usage: NextDevKit doesn't use template.tsx, mostly uses layout.tsx for template functionality.
4. loading.tsx - Loading UI File
What is loading.tsx
loading.tsx
creates Loading UI that displays while Route Segment content is loading.
Key Points
Provides instant user feedback, improves user experience, makes the app feel more responsive, and reduces Perceived Loading Time.
How to Use
export default function Loading() {
return (
<div className="flex items-center justify-center min-h-screen">
<div className="flex flex-col items-center space-y-4">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
<p className="text-gray-600">Loading blog posts...</p>
</div>
</div>
);
}
Key Considerations
- Suspense Boundary: Loading UI is automatically wrapped in React Suspense Boundary
- Layout Shift: Balance showing loading state with avoiding Layout Shift when content loads
- Scope: Loading state is shared across all pages in the same Segment
- Dynamic Routes: Loading state shows even when navigating between different Dynamic Routes
- Hierarchy:
loading.tsx
only handles loading states for its Route Segment and children
5. error.tsx - Error Handling File
What is error.tsx
error.tsx
creates custom Error UI that displays when errors occur in Route Segment.
Key Points
Gracefully handles Runtime Errors, providing better user experience instead of crashing the entire application.
How to Use
'use client';
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
return (
<div className="flex flex-col items-center justify-center min-h-screen">
<div className="text-center">
<h2 className="text-2xl font-bold mb-4">Something went wrong!</h2>
<p className="text-gray-600 mb-4">
{error.message || "An error occurred while loading the blog"}
</p>
<button
onClick={() => reset()}
className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
>
Try again
</button>
</div>
</div>
);
}
Key Considerations
- Client Component: Error component must be Client Component, requires
'use client'
- Error Boundary: Automatically wrapped in React Error Boundary
- Error Scope: Won't catch errors in
layout.tsx
ortemplate.tsx
files in the same Segment - Error Bubbling: Errors bubble up to the nearest parent Error Boundary
6. global-error.tsx - Global Error Handling File
What is global-error.tsx
global-error.tsx
creates Global Error UI that catches and handles errors at the application's root level.
Key Points
Ensures users get meaningful error information even when the highest level errors occur, instead of white screen or browser default error pages.
How to Use
'use client'
export default function GlobalError({
error,
reset,
}: {
error: Error
reset: () => void
}) {
return (
<html>
<body>
<div className="flex flex-col items-center justify-center min-h-screen">
<h2 className="text-2xl font-bold mb-4">System Error!</h2>
<p className="text-gray-600 mb-4">An unexpected error occurred</p>
<button
onClick={() => reset()}
className="px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700"
>
Reload Application
</button>
</div>
</body>
</html>
)
}
Key Considerations
- Environment Behavior: Only works in Production environment, Development environment shows default Error Overlay
- Layout Replacement: Completely replaces Root Layout when error occurs
- HTML Structure: Need to include
<html>
and<body>
tags in component - Complexity Control: Keep component simple to avoid causing errors itself
- Error Handling Hierarchy: As last resort for error handling, handle errors at more granular levels when possible
7. not-found.tsx - 404 Page File
What is not-found.tsx
not-found.tsx
creates custom UI for 404 Not Found errors.
Key Points
Creates branded, helpful pages that guide users back to valid content instead of showing browser default 404 pages.
How to Use
NextDevKit's 404 page example:
import NotFoundIcon from "@/components/icons/not-found";
import { Button } from "@/components/ui/button";
import Link from "next/link";
export default function NotFound() {
return (
<html lang="en">
<body>
<main className="flex flex-col items-center justify-center min-h-screen px-4 py-12">
<div className="text-center mb-16">
<NotFoundIcon className="mx-auto" />
<h1 className="text-4xl font-bold mb-4">404 Not Found</h1>
<p className="text-lg mb-16 text-gray-600 dark:text-gray-400">
The page you're looking for doesn't exist or has been
moved.
</p>
<Link href="/" className="inline-block">
<Button className="px-6 py-4text-lg rounded-md font-semibold cursor-pointer">
Return Home
</Button>
</Link>
</div>
</main>
</body>
</html>
);
}
Key Considerations
- Scope:
not-found.tsx
only handles Not Found scenarios for its folder and subfolders - notFound() Function: When using
notFound()
function, ensurenot-found.tsx
exists in the same Segment or parent Segment - API Routes:
not-found.tsx
doesn't handle 404 errors for API Routes, handle separately
8. default.tsx - Parallel Routes Default File
What is default.tsx
default.tsx
provides Fallback UI for Parallel Routes when no specific match is found.
Key Points
Creates smooth user experience in complex routing scenarios, especially when using Parallel Routes.
How to Use
export default function DefaultSidebar() {
return (
<div className="w-64 bg-gray-100 p-4">
<h2 className="text-xl font-bold mb-4">Default Sidebar</h2>
<p className="text-gray-600">Select a category to see more information.</p>
</div>
);
}
Key Considerations
- Usage Context: Only relevant in Parallel Routes context
- Compatibility: Ensure default component is compatible with all possible states of Parallel Routes
- Rendering Behavior: Displayed on initial render, replaced when more specific route is matched
- Design Considerations: Design to provide meaningful information or functionality when no specific route content exists
9. route.ts - API Routes File
What is route.ts
route.ts
creates API Routes directly in the app folder.
Key Points
Allows you to create Serverless API endpoints alongside frontend code with fine-grained control over HTTP Requests.
How to Use
import { NextResponse } from "next/server";
// Mock data store (use database in real apps)
let todos = [
{ id: 1, title: "Learn Next.js", completed: false },
{ id: 2, title: "Build an app", completed: false },
];
export async function GET() {
return NextResponse.json(todos);
}
export async function POST(request: Request) {
const data = await request.json();
const newTodo = {
id: todos.length + 1,
title: data.title,
completed: false,
};
todos.push(newTodo);
return NextResponse.json(newTodo, { status: 201 });
}
export async function PUT(request: Request) {
const data = await request.json();
const todoIndex = todos.findIndex(todo => todo.id === data.id);
if (todoIndex === -1) {
return NextResponse.json({ error: "Todo not found" }, { status: 404 });
}
todos[todoIndex] = { ...todos[todoIndex], ...data };
return NextResponse.json(todos[todoIndex]);
}
export async function DELETE(request: Request) {
const { searchParams } = new URL(request.url);
const id = parseInt(searchParams.get('id') || '0');
const todoIndex = todos.findIndex(todo => todo.id === id);
if (todoIndex === -1) {
return NextResponse.json({ error: "Todo not found" }, { status: 404 });
}
todos.splice(todoIndex, 1);
return NextResponse.json({ message: "Todo deleted" });
}
Key Considerations
- File Conflicts: Don't place
route.ts
with GET Handler andpage.tsx
in the same folder - Execution Context:
route.ts
always executes on Server, don't use Browser APIs - Static Generation: API Routes defined with
route.ts
aren't statically generated at Build Time
File Structure Example in NextDevKit
Let's see how NextDevKit organizes these special files:
Best Practices and Usage Recommendations
1. Choose the Right File Type
- Basic Pages: Start with
page.tsx
andlayout.tsx
- User Experience: Add
loading.tsx
anderror.tsx
to enhance experience - Special Needs: Add other special files as needed
2. Performance Optimization
- Layout Optimization: Leverage Layout's non-re-rendering characteristic
- Loading States: Use Loading states properly, avoid Layout Shift
- Error Boundary: Implement error handling at appropriate levels
3. Code Organization
- File Structure: Maintain clear file structure
- Component Reuse: Fully utilize Layout and Template reusability
- API Routes Design: Design API Routes structure reasonably
4. Development Workflow
- Progressive Enhancement: Start simple, gradually add complex features
- Test Coverage: Ensure each special file has appropriate tests
- Documentation Maintenance: Keep code documentation up-to-date
Summary
Next.js App Router's 9 special files provide a powerful and flexible foundation for building modern web applications:
- page.tsx - Define page content
- layout.tsx - Create shared Layout
- template.tsx - Template that needs re-rendering
- loading.tsx - Loading UI
- error.tsx - Error Handling UI
- global-error.tsx - Global Error Handling
- not-found.tsx - 404 page
- default.tsx - Parallel Routes default content
- route.ts - API Routes
By understanding and correctly using these files, you can build modern web applications with excellent performance and user experience. NextDevKit provides complete examples of these best practices, helping you get started quickly and build production-grade applications.
Remember, you don't need to use all these files in every project. Start with basic page.tsx
and layout.tsx
, then gradually add other special files as needed. With deeper understanding of these concepts, you'll be able to build more complex and feature-rich Next.js applications.
This content references the blog: Key Considerations for Next.js App Router Files.