Next.js, a widely adopted web development framework, introduced the new App Router as its default routing system with version 13.
Many developers working on both Pages Router and App Router projects find themselves asking, "Wait, which syntax was it again?" I certainly do!!
Pages Router and App Router differ in ways that cannot be ignored at the core of application development, from how routes are defined based on file structure, to how dynamic URL segments are handled, how rendering works, and how data is fetched from APIs.
Focusing on the points that are most often confused, this post serves as a brief memo covering the key differences between Pages Router and App Router!
Differences in File-Based Routing
Pages Router: The simplicity of file names becoming paths
The simplicity lies in how file names of .js, .jsx, .ts, and .tsx files placed under the pages directory become the URL paths as they are.
For example: src/page/about.tsx → the route becomes /about.
App Router: Structured by folders and specific file names
With the App Router, you can set up routing by placing folders and files under src/app.
However, a file named specifically "page" used for routing is treated as the page content corresponding to that path.
Example: src/app/about/page.tsx → The route becomes /about.
The "Special Files" That Define the App Router
Beyond page.tsx, the App Router also includes other files that have special meaning.
app/
├── page.tsx --> ページの中身。これが一番よく使うファイル
├── route.tsx --> APIの定義(page.jsと共存不可)
├── layout.tsx --> 共通の見た目
├── loading.tsx --> 読み込み中の画面
├── error.tsx --> エラー時の画面
├── global-error.tsx --> グローバルエラー画面
├── templete.tsx --> 共通の見た目(リセットされるレイアウト)
├── default.tsx --> デフォルトの画面
└── not-found.tsx --> notFound関数がスローされたときの画面
What surprised me most was not-found.tsx. Simply by creating this page, it is automatically displayed when a nonexistent URL is accessed. I was impressed that it could be implemented so easily...
Differences in Rendering
In the Pages Router, components are basically designed to be interactive on the client side, and server-side rendering is mainly used to speed up initial page display and for SEO. React hooks such as useState and useEffect can be freely used within page components and their child components.
In contrast, components inside app/ in the App Router are treated as Server Components by default unless explicitly specified. Therefore, useState and useEffect and other React hooks cannot be used. Client-side actions and event handlers such as onClick also cannot be used.
To make a component CSR in the App Router, you write use client.
use client
This is the boundary declaration between Server Components and Client Components. Once you declare use client, not only that component but also everything it imports is treated as a Client Component.
Dynamic Routing [○○]
In Next.js, dynamic routing is the mechanism for handling "dynamic URLs" where part of the URL changes depending on the user or content, such as article detail pages on a blog or news site.
For example, suppose you have links that navigate to Article A and Article B.
import Link from "next/link";
const linkStyle=`ext-lg font-bold border-b border-primary px-1`
export default function page() {
return (
<div>
<div className="flex gap-6">
<Link href="/post/A" className={linkStyle}>記事Aへのリンク</Link>
<Link href="/post/B" className={linkStyle}>記事Bへのリンク</Link>
</div>
</div>
);
}The destination of each link.
To use dynamic routing, create a file named [slug], and the same template can be used for both post/A and post/B.
Note: The naming inside [ ] is up to you, so it does not have to be slug.
With the App Router
/app/post/[slug]/page.tsx
export default aysinc function PostPage({ params }: { params: Promise<{ slug: string }>) {
const {slug}= await params;
return (
<div>
<h1>記事: {slug}</h1>
</div>
);
}
Use params. The [slug] part of the URL is available as params.slug.
Note: Previously, reading params did not need to be asynchronous, but starting with Next.js 15 it has been changed to be asynchronous. Therefore, it seems you need to define props as a Promise type.
With the Pages Router
/pages/post/[slug].tsx
import { useRouter } from 'next/router';
export default function PostPage() {
const router = useRouter();
const { slug } = router.query;
return (
<div>
<h1>記事: {slug}</h1>
</div>
);
}
Use useRouter. The value of the [slug] part of the URL is stored in router.query.slug.
How to Fetch Data from an API
microCMS, which is powering this Liberogic column, works well with frameworks like Next.js.
With the App Router
Simply using fetch with await in an async function is fine.
However, files that declare use client become CSR, so await in an async function can no longer be used!
async function getPost() {
const res = await fetch(`https://.../api/data/`);
// エラーハンドリング例
if (!res.ok) {
throw new Error(`Failed to fetch post: ${res.status}`);
}
return res.json();
}
Caller
export default function Example() {
const data = await getPost()
// ... data を使って表示 ...
}
For Pages Router
Use getStaticProps to fetch data and pass the retrieved data to the page component as props.
export async function getServerSideProps(context) {
// context.req, context.res などにアクセス可能
const res = await fetch(`https://.../api/data/`, {
headers: { Cookie: context.req.headers.cookie } // 例: Cookieを渡す
});
const data = await res.json();
return {
props: {
data,
},
};
}
Caller
export default function Example({ data }) {
// ... data を使って表示 ...
}
Understand both routers and master Next.js!
In my case, the first project where I used Next.js was with the App Router.
Later, while working on a different project with the Pages Router, I found myself wondering, "Was it index.tsx or page.tsx for creating a new page again??"
This article covers only a small part of Next.js, but researching the differences between the App Router and Pages Router again helped me organize my own understanding.
Personally, I like the App Router's syntax because it's easier to understand, but I want to be able to handle Pages Router projects confidently as well.
The ideal is to understand Next.js more deeply so I can choose the right router depending on the project or deliverable!
I work mainly on markup and frontend development using JavaScript, React, and Next.js. I'm always happy when a site I've been involved in launches successfully! My hobbies are playing the guitar. I love cats and roasted sweet potatoes 🐱🍠
Hirocchi
Frontend Engineer / Joined in 2022