LogoNEXTDEVKIT Tutorials

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:

app/dashboard/page.tsx
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

  1. Export Method: Must use export default, Named Export not supported
  2. Directory Structure: Avoid mixing pages/ and app/ directories in the same project
  3. Data Fetching: Can use fetch API and async/await directly in components
  4. 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):

src/app/layout.tsx
import type { PropsWithChildren } from "react";
import "./globals.css";

export default function RootLayout({ children }: PropsWithChildren) {
  return children;
}

Internationalization Layout Example:

src/app/[locale]/layout.tsx
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

  1. Root Layout: Root layout (app/layout.tsx) must define <html> and <body> tags
  2. Nested Layout: Be mindful of interactions between nested layouts
  3. Route Information: Layout cannot access current Route Segment, use useSelectedLayoutSegment Hook when needed
  4. 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

app/blog/template.tsx
"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

  1. Use Cases: Only use Template when you specifically need re-rendering on every navigation
  2. Performance Impact: Overuse may impact performance due to lack of Layout optimizations
  3. 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

app/blog/loading.tsx
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

  1. Suspense Boundary: Loading UI is automatically wrapped in React Suspense Boundary
  2. Layout Shift: Balance showing loading state with avoiding Layout Shift when content loads
  3. Scope: Loading state is shared across all pages in the same Segment
  4. Dynamic Routes: Loading state shows even when navigating between different Dynamic Routes
  5. 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

app/blog/error.tsx
'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

  1. Client Component: Error component must be Client Component, requires 'use client'
  2. Error Boundary: Automatically wrapped in React Error Boundary
  3. Error Scope: Won't catch errors in layout.tsx or template.tsx files in the same Segment
  4. 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

app/global-error.tsx
'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

  1. Environment Behavior: Only works in Production environment, Development environment shows default Error Overlay
  2. Layout Replacement: Completely replaces Root Layout when error occurs
  3. HTML Structure: Need to include <html> and <body> tags in component
  4. Complexity Control: Keep component simple to avoid causing errors itself
  5. 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:

src/app/not-found.tsx
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&apos;re looking for doesn&apos;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

  1. Scope: not-found.tsx only handles Not Found scenarios for its folder and subfolders
  2. notFound() Function: When using notFound() function, ensure not-found.tsx exists in the same Segment or parent Segment
  3. 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

app/@sidebar/default.tsx
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

  1. Usage Context: Only relevant in Parallel Routes context
  2. Compatibility: Ensure default component is compatible with all possible states of Parallel Routes
  3. Rendering Behavior: Displayed on initial render, replaced when more specific route is matched
  4. 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

app/api/todos/route.ts
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

  1. File Conflicts: Don't place route.ts with GET Handler and page.tsx in the same folder
  2. Execution Context: route.ts always executes on Server, don't use Browser APIs
  3. 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:

layout.tsx
not-found.tsx
globals.css
layout.tsx
layout.tsx

Best Practices and Usage Recommendations

1. Choose the Right File Type

  • Basic Pages: Start with page.tsx and layout.tsx
  • User Experience: Add loading.tsx and error.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:

  1. page.tsx - Define page content
  2. layout.tsx - Create shared Layout
  3. template.tsx - Template that needs re-rendering
  4. loading.tsx - Loading UI
  5. error.tsx - Error Handling UI
  6. global-error.tsx - Global Error Handling
  7. not-found.tsx - 404 page
  8. default.tsx - Parallel Routes default content
  9. 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.