🈂️

Next.jsでbasePathを変更した時に必要な対応

に公開

導入

Next.jsでサイトをサブディレクトリで運用したい場合は、以下のように設定することで可能です。

next.config.js
const nextConfig = {
  basePath: '/hoge', 
}

export default nextConfig

ですが、その設定を

  • Next.js側で自動で設定を反映してくれる部分
  • 自前で修正しないといけない部分

があったので、共有としてまとめます。

Next.jsのバージョンは以下になります

"next": "14.2.24"

自動で設定が反映された部分

Link⭕️

リンクコンポーネントは、以下の記述のままで
自動で、/hoge/listのようになってくれたので対応は不要でした。

import Link from 'next/link'

<Link href="/list">リスト一覧へ</Link>

router.push⭕️

useRouterを使ったリダイレクトのパスも、
/hoge/listに自動で飛んでくれました。

import { useRouter } from 'next/navigation'

const router = useRouter()
router.push('/list')

自前で修正しないといけない部分

Image❌

public以下に置いた画像をimageタグを使用した画像は全部切れてしまいました。
こちらは個別での対応が必要になります。

import Image from 'next/image'

<Image src='/common/logo.png' alt='' width={100} height={40} />

Imageコンポーネントのラッパーコンポーネントを用意して、
そこにAPP_BASE_PATHとして定数化したサブディレクトリを追加することで
アプリ全体に変更を適用させました。
(今回はNext.jsのImageコンポーネント使いませんでした、癖が強くて苦手、、)

components/utils/image.jsx
'use client'

import { twMerge } from 'tailwind-merge'

// constants
import { APP_BASE_PATH } from '@/constants'

export function Image({ src, alt = '', className = '', ...props }) {
  return (
    <img
      {...props}
      src={APP_BASE_PATH + src} // サブディレクトリを適用
      alt={alt}
      className={twMerge('inline-block', className)}
    />
  )
}

location.href❌

リダイレクトに関しては、router.pushでほぼ賄えると思うのですが、
私の場合、ログアウト時のリダイレクトなどにこちらを使用しています。
というのも、TanStack Queryを使ったSPAの場合、ログアウト後の状態をリセットする処理が
ちょっと面倒というのもあり、location.hrefを使えば簡単に状態を初期化出来るためです。

こちらも、こんな感じでラッパー関数を用意してあげましょう。

import { APP_BASE_PATH } from '@/constants'

export function hardRedirect(path) {
  window.location.href = `${APP_BASE_PATH}${path}`
}

cssの背景画像❌

これが地味にめんどいやつです。。
tailwindcssでベタ書きしているやつはもう、個別に対応しました。

bg-[url("/hoge/icon/under-arrow-gray.svg")]

reactのUIライブラリ系のcss.moduleの中の画像パス❌

react-selectとかその辺ですね。css.moduleでカスタマイズしているので
そこで画像を使っている場合は、個別で対応が必要です。

storybookで管理しているコンポーネントで画像使っている時❌

コンポーネント内で使っているImageコンポーネントのパスをいじったので、
こちらで画像のリンク切れが起こります。nextConfigの設定はstorybookとは無関係なので。
最初は絶望しましたが、こちらの設定でstorybook側にも画像のサブディレクトリ化が出来ました。

.storybook/main.js
-  staticDirs: ['../public'],
+  staticDirs: [
+    {
+      from: '../public',
+      to: '/hoge',
+    },
+  ],

番外編:プロキシの設定にはサブディレクトリを反映したくない

例えば、APIのプロキシの設定をしている時などに、今のままだとbasePath
rewritesにも反映されてしまいます。
その場合は、個別にbasePath:falseをすることで、basePathの設定を除くことが出来ます。

next.config.js
/** @type {import('next').NextConfig} */

const nextConfig = {
  basePath: '/hoge',
  async rewrites() {
      return [
        {
          source: `${process.env.NEXT_PUBLIC_API_URL}/:path*`,
          destination: `${process.env.NEXT_PUBLIC_PROXY_DESTINATION_URL}/:path*`,
          basePath: false, // これが必要
        },
      ]
  },
}

export default nextConfig

最後に

色々なアプリが混在しているプロジェクトではサブディレクトリ使う場面もありそうですよね。
「これ、もっと簡単に対応出来るよ」とか、ありましたら、教えて頂けるとありがたいです🙏

参考記事

https://zenn.dev/moozaru/articles/34fc246deab686

Discussion