Can't Import SVG in Next.js: Fix for Turbopack and Webpack
Why <Logo /> fails with "Element type is invalid" or "Module parse failed", and the SVGR setups that build on Next.js 16 with both bundlers.
TL;DR#
An import Logo from './logo.svg' does not give you a React component in Next.js. Without a loader, Next.js handles the file as a static image and the import is an object with src, width and height — perfect for next/image, wrong for <Logo />. If you only need to display the file, pass that object to <Image> and change nothing else. If you need the SVG inline as a component (to style it with currentColor, animate paths, set className), install @svgr/webpack and register it for the bundler you build with: turbopack.rules for Turbopack, which Next.js 16 uses by default, or a webpack() rule for next build --webpack.
The error#
The Stack Overflow question dates from the webpack 4 era of Next.js, and its error is the one webpack prints when no loader claims the file:
./static/Rolling-1s-200px.svg 1:0
Module parse failed: Unexpected token (1:0)
You may need an appropriate loader to handle this file type.On a current project the message you get depends on the bundler and on what you did with the import. We built a minimal App Router page on 1 October 2026 and recorded each case:
next 16.3.8, react 19.3.0, @svgr/webpack 8.1.0, node 24 (official Docker image)
page.tsx: import Logo from './logo.svg'; ... <Logo /> (no loader configured)
next build (Turbopack) and next build --webpack:
Error occurred prerendering page "/". 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.
next dev (Turbopack): GET / -> HTTP 500, same "Element type is invalid ... got: object"The original "Module parse failed" message still exists on Next.js 16 — we got it again with the webpack 5 rule from the question's second answer, covered further down.
Why it happens#
Next.js ships a built-in rule for image files. A static image import returns metadata, not markup; this is what the import evaluated to in our test:
{"src":"/_next/static/media/logo.1vf60aj3mmni9.svg","width":24,"height":24,"blurWidth":0,"blurHeight":0}React can render a string tag or a function, so handing it this object as <Logo /> produces "Element type is invalid … but got: object". The SVG never became JSX because nothing compiled it into JSX. That is the job of SVGR: its webpack loader, @svgr/webpack, turns the file's markup into a React component at build time.
The second half of the trap is the bundler. Turbopack is the default in Next.js 16, and webpack is opt-in with next build --webpack (CLI reference). The Turbopack docs state that webpack() configs in next.config.js are not recognized by Turbopack. Turbopack takes loaders through its own turbopack.rules option instead, and the Next.js docs list @svgr/webpack among the webpack loaders it has been tested with. So the classic webpack-only snippet does not help a Turbopack build. In Next.js 16 it even stops it: the version 16 upgrade guide says a plain next build fails on purpose when it finds a custom webpack configuration, and tells you to either build with next build --webpack or move the configuration to Turbopack options.
Fix#
1. Only displaying the SVG? Use the static import with next/image#
No package and no config. Pass the imported object to <Image>, which reads src, width and height from it:
import Image from 'next/image'
import logo from './logo.svg'
export default function Header() {
return <Image src={logo} alt="Company logo" />
}This built with Turbopack in our test and rendered a plain <img> pointing at the hashed file under /_next/static/media/. The Image docs explain why it is served as is: Next.js does not optimise SVG, and it applies unoptimized automatically when src ends with .svg. You do not need dangerouslyAllowSVG for a local static import like this one. For sizing the image inside a fluid container, see setting next/image to 100% height, which covers fill and the positioned parent it needs.
2. Need a component? Install SVGR#
npm install --save-dev @svgr/webpack3. Register SVGR for Turbopack (the default in Next.js 16)#
This is the configuration from the Next.js Turbopack docs:
// next.config.js
module.exports = {
turbopack: {
rules: {
'*.svg': {
loaders: ['@svgr/webpack'],
as: '*.js',
},
},
},
}import Logo from './logo.svg'
export default function Header() {
return <Logo className="logo" aria-hidden="true" />
}With this rule our page built and the prerendered HTML contained the inline element, attributes included: <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" class="logo">.
4. Register SVGR for webpack (next build --webpack)#
The rule has to replace Next.js's built-in handling of .svg, not sit next to it. This version finds the built-in rule that matches .svg, reuses its issuer, and excludes .svg from it:
// next.config.js
module.exports = {
webpack(config) {
const fileLoaderRule = config.module.rules.find((rule) => rule.test?.test?.('.svg'))
config.module.rules.push({
test: /\.svg$/i,
issuer: fileLoaderRule.issuer,
use: ['@svgr/webpack'],
})
fileLoaderRule.exclude = /\.svg$/i
return config
},
}It built with next build --webpack and produced the same inline <svg … class="logo">. Once a webpack() function is in the file, keep next build --webpack as the build script: a plain next build refuses to run with it, as the upgrade guide linked above explains. If Turbopack itself hangs on "Compiling…", the Turbopack stuck fix covers clearing the cache and forcing next build --webpack.
The old webpack 5 rule from the answers now fails#
Many projects still carry the webpack 5 rule from the question's second answer, which pushes SVGR with an issuer of { and: [/\.(js|ts|md)x?$/] } and leaves the built-in image rule untouched. On Next.js 16 with next build --webpack that config failed in our test with the question's own error (the echoed SVG source line that follows it is omitted here):
Failed to compile.
./app/logo.svg
Module parse failed: Unexpected token (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loadersReplace it with the rule in step 4 rather than adjusting the regular expression. We did not trace which internal import the old issuer condition misses, so treat this as an observed result, not an explained one.
Keep next/image and SVGR side by side#
The blanket '*.svg' rule turns every SVG import into a component, including the ones you already pass to <Image>. With the rule from step 3 and the page from step 1, the build failed:
Error occurred prerendering page "/". Read more: https://nextjs.org/docs/messages/prerender-error
Error: Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server". Or maybe you meant to call this function rather than return it.The import is now a component function, and <Image src={logo}> receives that function instead of the image object. The same message has unrelated causes in a root layout, listed in the Next.js 16 /_not-found prerender fix; here the page that fails to prerender is the one that imports the SVG. How the caret line under the message names the exact prop, and the fixes for a plain function prop, are in the Functions cannot be passed directly to Client Components fix.
On a recent Next.js 16 release, scope the rule to an opt-in query with Turbopack's condition syntax, where a string query must match exactly. The query condition is newer than condition itself; the version history on that page reads:
| 16.2.0 | turbopack.rules.*.condition.query was added. |
| 16.0.0 | turbopack.rules.*.condition was added. |On a release older than the first row, keep the blanket rule and reference displayed images from public/ by path instead. The scoped rule looks like this:
// next.config.js
module.exports = {
turbopack: {
rules: {
'*.svg': {
condition: { query: '?react' },
loaders: ['@svgr/webpack'],
as: '*.js',
},
},
},
}import Image from 'next/image'
import logo from './logo.svg'
import Logo from './logo.svg?react'
export default function Header() {
return (
<header>
<Logo className="logo" aria-hidden="true" />
<Image src={logo} alt="Company logo" />
</header>
)
}That page built and the HTML held both the inline <svg … class="logo"> and an <img> for the static file. TypeScript needs to know what the query import returns; without a declaration tsc stopped with error TS2307: Cannot find module './logo.svg?react' or its corresponding type declarations. Add a declaration file and list it in tsconfig.json include:
// svgr.d.ts
declare module '*.svg?react' {
import type { FC, SVGProps } from 'react'
const ReactComponent: FC<SVGProps<SVGSVGElement>>
export default ReactComponent
}With it, tsc --noEmit exited cleanly. The plain *.svg import keeps the typing Next.js already provides through next-env.d.ts.
Verify the fix#
Build, then look at what the page actually shipped rather than at the absence of an error:
npx next build
grep -o '<svg[^>]*class="logo"[^>]*>' .next/server/app/index.html
grep -o '<img[^>]*/_next/static/media/[^>]*>' .next/server/app/index.htmlAn inline component shows up as an <svg> element carrying your className; an image shows up as an <img> whose src points under /_next/static/media/. If neither command prints anything, the page is dynamic or the import is still the wrong kind. For a dynamic route, open the page in the browser and inspect the element: an <img> where you expected inline markup means the SVGR rule did not run for that bundler.
Pick the import by what the SVG is for. A logo or illustration that only needs to appear stays a static import in <Image>; an icon that inherits text colour or reacts to hover becomes a component through SVGR. Register SVGR under the key of the bundler you actually build with, and scope it with a query once both kinds live in the same project.
Further reading: Fix: FetchError request to localhost:1337 ECONNREFUSED ::1. Also see Fix Turbopack's Wrong Workspace Root Warning. Also see Fix Turbopack Build Errors from a Webpack Config. Also see Failed to find font override values: next/font/google fix.
One email a month — the latest explained errors
What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.
Frequently Asked Questions
Still stuck? Paste your error.
Paste the error message or stack trace — we check it against every fix and tool on the site.
Continue Reading
Fix Turbopack Build Errors from a Webpack Config
Find the plugin behind the unexpected webpack hook, choose a compiler deliberately, and verify that the behaviour you need still runs.
Fix Turbopack's Wrong Workspace Root Warning
Trace the lockfile behind an unexpected workspace root, choose the right standalone or monorepo boundary, and separate a reported memory spike from a verified cause.
Failed to find font override values: next/font/google fix
Why next/font/google warns about font override values for newer Google Fonts, why the popular fix only works on webpack, and how to get the CLS fallback back.


