Next.js, a web development framework with widespread support, introduced a new App Router as the default routing system in version 13.
When working with projects using both Pages Router and App Router, you might find yourself thinking "which syntax was it again?" I certainly do!!
There are significant differences between Pages Router and App Router that cannot be ignored, especially in the core aspects of application development: starting with how routing is defined based on file structure, how dynamic URL segments are handled, the rendering mechanism, and data fetching from APIs.
I'll focus on the points that tend to get mixed up and document the main differences between Pages Router and App Router as a quick reference.
Differences in File Routing
Pages Router: The simplicity of filenames becoming paths directly
The simplicity is that .js, .jsx, .ts, and .tsx files placed in the pages directory have their filenames become URL paths directly.
Example: src/page/about.tsx ⇒ The routing becomes /about.
App Router: Structure with folders and specific file names
App Router enables routing by placing folders and files under src/app.
However, files with the specific file name "page" are treated as the content for the page corresponding to that path.
Example: src/app/about/page.tsx ⇒ The routing becomes /about.
The "special files" that characterize App Router
In addition to page.tsx, App Router has other files with 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 particularly impressed me was not-found.tsx. Simply by preparing this file, it automatically displays when accessing a non-existent URL. I was amazed at how simple it is to implement.
Rendering differences
Components in Pages Router are fundamentally designed to become interactive on the client side, with server-side rendering primarily used for faster initial display and SEO. React hooks like useState and useEffect can be freely used within page components and their child components.
On the other hand, components within the App Router's app/ directory are treated as server components by default unless explicitly specified otherwise. For this reason, useState and useEffect and other React hooks cannot be used. Client-side actions and event handlers like onClick also cannot be used.
To use CSR with the App Router, you write use client.
use client
This is a boundary declaration between Server Component and Client Component. When you declare use client, not only that component but all imported components are treated as Client Components.
Dynamic routing [○○]
Dynamic routing in Next.js is a mechanism for handling "dynamic URLs" where part of the URL changes based on the user or content, such as blog or news article detail pages.
For example, suppose there are 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>
);
}Navigation destinations for each link
For dynamic routing, if you create a file named [slug], you can use the same template for both post/A and post/B.
*The naming in [ ] is arbitrary, so it doesn't have to be slug.
For 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>
);
}
You use params. params.slug contains the [slug] portion of the URL.
Note: Parameter retrieval with params did not previously need to be asynchronous, but as of Next.js 15, it has been changed to be asynchronous. As a result, props needs to be defined as a Promise type.
For 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 router.query.slug contains the value of the [slug] portion of the URL.
API Data Fetching Methods
microCMS, which is actively used in Liberogic columns as well, works well with frameworks like Next.js.
For the App Router:
You can simply use fetch or async await.
However, if you declare use client, the file becomes CSR and you can no longer use async await!
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();
}
Call Destination
export default function Example() {
const data = await getPost()
// ... data を使って表示 ...
}
For Pages Router
Use getStaticProps to fetch data and pass it 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,
},
};
}
Call Destination
export default function Example({ data }) {
// ... data を使って表示 ...
}
Master two routers and get the most out of Next.js!
In my case, the first project I worked on with Next.js used the App Router.
After that, I worked on another project using the Pages Router, and I found myself wondering, "Should I name this file index.tsx or page.tsx again?"
While this article covers only a portion of Next.js, revisiting the differences between App Router and Pages Router has helped me organize my understanding.
Personally, I prefer the App Router's syntax because it's clearer, but I also want to handle Pages Router projects with confidence.
The ideal outcome would be to understand Next.js more deeply and be able to choose the right router for each project and use case!
I focus on frontend development with markup, JavaScript, React, and Next.js. I'm always happy when a site I've worked on goes live successfully! My hobbies are playing guitar, and I love cats and roasted sweet potatoes 🐱🍠
Hiraicchi
Frontend Engineer / Joined 2022