🧏♂️
Next.js を Pages Router から App Router に移行するときにやったこと
Next.js13 で導入された App Router, Route Handler に移行するためにやったことをざっくりまとめます。
前提
- Chakra UI, next-seo を使用している想定
-
next/link, next/image, next/script
については省略します - Next.js v13.4.6
- Chakra UI v2.7.0
App Router(app directory)
_app.ts
と Layout
を layouts.tsx
にする
before
_app.tsx
import { Box, ChakraProvider } from '@chakra-ui/react'
import { NextPage } from 'next'
import { DefaultSeo } from 'next-seo'
import { ReactElement, ReactNode } from 'react'
import Head from 'next/head'
import type { AppProps } from 'next/app'
type NextPageWithLayout<P = object, IP = P> = NextPage<P, IP> & {
getLayout?: (page: ReactElement) => ReactNode
}
type AppPropsWithLayout = AppProps & {
Component: NextPageWithLayout
}
export default function App({ Component }: AppPropsWithLayout) {
const getLayout = Component.getLayout ?? ((page) => page)
return (
<ChakraProvider>
<Head>
<title>title</title>
<meta name="description" content="description" />
<link rel="icon" href="/favicon.ico" />
<meta
name="viewport"
content="width=device-width,initial-scale=1"
/>
</Head>
<DefaultSeo {...seo} />
{getLayout(
<Box as="main">
<Component {...pageProps} />
</Box>
)}
</ChakraProvider>
)
})
after
まずはapp/layout.tsx
を作成
layout.tsx
import { ReactNode } from 'react'
import { Providers } from './providers'
export const metadata = {
title: 'title',
// 以下のように template を使用すると、他のレイアウトで title を設定時に `title | AppName` という形になる
// title: {
// template: '%s | AppName',
// },
description: 'description',
}
export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html lang="ja">
<body>
<main>
<Providers>{children}</Providers>
</main>
</body>
</html>
)
}
export const metadata = {
viewport: {
width: 'device-width',
initialScale: 1,
maximumScale: 1,
},
}
app/providers.tsx
を作成
providers.tsx
'use client'
import { CacheProvider } from '@chakra-ui/next-js'
import { ChakraProvider } from '@chakra-ui/react'
export function Providers({ children }: { children: React.ReactNode }) {
return (
<CacheProvider>
<ChakraProvider>{children}</ChakraProvider>
</CacheProvider>
)
}
favicon, OGP画像, apple-touch-icon を設定
Pages Router では next-seo で設定していましたが、 App Router ではそれぞれ app/icon.*, app/opengraph-image.*, apple-touch-icon.*
というファイルを設置すれば設定完了です
useRouter() の pathname, asPath を書き換える
- import を next/router から next/navigation にする
- import { useRouter } from 'next/router'
+ import { useRouter } from 'next/navigation'
- pathname, asPath を usePathname() にする
- const router = useRouter()
- const path = router.pathname
+ const path = usePathname()
// クエリパラメーターは useSearchParams() を使用して取得する
+ const searchParams = useSearchParams()
+ const search = searchParams.get('search')
// URL -> `/dashboard?search=my-project`
// `search` -> 'my-project'
Route Handler
Basic認証をする middleware用API の場合
before
src/pagees/api/auth.ts
import type { NextApiRequest, NextApiResponse } from 'next'
export default function handler(_: NextApiRequest, res: NextApiResponse) {
res.statusCode = 401
res.setHeader('WWW-authenticate', 'Basic realm="Secure Area"')
res.end('Basic Auth Required')
}
after
src/app/api/auth/route.ts
export async function GET() {
return new Response('Basic Auth Required', {
status: 401,
headers: {
'WWW-authenticate': 'Basic realm="Secure Area"',
},
})
}
Discussion