
[Next.js 16] CSS の url() の Module not found の直し方
2026-01-312026-09-22バンドラがデフォルトで Turbopack になったのが原因でした。node_modules のアセットを CSS から参照するときの書き方と、Webpack との違い。
こんにちは、 wand です!
このたび、当ブログのフロントエンドを Next.js 15 から Next.js 16 にバージョンアップしました。
大きな破壊的変更は少ない印象だったのですが、実際に移行してみると ビルド時に思わぬエラーに遭遇しました。
結論から言うと、Turbopack がデフォルトになったことによる、CSS 内 SVG 参照パスの解決方法の違いが原因でした。
同じように lucide-static を使っている人がハマりそうなのですが、2026/1/31 執筆時点では、CSS の url() で同じことが起きるという情報が日本語・英語ともに見当たらなかったため、備忘録も兼ねてまとめます。
Next.js 16 で何が変わったのか
Next.js 16 の大きな変更点のひとつが、バンドラがデフォルトで Turbopack になったことです。
-
Next.js 15.3
- デフォルト: Webpack
--turbopackフラグを付けることで Turbopack を利用可能 (アルファ版機能)
-
Next.js 16
- デフォルト: Turbopack
--webpackフラグを付けることで Webpack を利用可能
Next.js 15.3Next.js 15.3 includes Turbopack for builds, new client instrumentation and navigation hooks, and more.
Next.js 16Next.js 16 includes Cache Components, stable Turbopack, file system caching, React Compiler support, smarter routing, new caching APIs, and React 19.2 features.
つまり、バンドラを明示していなかった場合、Webpack から Turbopack に切り替わったということになります。
前提:lucide-static の SVG を CSS の url() で参照している
このブログでは、
これは info です。
のような表示を行う際、アイコン画像として lucide-static の SVG を CSS の url() 経由で参照しています。
mask-image: url(~lucide-static/icons/info.svg);
この書き方は、Lucide の公式ドキュメントにも記載されている Webpack ビルド前提の方法です。
Lucide IconsBeautiful & consistent icon toolkit made by the community.
Webpack ビルドでは、node_modules 配下のパッケージを CSS から参照する際、
パスの先頭に ~ を付けることでモジュールとして解決されます。
この ~ は CSS の書き方ではなく、Webpack で CSS を読み込む css-loader の記法です。
逆に、~ を付けない場合は相対パスとして解釈され、次のようなビルドエラーになります。
mask-image: url(lucide-static/icons/info.svg);
Error: Cannot find module './lucide-static/icons/info.svg'
Turbopack での挙動
ところが、Turbopack ではこの書き方がそのまま通用しません。
❌ Turbopack で ~ 付きパス
mask-image: url(~lucide-static/icons/info.svg);
この場合、次のエラーが発生しました。
Module not found: Can't resolve '~lucide-static/icons/info.svg'
✅ Turbopack では ~ を外す
Turbopack 環境では、~ を付けずにそのまま指定すると解決します。
mask-image: url(lucide-static/icons/info.svg);
Webpack とは真逆の挙動となりました。
~ は Webpack のレガシーな記法で、Turbopack では未対応です。
Next.js の公式ドキュメントは Sass で node_modules を読む場合として書いていますが、CSS の url() でも同じでした。
まとめ
Webpack と Turbopack での挙動の違いを表にまとめると、次のようになります。
| バンドラ | CSS 内での SVG 参照方法 | 備考 |
|---|---|---|
| Webpack | url(~lucide-static/icons/info.svg) | ~ が必要 |
| Turbopack | url(lucide-static/icons/info.svg) | ~ を付けない(付けるとエラー) |
Next.js 15 → 16 への移行で、
- CSS 内で SVG やその他画像を参照している
node_modules配下のアセットを使っている- 突然「Module not found」が出始めた
という場合は、パス先頭の ~ を一度疑ってみてください。
同じところでハマる人の助けになれば幸いです。
