The new Next.js app router system gives a handy way to manage routing in an application. NextJS has some conventional or special files that serve some useful purposes. Let's deep dive into learning those files.

Just to mention that Next.js supports 2 types of extensions on files. One is tsx for TypeScript app builders, and another is for .js for vanilla JavaScript builders. In this blog, I will only mention the .tsx extension, but you can definitely use the .js extension if you don't want to use TypeScript.

page.tsx

This is the special file that tells the compiler that 'This file should render a route.' The folder name automatically becomes a route and the page.tsx file returns a React element that tells what to render on that particular route.

app/
  about/
    page.tsx   <-- This is the route for /about
  contact/
    page.tsx   <-- This is the route for /contact

Example Code:

// app/about/page.tsx

const About = () => {
  return (
    <div>About page</div>
  );
};

export default About;

layout.tsx

This special file plays a very powerful role. It defines the structure nested and shared across all the pages. layout.tsx gets all the pages through the children prop and displays them according to the route hit.

It is particularly useful as it gives layouts of the shared piece of UI and doesn't re-render on client-side route changes.

app/
  layout.tsx       <-- Root layout, wraps all pages
  about/
    page.tsx       <-- /about
    layout.tsx     <-- Layout specific to /about and its children
  contact/
    page.tsx       <-- /contact

Example Code:

// app/laout.tsx

import Footer from "../_ui/Footer";
import Header from "../_ui/Header";

const RootLayout = ({ children }: { children: React.ReactElement }) => {
  return (
    <>
      <Header />
      <main>{children}</main>
      <Footer />
    </>
  );
};

export default RootLayout;

loading.tsx

This file is seen when the page or layout (or data for them) is in pending state and coming from external sources. It is super useful for server components. No need to manually select the loading state and use useEffect to make the state change. One cool thing about this file is that all the nested folders can use this file to show a loading state.
NOTE: It is a client component. use the 'use client' directive
Just place this file inside the layout.tsx or page.tsx

Example Code:

// app/blog/loading.tsx

export default function Loading() {
  return <p>⏳ Loading blog post...</p>;
}

error.tsx

This file is useful to catch and display errors if an error is thrown in the layout or page. Similar to loading.tsx, it is also a client component.
This component receives two props, error and reset. The error prop is to get access to the error that occurred in the page or layout. The reset prop is a function that retries to the route.
one key thing to remember is that error.tsx file only catches the error in the nested pages and layouts. It cannot catch the errors of the same folder files (i.e. page.tsx & template.tsx)

app/
  error.tsx    <-- The main error-catching file
  layout.tsx   <-- cannot catch the error of this layout
  about/
    page.tsx   <-- can catch the error of this page

Example Code:

// app/blog/error.tsx

'use client'; 

const Error = ({ error, reset }: { error: Error; reset: () => void })=> {

  return (
    <div>
      <h2>😓 {error.message}</h2>
      <button onClick={reset}>Try again</button>
    </div>
  );
}

export default Error

not-found.tsx

It is pretty much self-explanatory. If a route is not found, this file will show a 404 response. Next.js has its own 404 not found display, but this file helps to customize that UI.
Also, the layout's structure will have an effect here.

Example Code:

// app/blog/not-found.tsx

const NotFound = () => {
  return <h1>🔍 Blog post not found!</h1>;
}

export default NotFound

template.tsx

template.tsx serves almost a similar functionality to layout.tsx. like layout.tsx, it wraps around page.tsx.
The main difference is that layout.tsx is persistent. which means it will not re-render on navigation. whereas the template.tsx re-renders on every navigation. This behavior can affect performance, but it's good for pages that look similar but should reset states on route change.

// app/dashboard/template.tsx

const DashboardTemplate = ({ children }: { children: React.ReactNode }) => {
  return (
    <div>
      <h1>Dashboard Template</h1>
      <div>{children}</div>
    </div>
  );
}

export default DashboardTemplate;

global-error.tsx

As I mentioned earlier, that error.tsx file can only catch the errors of nested folders' pages and layouts. Two questions arise from here,

How to catch the error of root layout and page?
What if the error is unexpected and the error.tsx cannot catch it?

The global-error.tsx helps answer these questions. It acts as a catch-all error boundary of the entire app. If any error bubbles up and is not caught by a route-level error.tsx, then global-error.tsx is shown. Moreover, the root level error is also shown by this file.

const GlobalError = ({ error, reset }: { error: Error; reset: () => void }) => {
  const router = useRouter();
  const reload = () => {
    startTransition(() => {
      router.refresh();
      reset();
    });
  };
   return (
    <html>
      <body>
        <div className="text-red-700">
          {error.message}
          <button onClick={reload}>Try again</button>
        </div>
      </body>
    </html>
  );
};

export default GlobalError;

NOTE: Must wrap in and (since this is app-wide). Global error boundaries works only on production mode.

route.ts

Next.js is a full-stack framework. The route.ts file is used to define custom API endpoints inside the /app directory. Just create this file and export a function in the exact name of an HTTP method (i.e. GET,POST). Supported HTTP methods are GET, POST, PATCH, DELETE, PUT, HEAD, OPTIONS, etc.

NOTE: Each function should be an exporting async function that returns a Response

Example Code:

// app/api/hello/route.ts

//for get request
export async function GET() {
  return new Response("Hello from Next.js API!");
}

//for post request
export async function POST(request: Request) {
  const data = await request.json();
  return Response.json({ message: "data sent!", yourData: data });
}

default.tsx

One of the cool feature of Next.js is the usage of parallel routes. these are the folders that have @ in the beginning. The pages of these folders can be accessed by the layouts through props. If a route is not active(not in the current URL path), then the default.tsx file works as a fallback UI.

app/
 └── layout.tsx
 └── @feed/
     └── default.tsx   <-- this renders when no route in @feed is active
 └── @modal/
     └── default.tsx   <-- fallback UI when no modal route is selected

Example Code:

// app/layout.tsx

//the feed and modal came from parallel routes @feed and @modal
export default function RootLayout({ children, feed, modal }: { 
  children: React.ReactNode;
  feed: React.ReactNode;
  modal: React.ReactNode;
}) {
  return (
    <>
      {children}
      <aside>{feed}</aside>
      <div>{modal}</div>
    </>
  );
}

middleware.ts

This is a file that returns a function named middleware() and this function intercepts incoming requests and runs code before rendering pages. It is useful for authentication, cookies, headers, redirects/rewrites etc.
always put this file in the root directory inside the /src folder subsequent to the /app folder.

Example Code:

import { NextRequest, NextResponse } from "next/server";

export function middleware(request: NextRequest) {
  //this middleware redirects to the v2 api endpoint
  if (request.nextUrl.pathname === "/v1/api") {
    return NextResponse.rewrite(new URL("/v2/api", request.url));
  }
  const response = NextResponse.next(); //to act as default response

  //working with cookies
  const themePreference = request.cookies.get("theme");

  if (!themePreference) {
    response.cookies.set("theme", "dark");
  }

  return response;
}

head.tsx

This special file is optional. It customizes the section (like title, meta tags, etc.) for a specific route or layout. More precisely, it helps to create dynamic content. It only affects the route it's inside.

Example Code:

app/about/head.tsx

export default function Head() {
  return (
    <>
      <title>About Us | Next APP</title>
      <meta name="description" content="Learn more about us." />
    </>
  );
}

Conclusion

Thank you for reading this blog. I hope this was helpful to you. These were just the fundamentals. Next.js provides a fantastic one place to go option for all web dev needs.
Did I miss anything? Write about it in the comments below.

Do you have anything to say about Webdev, Coding stuff? let's have a chat, connect with me on,
LinkedIn
Twitter