Next.jsの15.5でstableになった「Typed Routes」を使って<Link href='/hoge'>を型安全に使う
はじめに
今日はNext.jsの15.5がリリースされて「Typed Routes」がstableになったので、自分のサービスにも適用していきたいと思います。
この前作った「this is OOO」で使ってみます。
まずはNext.js関連のライブラリを最新バージョンに更新します。
pnpm install next@latest react@latest react-dom@latest
このプロジェクトはOpenNextで動かしてるので@opennextjs/cloudflareも関係しているため、これも更新して、pnpm run previewで動くか確認します。
ログ
pnpm run preview
> thisisooo@0.1.0 preview /Users/shinaps/dev/thisisooo
> opennextjs-cloudflare build && opennextjs-cloudflare preview --env development
┌─────────────────────────────┐
│ OpenNext — Cloudflare build │
└─────────────────────────────┘
App directory: /Users/shinaps/dev/thisisooo
Next.js version : 15.5.0
@opennextjs/cloudflare version: 1.6.5
@opennextjs/aws version: 3.7.4
┌─────────────────────────────────┐
│ OpenNext — Building Next.js app │
└─────────────────────────────────┘
> thisisooo@0.1.0 build /Users/shinaps/dev/thisisooo
> next build
▲ Next.js 15.5.0
- Environments: .env.production, .env
- Experiments (use with caution):
· serverActions
Using vars defined in .dev.vars
Creating an optimized production build ...
Using vars defined in .dev.vars
Using vars defined in .dev.vars
Using vars defined in .dev.vars
✓ Compiled successfully in 8.6s
✓ Linting and checking validity of types
✓ Collecting page data
Using vars defined in .dev.vars
✓ Generating static pages (13/13)
✓ Collecting build traces
✓ Finalizing page optimization
Route (app) Size First Load JS
┌ ƒ / 3.07 kB 120 kB
├ ○ /_not-found 993 B 103 kB
├ ƒ /api/auth/[...all] 131 B 102 kB
├ ○ /apple-icon.jpeg 0 B 0 B
├ ƒ /articles 167 B 105 kB
├ ƒ /articles/[articleId] 68.1 kB 182 kB
├ ƒ /articles/[articleId]/opengraph-image 131 B 102 kB
├ ƒ /articles/[articleId]/twitter-image 131 B 102 kB
├ ○ /icon.jpeg 0 B 0 B
├ ƒ /interviews 4.37 kB 121 kB
├ ƒ /interviews/[interviewId] 6.52 kB 133 kB
├ ○ /opengraph-image.jpg 0 B 0 B
├ ƒ /profile 4.76 kB 147 kB
├ ○ /sign-in 2.28 kB 122 kB
└ ○ /twitter-image.jpg 0 B 0 B
+ First Load JS shared by all 102 kB
├ chunks/150-db3db4cf74aef2d5.js 45.3 kB
├ chunks/cccb8abb-197eef17418c575f.js 54.2 kB
└ other shared chunks (total) 1.93 kB
ƒ Middleware 34.5 kB
○ (Static) prerendered as static content
ƒ (Dynamic) server-rendered on demand
┌──────────────────────────────┐
│ OpenNext — Generating bundle │
└──────────────────────────────┘
Bundling middleware function...
Bundling static assets...
Bundling cache assets...
Building server function: default...
Applying code patches: 1.923s
# copyPackageTemplateFiles
⚙️ Bundling the OpenNext server...
Worker saved in `.open-next/worker.js` 🚀
OpenNext build complete.
┌───────────────────────────────┐
│ OpenNext — Cloudflare preview │
└───────────────────────────────┘
Populating R2 incremental cache...
Using vars defined in .dev.vars
100%|█████████████████████████████████████████████| 9/9 [00:04:<00:00:, 2.12it/s]
Successfully populated cache with 9 assets
Tag cache does not need populating
⛅️ wrangler 4.29.0 (update available 4.32.0)
─────────────────────────────────────────────
Using vars defined in .dev.vars
Your Worker has access to the following bindings:
Binding Resource Mode
env.KV KV Namespace local
preview-kv
env.D1 D1 Database local
d1
env.NEXT_INC_CACHE_R2_BUCKET R2 Bucket local
thisisooo-next-incremental-cache-dev
env.WORKER_SELF_REFERENCE Worker local [not connected]
thisisooo
env.ASSETS Assets local
env.NEXTJS_ENV Environment Variable local
"(hidden)"
Service bindings, Durable Object bindings, and Tail consumers connect to other `wrangler dev` processes running locally, with their connection status indicated by [connected] or [not connected]. For more details, refer to https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/#local-development
╭──────────────────────────────────────────────────────────────────────╮
│ [b] open a browser [d] open devtools [c] clear console [x] to exit │
╰──────────────────────────────────────────────────────────────────────╯
⎔ Starting local server...
[wrangler:info] ✨ Parsed 1 valid header rule.
[wrangler:info] Ready on http://localhost:8787
問題なく動きました。
Typed Routes
やることはnext.config.tsにtypedRoutes: trueを追加するだけです。
const nextConfig = {
typedRoutes: true, // Now stable!
};
export default nextConfig;
補完機能は微妙(エディタによるのかもしれない)ですが、

無効なページへのパスを入れると型エラーがちゃんと出ました。

Route Props Helpers
「Typed Routes」によってPageProps, LayoutProps, RouteContextについても型チェックが効くようになったそうなので試してみました。
The system automatically discovers routes from your file structure, supporting dynamic routes, parallel routes, and custom routes from next.config.js. Type generation runs in both development and build modes, immediately regenerating types when your file structure changes in development, and scales efficiently to large projects by generating only a few optimized files instead of the many individual files used in the previous implementation.
と記載されている通り、ファイルの構造から自動で検知してくれるみたいですね。
LayoutProps
今までは
RootLayout(props: { children: ReactNode; header: ReactNode })
としていたところを、
RootLayout(props: LayoutProps<'/'>)
と書き換えました。

補完も効きます。

PageProps
今までは
InterviewPage({ params }: { params: Promise<{ interviewId: string }> })
としていたところを、
InterviewPage({ params }: PageProps<'/interviews/[interviewId]'>)
と書き換えました。

RouteContextはsrc/app/api/auth/[...all]/route.tsしか使ってなくて、これもbetter-authの設定なので特にやることはありませんでした。
感想
嬉しいかも〜!
Discussion