wandfuldays

[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.nextjs.org 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.nextjs.org

つまり、バンドラを明示していなかった場合、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.Lucide

Webpack ビルドでは、node_modules 配下のパッケージを CSS から参照する際、
パスの先頭に ~ を付けることでモジュールとして解決されます。

この ~ は CSS の書き方ではなく、Webpack で CSS を読み込む css-loader の記法です。

GitHub - webpack/css-loader: CSS LoaderCSS Loader. Contribute to webpack/css-loader development by creating an account on GitHub.GitHub

逆に、~ を付けない場合は相対パスとして解釈され、次のようなビルドエラーになります。

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() でも同じでした。

API Reference: Turbopack | Next.jsTurbopack is an incremental bundler optimized for JavaScript and TypeScript, written in Rust, and built into Next.js.nextjs.org

まとめ

Webpack と Turbopack での挙動の違いを表にまとめると、次のようになります。

バンドラCSS 内での SVG 参照方法備考
Webpackurl(~lucide-static/icons/info.svg)~ が必要
Turbopackurl(lucide-static/icons/info.svg)~ を付けない(付けるとエラー)

Next.js 15 → 16 への移行で、

  • CSS 内で SVG やその他画像を参照している
  • node_modules 配下のアセットを使っている
  • 突然「Module not found」が出始めた

という場合は、パス先頭の ~ を一度疑ってみてください。

同じところでハマる人の助けになれば幸いです。


wand

「wand」は魔法の杖を意味します。魔法のようにさまざまなものを自分の手で生み出せるようになりたい、そんな思いを込めました。 ハンドメイド、家庭菜園、DIY、プログラミング等、「つくる」をテーマに色々なことをしていきたいと思っています。 Amazonのアソシエイトとして、wand は適格販売により収入を得ています。 GitHub: https://github.com/wand2016