Next.jsのdynamic importをもっと動的に

  • Next.js
  • React

ぬにょす(挨拶)。

今回紹介するのは、Next.js が提供する dynamic 関数を使って変数で指定したコンポーネントを読み込む方法です。

結論のコード

// 変数 component には、事前にコンポーネント名が格納されている
const DynamicComponent = dynamic(() =>
  import("./module").then((mod: any) => mod[component])
);

はじめに

Next.js には dynamic import という機能があります。

https://nextjs.org/docs/advanced-features/dynamic-import

当該コンポーネントの表示が必要になったタイミングで import することで、最初のページ読み込みにおける転送量を減らすことができます。

この dynamic import で読み込むコンポーネントを変数で指定できないものかと思った次第です。要するに

switch (arg) {
  case "A":
    DynamicComponent = dynamic(() => import("./A"));
    break;
  case "B":
    DynamicComponent = dynamic(() => import("./B"));
    break;
  case "C":
    DynamicComponent = dynamic(() => import("./C"));
    break;
}

みたいなことを

DynamicComponent = dynamic(() => import(`./${arg}`))

のように書けないもんかな、と。

import()に変数を渡してもダメっぽい

最初はこれでイケるんじゃないかと思ったのですが。

// 何らかの事前処理で modulePath に "../components/hello" をセットした上で
const DynamicComponent = dynamic(() => import(modulePath))

期待通りの動きにはなりませんでした。変数経由だと、コンパイルなりビルドなりの時点では読み込むモジュール名が不明ですからね。生成された js ファイルに、当該モジュールのコードが入らないんだと推測してます。

名前つきexportを応用してみた

Next.js 公式サイトの dynamic import のページには、名前つきで export されたコンポーネントを dynamic import する例が載っています。

以下、一部引用。

const DynamicComponent = dynamic(() =>
  import('../components/hello').then((mod) => mod.Hello)
)

"../components/hello" を読み込んだ後、モジュールに含まれる "Hello" というコンポネントを返却しているように解釈できます。

mod.Hello は mod オブジェクトの Hello メンバにアクセスしているわけだから

// 何らかの事前処理で moduleName に "Hello" をセットした上で
const DynamicComponent = dynamic(() =>
  import('../components/hello').then((mod) => mod[moduleName])
)

という書き方と等価なんじゃないか?と思って試してみたらビンゴでした。

まとめ

  • import() の引数に変数を渡してもダメ。
  • import() の引数には文字列リテラルを指定し、使用するファイル(モジュール)を明確にした上で読み込むコンポーネントを変数で指定することは可能。

後書き

何故このようなコードが必要になったかというと、react-icons パッケージのアイコン一覧を表示する必要があったからです。

100を超える import を書く気はしなかったので、外部jsonファイルから読み込んだコンポーネント名を使って import できないかと試行錯誤した結果、今回紹介した方法を見つけた次第です。

記事の一覧へ