Next.js + Turbopack + SVGR

Or how I learned that it is not enough to add rule for .svg under turbo.


During the work on this website, I decided to go with SVG icons (obviously). I chose to download the SVG files of common icons from lineicons and import them directly where needed. It also turned out that some of the ones I needed were missing in that set, but easily available elsewhere (also as SVGs). In places that called for more details, I used the ones from Futuro Icons Collection.

Once all the necessary icons landed in my app icons folder, it was time to configure my freshly updated to Next.js 15 and converted to TypeScript config. Obviously, the first step was to go the docs and check how to do it with Turbopack, which lead me to the example in documentation. I ended up with next.config.ts looking like that:

import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  experimental: {
    turbo: {
      rules: {
        "*.svg": {
          loaders: ["@svgr/webpack"],
          as: "*.js",
        },
      },
    },
  },
};

I fired up npm run dev, did some other upgrade-related adjustments and all was well.

Not.

Because deploy failed (and that is a lesson learnt to add pre-push hook for main branch even for tiny personal projects :smile:). And the output was cryptic:

Error occurred prerendering page "/_not-found". Read more: https://nextjs.org/docs/messages/prerender-error
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.

with absolutely nothing meaningful in the stack trace. I checked the not-found.tsx file (very primitive, and nothing was changed during upgrade) and then run build locally. It also failed, but with different page.

Ok, so it's probably layout.tsx, right? After some more digging and uncommenting code line-by-line, I finally narrowed it down to the icon. Which, you know, was working perfectly fine when running dev...

Going to the @svgr/webpack documentation for integrating with Next.js finally shed some light on th issue I was having, namely while there was nothing about Turbopack, it (among other useful tips) showed the example webpack config. I just pasted it into my config, while thinking "I'm using turbo, it says experimental turbo when I run npm run build, but that's the only idea I have left".

It worked.

So, note to my future self: you need both the webpack config and the adjustments for SVGs to work.

Relevant next.config.ts parts for future reference:

import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  webpack(config) {
    // Grab the existing rule that handles SVG imports
    const fileLoaderRule = config.module.rules.find((rule: { test?: RegExp }) =>
      rule.test?.test?.(".svg"),
    );

    config.module.rules.push({
      test: /\.svg$/i,
      issuer: fileLoaderRule.issuer,
      use: ["@svgr/webpack"],
    });

    // Modify the file loader rule to ignore *.svg, since we have it handled now.
    fileLoaderRule.exclude = /\.svg$/i;

    return config;
  },

  experimental: {
    turbo: {
      rules: {
        "*.svg": {
          loaders: ["@svgr/webpack"],
          as: "*.js",
        },
      },
    },
  },
};

export default nextConfig;