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;
Next.js is a full-stack framework. The Example Code: One of the cool feature of Next.js is the usage of parallel routes. these are the folders that have Example Code: 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. Example Code: This special file is optional. It customizes the Example Code: 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. Do you have anything to say about Webdev, Coding stuff? let's have a chat, connect with me on,NOTE: Must wrap in and (since this is app-wide). Global error boundaries works only on production mode.
route.ts
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
// 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
@
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
// 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
always put this file in the root directory inside the /src
folder subsequent to the /app
folder.
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
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.
app/about/head.tsx
export default function Head() {
return (
<>
<title>About Us | Next APP</title>
<meta name="description" content="Learn more about us." />
</>
);
}
Conclusion
Did I miss anything? Write about it in the comments below.
LinkedIn
Twitter