本ページで紹介するサンプルコードは下記からダウンロードできます。
下記を参考にインストールします。
下記のものが必要です。
「自動インストール方式」と「手動インストール方式」がありますが、ここではシンプルな手動インストール方式を説明します。
$ mkdir myapp $ cd myapp $ npm install next@latest react@latest react-dom@latest typescript@latest @types/react @types/node $ mkdir app
./package.json に下記を追加します。これで npm run dev や npm run build などのコマンドを使用できるようになります。
{ "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint" }, "dependencies": { :
./app/layout.tsx を作成します。レイアウトはページの外枠を定義します。
export default function RootLayout({ children, }: { children: React.ReactNode }) { return ( <html lang="ja"> <body>{children}</body> </html> ) }
./app/page.tsx を作成します。レイアウトの中に埋め込むページを定義します。
export default function Page() { return <h1>Top Page</h1> }
開発用簡易サーバを起動します。
$ npm run dev
ブラウザから http://{サーバアドレス}:3000/ にアクセスして「Top Page」が表示されれば成功です。
本番サーバを起動するには build してから start します。
$ npm run build $ npm run start
Next.js のおおまかなフォルダ構成は下記となります。
myapp/ : プロジェクト名。好きな名前を命名。 app/ : App Routerを使用する場合のメインフォルダ pages/ : Pages Routerを使用する場合のメインフォルダ(今回は使用しません) public/ : スタティックファイルを格納するフォルダ(後述します) src/ : オプショナルなアプリケーションソースを格納するフォルダ(今回は使用しません)
myapp の下には必要に応じて下記などのファイルを置いていきます。詳細は Next.js の マニュアル を参照してください。
next.config.js Configuration file for Next.js package.json Project dependencies and scripts instrumentation.ts OpenTelemetry and Instrumentation file middleware.ts Next.js request middleware .env Environment variables .env.local Local environment variables .env.production Production environment variables .env.development Development environment variables .eslintrc.json Configuration file for ESLint .gitignore Git files and folders to ignore next-env.d.ts TypeScript declaration file for Next.js tsconfig.json Configuration file for TypeScript jsconfig.json Configuration file for JavaScript
./myapp/app の下には必要に応じて下記などのファイルを作成していきます。拡張子は JavaScript(.js), JavaScript XML(.jsx), TypeScript(.tsx) を意味します。本書では主に .jsx を使用します。
layout .js .jsx .tsx Layout template .js .jsx .tsx Re-rendered layout page .js .jsx .tsx Page loading .js .jsx .tsx Loading UI not-found .js .jsx .tsx Not found UI error .js .jsx .tsx Error UI global-error .js .jsx .tsx Global error UI route .js .ts API endpoint default .js .jsx .tsx Parallel route fallback page
Next.js では App Router と Pages Router の二つの方式をサポートしており、どちらを選ぶかによって開発方式が大きく異なります。./app を使用するのが App Router で、./pages を使用するのが Pages Router です。App Router は Next.js v13.4 から導入された新しい開発方式で、Pages Router の進化版とも言えることから、今後は App Router 方式が主流になると思われます。本書では App Router についてのみ説明していきます。
./app/{パス名}/page.tsx を作成すると、ブラウザから http://{サーバ名}/{パス名} でアクセスできます。./app/{パス名1}/{パス名2}/page.tsx を作成すると、ブラウザから http://{サーバ名}/{パス名1}/{パス名2} でアクセスできます。試しに ./app/page-test フォルダを作成して、配下に ./app/page-test/page.tsx を作成してみます。
export default function Page() { return <h1>Page Test</h1> }
ブラウザから http://~/page-test にアクセスして Page Test が表示されれば成功です。
./app/layout.tsx で指定したレイアウトはすべてのページに適用されます。./app/xxx/layout.tsx で指定したレイアウトは、xxx 配下のすべてのページに適用されます。./app/layout-test フォルダを作成して layout.tsx と page.tsx を作成してみます。
export default function Layout({ children, }: { children: React.ReactNode }) { return ( <> <hr /> <div>{children}</div> <hr /> </> ) }
export default function Page() { return <h1>Layout Test</h1> }
http://~/layout-test を開いて Layout Test の上下に線が引かれていれば成功です。これは、http://~/layout-test/page1/page2 のように階層が深くなっても反映されます。
テンプレート(Template)はレイアウト(Layout)とほぼ同じ機能を持ちます。レイアウトは画面が遷移してもDOM要素やステートが維持されるのに対し、テンプレートは遷移の度に新しいDOM要素が作成され、ステートがリセットされます。画面遷移時にレイアウトに埋め込んだフォームを初期化したい場合やアニメーションを表示したい場合など、レイアウトの代わりにテンプレートが使用されます。./app/template-test フォルダを作成して template.tsx と page.tsx を作成してみます。
export default function Template({ children }: { children: React.ReactNode }) { return <div>{children}</div> }
export default function Page() { return <h1>Template Test</h1> }
http://~/template-test にアクセスして Template Test が表示されれば成功です。
時間のかかるコンポーネントを表示する際に非同期で呼び出しを行い、待っている間ローディングアイコン等を表示することができます。まず、./app/loading-test フォルダを作成し、ローディング中であることを示す loading.tsx 部品を作成します。
export default function Loading() { return <div>Loading...</div> }
次に時間のかかるコンポーネント WaitComponent を作成します。
function aWait() { return new Promise(callback => { setTimeout(callback, 3000) }) } export default async function WaitComponent() { await aWait() return <div>Finished!</div> }
これらを呼び出す page.tsx を作成します。時間のかかるコンポーネントを <Suspense>~</Suspense> で囲み、fallback に Loading を指定します。<Suspense> を使用することにより、時間のかかる処理が終わるまでページ全体のレンダリングが待たされることなく、個々の <Suspense> を個別に非同期に呼び出すことができます。また、読み込み中は fallback で指定したローディング部品を表示します。
import { Suspense } from 'react' import Loading from './loading' import WaitComponent from './wait' export default function Page() { return ( <div> <h1>Loading Test</h1> <Suspense fallback={<Loading />}> <WaitComponent /> </Suspense> </div> ) }
http://~/loading-test にアクセスすると数秒間 Loading... が表示されて Finished! に変われば成功です。
./app 直下に not-found.tsx を作成すると、404 Not Found エラーが発生した際のページを作成することができます。
export default function NotFound() { return <h1>Not Found</h1> }
存在しないページにアクセスして Not Found が表示されれば成功です。
下記のようなエラーページを ./app/error-test/error.tsx として用意します。
'use client' import { useEffect } from 'react' export default function Error({error, reset,}: { error: Error & { digest?: string } reset: () => void }) { useEffect(() => {console.error(error)}, [error]) return ( <> <h1>Error</h1> <button onClick={() => reset()}>Try again</button> </> ) }
./app/error-test/page.tsx の中でエラーを発生させてみます。
export default function Page({params, searchParams}) { const error = searchParams?.error if (error) { throw new Error('ERROR!!!!!') } return <h1>Error Test</h1> }
http://~/error-test?error=1 にアクセスしてエラーページが表示されれば成功です。
error.tsx では page.tsx で発生したエラーを捕捉することはできますが、layout.tsx や template.tsx で発生したエラーを捕捉することができません。これらを捕捉するには global-error.tsx を作成します。
'use client' export default function GlobalError({ error, reset, }: { error: Error & { digest?: string } reset: () => void }) { return ( <html> <body> <h2>Global Error</h2> <div>{error.message}</div> <button onClick={() => reset()}>Try again</button> </body> </html> ) }
グローバルのCSSを設定するには下記の様にします。
body { background-color: #ddf; }
import './global.css' :
クラス名を指定したCSSを適用するには下記の様にします。
.test1 { color: red; }
.test2 { color: blue; }
import style1 from './test1.module.css' import style2 from './test2.module.css' export default function Page() { return ( <> <h1>CSS Test</h1> <div className={style1.test1}>Test1 message.</div> <div className={style2.test2}>Test2 message.</div> </> ) }
http://~/css-test を開いて背景が薄青く、赤いメッセージと青いメッセージが表示されていれば成功です。
Tailwind CSS を使用することもできます。Tailwind CSS を使用する場合は、すてのデフォルトスタイルがキャンセルされるため、すべての部品のスタイルを再構築する必要があります。
$ npm install -D tailwindcss postcss autoprefixer $ npx tailwindcss init -p
./tailwind.config.js に下記を追加します。
module.exports = { content: [ './app/**/*.{js,ts,jsx,tsx,mdx}' ], theme: { extend: {}, }, plugins: [], }
./app/tailwind-test/style.css を作成します。
@tailwind base; @tailwind components; @tailwind utilities;
これを ./app/tailwind-test/page.tsx から読み込みます。
import './style.css' export default function Page() { return <h1 className="text-6xl font-bold text-blue-600">Tailwind Test</h1> }
http://~/tailwind-test を開いて Tailwind Test の文字が青くなっていれば成功です。反映されない場合は簡易サーバ(npm run dev)を再起動してください。
スタイルシートとして Sass を使用することもできます。
$ npm install --save-dev sass
$body-background-color: #dfd; body { background-color: $body-background-color; }
import './style.scss' export default function Page() { return <h1>Sass Test</h1> }
http://~/sass-test にアクセスして背景が緑になったら成功です。
./app 配下にディレクトリを作成しても page.tsx や route.ts を配置しない限りはアクセスされることはありませんが、アンダーバー(_)で始まるフォルダはプライベートフォルダと認識され、たとえ page.tsx や route.ts が配置されてもアクセスされることはありません。
./app/
xxx/page.tsx
yyy/route.ts
_lib/page.tsx # アクセスされない
./app/_lib/page.tsx を作成します。
export default function Page() { return <h1>Private Folder Test</h1> }
http://~/_lib にアクセスしても page.tsx は表示されず、Not Found になることが確認できます。
./app/(...) の名前を持つフォルダはルートグループを形成します。例えば下記のフォルダ構成では、member1~member3 は一つの group1 グループに含まれますが、http://~/member1, http://~/member2, http://~/member3 としてアクセスすることができます。グループはレイアウトやテンプレートを共有することができます。
./app/ (gorup1)/ layout.tsz member1/page.tsx member2/page.tsx member3/page.tsx
まず、./app/(group1) フォルダを作成します。( や ) がシェルのメタ文字として解釈されないようにパス名を '...' で囲みます。
$ mkdir './app/(group1)'
export default function Layout({ children, }: { children: React.ReactNode }) { return ( <> <h1>(group1)</h1> <div>{children}</div> </> ) }
export default function Page() { return <h2>Member 1</h2> }
http://~/member1 にアクセスして (group1) と Member 1 が表示されれば成功です。
http://~/blog/{slug} の様に URL でブログIDなどのパラメータを受け取りたい場合があります。./app/blog/[slug] という名前のフォルダを作成します。
$ mkdir -p './app/blog/[slug]'
./app/blog/[slug]/page.tsx ファイルを作成します。
export default function Page({ params }: { params: { slug: string } }) { return <h1>Blog: {params.slug}</h1> }
http://~/blog/123 にアクセスして Blog: 123 と表示されれば成功です。
http://~/blog2/{slug1}/{slug2}/{slug3} のように階層的なパラメータを受け取るには、./app/blog2/[...slugs]/page.tsx ファイルを作成します。
$ mkdir -p './app/blog2/[...slugs]'
export default function Page({ params }: { params: { slugs: string[] } }) { return <h1>Blog: {params.slugs.join(', ')}</h1> }
http://~/blog2/123/456 にアクセスして Blog: 123, 456 と表示されれば成功です。
上記の例では /blog2/123 や /blog2/456 にはアクセスできても /blog2 にアクセスすると 404 Not Found となってしまいます。/blog2 にもアクセスできるようにするには、[[...slugs]] の様に括弧を二重にします。下記の例では /blog3 にアクセスすると (None) を表示します。
$ mkdir -p './app/blog3/[[...slugs]]'
export default function Page({ params }: { params: { slugs: string[] } }) { if (params.slugs) { return <h1>Blog: {params.slugs.join(', ')}</h1> } else { return <h1>Blog: (None)</h1> } }
http://~/blog3 にアクセスして Blog: (Node) が、http://~/blog3/123/456 にアクセスして Blog: 123, 456 と表示されれば成功です。
http://~/blog4/{カテゴリ}/{slug} を受け取るには ./app/blog4/[category]/[slug]/page.tsx ファイルを作成します。
$ mkdir -p './app/blog4/[category]/[slug]'
export default function Page({ params }: { params: { category: string, slug: string } }) { return <h1>Blog: {params.category} / {params.slug}</h1> }
http://~/blog4/movie/123 にアクセスして Blog: movie / 123 と表示されれば成功です。
Next.js 13 からサポートされた機能で、ひとつのレイアウトの中で複数の子ノードを並列に描画することができます。時間のかかる子ノードを複数同時に描画したい場合、子ノードを @ で始まるフォルダ名で作成します。
./app/ parallel-test/ layout.tsx page.tsx @node1/page.tsx @node2/page.tsx
export default function Layout(props) { return ( <div> <div>{props.children}</div> <div>{props.node1}</div> <div>{props.node2}</div> </div> ) }
export default function Page() { return <h1>Parallel Test</h1> }
export default function Page() { return <h2>Node1</h2> }
export default function Page() { return <h2>Node2</h2> }
http://~/parallel-test にアクセスして Parallel Test, Node1, Node2 が表示されていれば成功です。
Next.js 13.3 からサポートされた機能で、現在のページの上に、別のページをモーダルで表示することができます。写真をクリックするとその写真がモーダルで拡大表示されるケースなどで利用されます。少々コード量が多いので本家が提供しているサンプルを紹介します。
ソースサンプルは下記のファイルから構成されます。
./app/layout.tsx ./app/page.tsx ./app/default.tsx ./app/global.css ./app/photos/[id]/page.tsx ./app/@modal/default.tsx ./app/@modal/(.)photos/[id]/modal.tsx ./app/@modal/(.)photos/[id]/page.tsx
(.) などは下記の意味を持ちます。
(.) 同一階層のセグメントにマッチ (..) ひとつ上のディレクトリのセグメントにマッチ (..)(..) ふたつ上のディレクトリのセグメントにマッチ (...) ルート(./app)ディレクトリにマッチ
React なので部品をコンポーネント化することができます。テストメッセージを表示する部品を作成してみます。ここらへんの詳細は React を参照してください。
export default function TestMessage() { return <div>Test message.</div> }
import TestMessage from './testMessage' export default function Page() { return ( <> <h1>Component Test</h1> <TestMessage /> </> ) }
http://~/component-test を開いて Test message. と表示されていれば成功です。
Page Test へのリンクを張ってみます。<a> で張る方法と <Link> で張る方法があります。
import Link from 'next/link' export default function Page() { return ( <> <h1>Link Test</h1> <ul> <li><a href="page-test">Page Test</a></li> <li><Link href="page-test">Page Test</Link></li> </ul> </> ) }
http://~/link-test を表示して Page Test へのリンクが2つ表示されていれば成功です。<a> で張った場合は遷移時に画面の再読込が行われるのに対して、<Link> で張った場合は再読み込み無しに描画されます。
useRouter() を用いることで、ユーザクリック時ではなく、プログラム中から任意のタイミングでリンク先にジャンプすることができます。
'use client' import { useRouter } from 'next/navigation' export default function Page() { const router = useRouter() return ( <div> <h1>useRouter Test</h1> <button onClick={() => router.push('/')}>Top</button> </div> ) }
http://~/use-route-test にアクセスしてボタンをクリックすると Top ページに遷移すれば成功です。
Googleフォントを利用することができます。Google の Roboto フォントを使用する例を下記に示します。指定可能なパラメータは Font を参照してください。
import { Roboto } from 'next/font/google' const Roboto900 = Roboto({ weight:'900', preload:false }) export default function Page() { return ( <> <h1>Google Roboto Font Test</h1> <h1 className={Roboto900.className}>Google Roboto Font Test</h1> </> ) }
http://~/font-test にアクセスしてフォントの異なるタイトルが表示されていれば成功です。
イメージコンポーネント <Image> を使用することができます。指定可能なパラメータは <Image> を参照してください。<img> とあまり変わらないように見えますが、配置されるサイズに応じて適切なサイズにトリミングしてくれます。
import Image from 'next/image' export default function Page() { return ( <> <h1>Image Test</h1> <Image src="/image/sample.png" width={58} height={40} alt="Sample Image" /> </> ) }
./public/image/sample.png に任意の PNGファイルを配置してください。http://~/image-test にアクセスすると画像が表示されれば成功です。
./components/button.tsx 部品を作成し、これを ./app/*/page.tsx から import する場合、'../../components/button' と記述する必要がありますが、tsconfig.json または jsconfig.json に baseUrl を指定すると、'components/button' として参照できるようになります。npm run dev を再起動する必要があります。
{ "compilerOptions": { "baseUrl": ".", :
export default function Button() { return <button>Click me</button> }
import Button from 'components/button' export default function Page() { return ( <> <h1>Absolute Import Test</h1> <Button /> </> ) }
コンフィグを書き換えたので npm run dev を再起動した後、http://~/absolute-import-test にアクセスし、Click me ボタンが表示されれば成功です。
./tsconfig.json または ./jsconfig.json に paths を指定するとパス名のエイリアスを使用することができるようになります。
{ "compilerOptions": { "baseUrl": ".", "paths": { "@alias1/*": ["components/*"] }, :
import Button from '@alias1/button' export default function Page() { return ( <> <h1>Module Alias Test</h1> <Button /> </> ) }
コンフィグを書き換えたので npm run dev を再起動した後、http://~/module-alias-test にアクセスし、Click me ボタンが表示されれば成功です。
fetch()を用いて他のWebにアクセスすることができます。fetch() に関する詳細は fetch() を参照してください。
export default async function Page() { const response = await fetch('https://www.tohoho-web.com/cgi/client-ip-address.cgi') const body = await response.text() return ( <> <h1>Fetch Test</h1> <div>{body}</div> </> ) }
http://~/fetch-test にアクセスし、IPアドレスが表示されていれば成功です。
Next.js では簡単にサーバサイドの実行とクライアントサイドの実行を切り替えることができます。デフォルトはサーバサイドです。
export default function Page() { console.log('Server Side Test') return <h1>Server Side Test</h1> }
http://~/server-side-test にアクセスするとサーバ側のコンソールに Server Side Test が表示されます。
'use client' を指定するとクライアントサイドになります。
'use client' export default function Page() { console.log('Client Side Test') return <h1>Client Side Test</h1> }
http://~/client-side-test にアクセスするとクライアント側の開発ツールのコンソールに Client Side Test が表示されます。
また、下記の様に 'use server' を指定したサーバサイドアクションを、クライアント側のフォームから簡単に呼び出すことができます。下記の例ではクライアントのフォームに入力したメッセージがサーバに送られ、サーバのコンソールに出力されます。
export default function Page() { async function action_test(form) { 'use server' const message = form.get('message') console.log({message}) } return ( <> <h1>Server Action Test</h1> <form action={action_test}> <input type="text" name="message" /> <button type="submit">OK</button> </form> </> ) }
http://~/server-action-test にアクセスしてフォームに入力して [OK] を押すと、サーバ側にメッセージが表示されれば成功です。
フォーム部品にデフォルト値を設定するには value="..." ではなく defaultValue="..." を指定します。
<input type="text" name="message" defaultValue="xxx" />
サーバで処理した結果を返すには useFormState() を用います。クライアントでフォームを表示し、サブミット時にサーバアクションを呼び出します。今回は常に成功するメソッドとしていますが、結果を Form State を経由して画面に返却しています。
'use server' export async function loginAction(state: any, formData: FormData) { const user_id = formData.get('user_id') const password = formData.get('password') console.log({user_id, password}) return { message: 'OK', } }
'use client' import { useFormState } from 'react-dom' import { loginAction } from './login' const initialState = { message: '' } export default function Page() { const [state, formAction] = useFormState(loginAction, initialState) return ( <> <h1>Login Test</h1> <form action={formAction}> <div><input type="text" name="user_id" placeholder="User ID" /></div> <div><input type="password" name="password" placeholder="Password" /></div> <button type="submit">Login</button> <p>{state?.message}</p> </form> </> ) }
http://~/login-test にアクセスして、User ID と Password に何かを入力して Login ボタンを押し、OK と表示されれば成功です。
route.ts は REST-API を作成するのに便利な機能です。下記の内容で ./app/api/books/route.ts を作成します。
var books = [ { title: '吾輩は猫である', author: '夏目漱石' }, ] export async function GET(request: Request) { return Response.json({ books: books }, { status: 200 }) } export async function POST(request: Request) { var book = await request.json() books.push({ title: book.title, author: book.author }) return Response.json({ result: 'OK' }, { status: 200 }) }
curl でこれらを呼び出してみます。
$ curl http://localhost:3000/api/books {"books":[{"title":"吾輩は猫である","author":"夏目漱石"}]} $ curl -X POST http://localhost:3000/api/books -d '{"title":"銀河鉄道の夜","author":"宮沢賢治"}' {"result":"OK"} $ curl http://localhost:3000/api/books {"books":[{"title":"吾輩は猫である","author":"夏目漱石"},{"title":"銀河鉄道の夜","author":"宮沢賢治"}]}
上記の例では入出力に JavaScript 標準の Response/Response を使用していますが、Next.js で拡張した NextRequest/NextResponse を使用することもできます。
リクエストヘッダを読み込むには headers() を使用します。レスポンスヘッダを指定するには Response() の第2引数オブジェクトに headers を指定します。詳細は headers を参照してください。
import { headers } from 'next/headers' export async function GET(request: Request) { const user_agent = headers().get('User-Agent') return new Response('OK', {status: 200, headers: {'X-Test-Header': user_agent}}) }
curl で http://~/api/header-test を呼び出して、X-Test-Header が返却されれば成功です。
$ curl -i http://localhost:3000/api/header-test HTTP/1.1 200 OK x-test-header: curl/7.76.1 :
Cookie を参照するには cookies() から get() して value を参照します。設定するには Response() の第2引数オブジェクトの headers に Set-Cookie を指定します。詳細は cookies を参照してください。
import { cookies } from 'next/headers' export async function GET(request: Request) { var my_cookie = cookies().get('my_cookie') var my_cookie_value = my_cookie ? my_cookie.value : 'DUMMY_DATA' return new Response('OK', {status: 200, headers: {'Set-Cookie': `my_cookie=${my_cookie_value}`}}) }
http://~/api/cookie-test を呼び出して Set-Cookie が設定されていれば成功です。
$ curl -i -H "Cookie: my_cookie=AAA" http://localhost:3000/api/cookie-test HTTP/1.1 200 OK set-cookie: my_cookie=AAA :
https://~/~?query=hello のようなURL中のクエリパラメータを得るには NextRequest.nextUrl.searchParams から get() します。
import { type NextRequest } from 'next/server' export function GET(request: NextRequest) { const param1 = request.nextUrl.searchParams.get('param1') console.log({param1}) return Response.json({ result: 'OK' }) }
http://~/api/query-test?param1=Hello にアクセスしてサーバ側のコンソールに { param1: 'Hello' } と表示されれば成功です。
<form method="POST"> で送信されるフォームデータを参照するには formData() を await して get() します。
export async function POST(request: Request) { const formData = await request.formData() const name = formData.get('name') const email = formData.get('email') return Response.json({ name, email }) }
http://~/api/form-test を下記の様に呼び出して下記の様に表示されれば成功です。
$ curl -X POST http://localhost:3000/api/form-test -d 'name=Yamada&email=yamada@example.com' {"name":"Yamada","email":"yamada@example.com"}
画面とAPIサーバが異なるオリジンの場合、CORS を設定する必要があります。 ヘッダで Access-Control-Allow-* を返却します。
export async function GET(request: Request) { return Response.json({ result: 'OK' }, { status: 200, headers: { 'Access-Control-Allow-Origin': 'https://www.example.com/', 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type, Authorization', }, }) }
https://~/~?param1=hello のようなURL中のクエリパラメータを得るには searchParams を参照します。
import { useSearchParams } from 'next/navigation' export default function Page({params, searchParams}) { const param1 = searchParams?.param1 return ( <> <h1>Query Test</h1> <div>{param1}</div> </> ) }
http://~/query-test?param1=Hello にアクセスして Hello が表示されれば成功です。
リダイレクトするには redirect() を使用します。
import { redirect } from 'next/navigation' export default function Page() { redirect('https://www.google.com/') }
http://~/redirect-test にアクセスして Google にリダイレクトされれば成功です。
メタデータとしてタイトルを設定してみます。
import './global.css' import { Metadata } from 'next' export const metadata: Metadata = { title: 'NEXT.JS', } export default function RootLayout({ :
http://~/ を開いてブラウザのタブに NEXT.JS と表示されていれば成功です。
./middleware.ts には、Next.js が page.tsx を呼び出す前に行う共通処理を記述できます。リクエストのロギングや認証チェックなどに利用できます。
import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' export function middleware(request: NextRequest) { console.log(request.url) return NextResponse.redirect(new URL('/', request.url)) } export const config = { matcher: '/middleware-test/:path*', }
http://~/middleware-test にアクセスして / にリダイレクトすれば成功です。
config.matcher にはミドルウェアを適用するパスを指定します。複数指定や正規表現も使用できます。:path* は任意のパス名を示します。
export const config = { matcher: ['/mid-test1/:path*', '/(mid-test2|mid-test3)/:path*'], }
redirect() は別のパスにリダイレクトします。rewrite() はURL表示はそのままで実行内容のみを転送します。
export function middleware(request: NextRequest) { if (request.nextUrl.pathname.startsWith('/mid-test1')) { return NextResponse.redirect(new URL('/', request.url)) } if (request.nextUrl.pathname.startsWith('/mid-test2')) { return NextResponse.rewrite(new URL('/', request.url)) } }
http://~/mid-test1 にアクセスするとトップページにリダイレクトします。http://~/mid-test2 にアクセスすると URL はそのままでトップページの内容を表示します。