This article is a living document that will evolve as I explore Next.js through the official tutorials.


Core file extensions in Next.js

These are the extensions you’ll see across all modern Next.js projects:

  • .tsx — React components written in TypeScript. This is the dominant extension in App Router projects. Next.js supports .js as well, but .tsx is the standard for TypeScript users.
  • .ts — Non‑React TypeScript files such as utilities, config, server functions, API route handlers, and instrumentation.
  • .js / .jsx — JavaScript equivalents of the above. Fully supported but less common in TypeScript‑first setups.
  • .css/ .scss — Global styles, module styles, and component‑scoped styles.
  • .md — Markdown content (often used for docs).
  • .mdx — MDX content (Markdown + JSX) when using MDX integration.


Special Next.js files (App Router)

These files use the same .js or .tsx extensions, but their filenames give them special meaning. They live inside the app/ directory and define routing, layouts, and behaviors. Examples include:

  • page.tsx — Defines a route’s UI. A page.tsx file exports a React component that becomes the UI for a route segment. In the App Router, every accessible route requires a page.tsx file.
  • layout.tsx — Wraps pages with shared UI (navigation, layout structure).
  • loading.tsx Route‑level loading UI.
  • error.tsx — Error boundary for a route.
  • template.tsx — Similar to layouts but re-render on navigation. Useful when you want a fresh component instance for each page transition.
  • global-error.tsx — Root-level error boundary (typically app/global-error.tsx) that handles uncaught errors across the entire application.
  • not-found.tsx — 404 UI for a route.
  • route.ts — Defines API endpoints using the App Router’s Route Handlers. These replace the traditional pages/api pattern and allow server-side logic such as GET, POST, PUT, and DELETE handlers. Note: a route segment generally shouldn’t contain both route.ts and page.tsx at the same level.
  • default.tsx — Default UI used for parallel routes in the App Router.
  • middleware.ts — Edge middleware at the project root.
  • These conventions are documented across multiple sources.


Special top‑level project files

These also use standard extensions but have framework‑level meaning:

  • next.config.js — Next.js configuration.
  • instrumentation.ts — OpenTelemetry and instrumentation setup.
  • proxy.ts — Request proxying.
  • .env .env.local — Environment variables.
  • package.json — Dependencies and scripts.


How to think about it

Next.js doesn’t introduce new file extensions. Instead, it uses standard JavaScript or TypeScript files and assigns special meaning to specific filenames placed in specific directories (such as the app/ folder). This convention enables features like file-based routing, layouts, and server components without additional configuration.

When you first encounter Next.js, it can feel like magic—but it's really just thoughtful design meeting practical development needs. This is my journey of discovering what makes Next.js click, written for developers who want to understand the "why" behind the framework.

File-System Routing: The Aha Moment

One of the most immediately satisfying aspects of Next.js is its file-system based routing. It feels instantly familiar, yet more powerful than what many developers have grown accustomed to.

The Connection to Classic Web Development

Remember index.html? If you're a web developer of any experience, you've definitely worked with the convention that a directory containing an index file serves as the route handler. Next.js takes this proven pattern and elevates it:

  • Create a folder structure that mirrors your URL paths
  • Add a page.tsx (or .jsx) file inside each route folder
  • That's it—your route is live

It's conceptually the same as dropping an index.html in a directory and watching your web server handle it, but with modern React components and zero manual routing configuration.

Extensibility Without Compromising Security

What I find especially elegant is how this extends to colocating related code. Inside a route folder, you can add:

  • Utils and helpers for that specific route
  • Components only used by that route
  • Data fetching logic and server actions
  • Styles scoped to that feature

All of this lives safely inside your app directory—there are no security concerns about accidentally exposing private utility files. Next.js determines routes using folder structure and special files. A folder containing a page.tsx file becomes a route segment. Other files inside that folder—such as utilities, components, or helpers—are not automatically exposed as routes. Instead, they act as internal implementation details used by the page or layout components.

This is fundamentally different from serving static files from a public folder. You're grouping functionality, not worrying about what might get accidentally served to a client.

The Developer Experience Win

The beauty here is that you don't need to:

  • Maintain a separate routing configuration file
  • Worry about route registration order or conflicts
  • Keep your folder structure in sync with your routes (they're the same thing)
  • Fear that a utility file will be served as a route

Your code organization becomes your routing. It's delightfully simple.

The Link Component: Smarter Navigation

Next.js Navigation Example animated gif

Now that you understand routing in Next.js, let's talk about how you navigate between those routes. You might think: "Can't I just use the humble <a> element like I always have?" Technically yes, but you'd be leaving performance on the table.

Enter the Link component from next/link. It's Next.js's answer to page navigation, and it's a deceptively simple abstraction that handles a lot of complexity for you:

import Link from 'next/link';

export default function Navigation() {
  return (
    <Link href="/dashboard">
      Go to Dashboard
    </Link>
  );
}

Why Replace <a> with Link?

The difference between a traditional anchor tag and a Next.js Link is subtle in markup but profound in behavior:

  • Automatic Prefetching: When a Link enters the viewport, Next.js can start prefetching the linked route in the background. By the time users click, the page is often already prepared. Prefetching is only enabled in production and can be configured (or disabled) via the prefetch option.
  • Client-Side Navigation: Link enables seamless client-side transitions without full page reloads. Your app stays snappy and preserves its state.
  • Progressive Enhancement: If JavaScript is disabled, a Link still works as a standard anchor tag, gracefully degrading.
  • Unified Routing System: Links are aware of your Next.js routing structure, preventing typos and broken links.

Here's the key insight: <a href="/dashboard"> causes a full page reload, losing any client-side state and waiting for the entire next page to load from scratch. A <Link href="/dashboard"> navigates within your app without reloading, and it's already preparing the next page before you click.

Link in Practice

Consider a dashboard with a sidebar navigation. You might have something like this in your navigation component:

import Link from 'next/link';

export function NavLinks() {
  return (
    <>
      <Link href="/dashboard">Home</Link>
      <Link href="/dashboard/invoices">Invoices</Link>
      <Link href="/dashboard/customers">Customers</Link>
    </>
  );
}

Each of these Links will prefetch their destination, and clicking them will navigate smoothly with client-side rendering. No flash of loading, no loss of scroll position in other parts of your app.

When Link Meets Layouts

The real magic happens when you combine Links with layout components. Your layouts persist across page navigation, so if you have navigation in your root layout, it stays interactive and ready while content below it swaps in and out. This creates the experience of a true single-page application, not a collection of isolated HTML files.

This is where Next.js truly shines: it gives you the simplicity of traditional server-rendered pages (file-system routing, built-in SEO) with the responsiveness of a modern SPA (client-side navigation, prefetching, preserved state).

What's Next?

As I progress through the Next.js tutorials, I'll be exploring:

  • Server Components and the rendering strategy
  • Data fetching patterns and database integration
  • API routes and server actions
  • Deployment and performance optimization
  • Real-world patterns and best practices

Stay tuned as I unlock more "aha moments" along the way.


This blog documents my learning journey. Each section will be refined and expanded as I deepen my understanding.

Liked this post? Leave a comment below or share it with your network!