reactjsnext.jssvgsvgr

Problem with SVGs in NEXT.JS and next.config.ts file, SVGR setup


I'm importing local SVG into the component(Navbar) as: import Icon from "@/assets/svg/icon.svg";.

Then, I configured next.config.ts accordingly as follows:

import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  webpack(config) {
    config.module.rules.push({
      test: /\.svg$/,
      use: [
        {
          loader: "@svgr/webpack",
          options: {
            icon: true,
          },
        },
      ],
    });
    return config;
  },
  experimental: {
    turbo: {
      rules: {
        "*.svg": {
          loaders: ["@svgr/webpack"],
          as: "*.js",
        },
      },
    },
  },
};

export default nextConfig;

I copied it from a GitHub discussion from @bloodykheeng.

Previously, I made multiple prompts on DeepSeek and ChatGPT trying to solve it but it only kept answering code that didn't work.

I also pasted the code above, which DOES work, on DeepSeek and it said it needed some adjustments, which broke the working code. They say, "if it's not broken, don't fix it" maybe is true?

AI also told me that turbopack isn't experimental anymore and that I should remove the experimental which also breaks the code.

When running pnpm run dev, I keep seeing this message whenever the code ISN'T exactly as the one above:

 ⚠ Invalid next.config.ts options detected: 
 ⚠     Unrecognized key(s) in object: 'turbo'
 ⚠ See more info here: https://nextjs.org/docs/messages/invalid-next-config
 ✓ Ready in 1623ms
 ⚠ Webpack is configured while Turbopack is not, which may cause problems.
 ⚠ See instructions if you need to configure Turbopack:
  https://nextjs.org/docs/app/api-reference/next-config-js/turbo

How should I configure next.config.js following best practices of NEXTJS 15.1.8 as of 05/2025?

AI told me that for using SVGs with React, I need SVGR which I downloaded using pnpm. Version: 8.1.0

Here is Navbar.tsx which I strongly believe is tied with the problem

"use client";

import React, { useEffect, useState } from "react";
import Link from "next/link";
import Image from "next/image";

import WMIcon from "@/assets/svg/brand.svg";
import SearchIcon from "@/assets/svg/search.svg";
import MenuIcon from "@/assets/svg/menu.svg";
import CloseIcon from "@/assets/svg/close.svg";

interface NavItemProps {
  text: string;
  href: string;
  className?: string;
}

const NavItem = ({ text, href, className = "" }: NavItemProps) => (
  <li className="list-none">
    <Link
      href={href}
      className={`group relative inline-block text-md font-normal text-gray-900 px-2 py-1 ${className}`}
      role="navigation"
      aria-label={text}
    >
      <span className="relative z-10">{text}</span>
      <span
        className="absolute left-0 bottom-0 w-full h-px bg-gray-900 
                            transform origin-right scale-x-0 
                            transition-transform duration-300 ease-[ease]
                            group-hover:origin-left group-hover:scale-x-100"
      ></span>
    </Link>
  </li>
);

const Navbar = () => {
  const [isOpen, setIsOpen] = useState(false);
  const [isMounted, setIsMounted] = useState(false);

  useEffect(() => {
    setIsMounted(true);
  }, []);

  if (!isMounted) return null;

  return (
    <header className="bg-white shadow-sm">
      <nav className="max-w-7xl mx-auto p-2 sm:px-6 lg:px-8 w-full">
        <div className="flex justify-between items-center w-full">
          <div className="flex items-center">
            <Link
              href="/"
              className="flex shrink-0 items-center space-x-3"
              aria-label="homepage"
            >
              <WMIcon
                className="h-8 w-8"
                viewBox="0 0 24 24"
                aria-hidden="true"
              />
            </Link>
          </div>

          <div className="hidden md:flex md:items-center md:space-x-8">
            <NavItem text="Collections" href="/" />
            <NavItem text="Account" href="/" />

            <div className="relative">
              <SearchIcon className="h-5 w-5 absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" />
              <input
                type="text"
                placeholder="What are you looking for?"
                className="pl-10 pr-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-300 focus:border-transparent"
              />
            </div>
          </div>

          <div className="flex items-center md:hidden">
            <button
              onClick={() => setIsOpen(!isOpen)}
              className="inline-flex items-center justify-center p-2 rounded-md text-gray-600 hover:text-gray-900 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-blue-500"
              aria-expanded={isOpen}
            >
              <span className="sr-only">
                {isOpen ? "Close menu" : "Open menu"}
              </span>
              {isOpen ? <CloseIcon /> : <MenuIcon />}
            </button>
          </div>
        </div>

        <div className={`md:hidden pb-4 ${isOpen ? "block" : "hidden"}`}>
          <div className="pt-4 space-y-4">
            <NavItem text="Collections" href="/" />
            <NavItem text="Account" href="/" />

            <div className="px-4">
              <div className="relative">
                <SearchIcon className="h-5 w-5 absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" />
                <input
                  type="text"
                  placeholder="What are you looking for?"
                  className="w-full pl-10 pr-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
                />
              </div>
            </div>
          </div>
        </div>
      </nav>
    </header>
  );
};

export default Navbar;

Here is a snippet of NEXTJS error


Solution

  • Answered!

    Based on this article from NEXTJS own website, I added this to next.config.ts

    module.exports = {
      turbopack: {
        rules: {
          '*.svg': {
            loaders: ['@svgr/webpack'],
            as: '*.js',
          },
        },
      },
    }
    

    I already read this article, but I was adding the snippet as is, which is not the right way to do it.

    ❌ Wrong Way

    Ctrl+C

    Ctrl+V


    ✅ Correct Way

    import type { NextConfig } from "next";
    
    const nextConfig: NextConfig = {
      // INSIDE CONST, REMOVE MODULE.EXPORTS
      turbopack: {
        rules: {
          "*.svg": {
            loaders: ["@svgr/webpack"],
            as: "*.js",
          },
        },
      },
    };
    
    export default nextConfig;
    

    Again, to reinforce you from not committing my mistakes, do not add the snippet from NEXTJS webpage as is.