How to Use SVG in Next.js
Learn when to use SVG images, inline markup, or SVGR components in Next.js, with Turbopack setup, security guidance, and working examples.

Short answer: use an SVG as a normal image when you only need to display it, use inline SVG when you need direct control over its markup, and use an SVG-to-React loader such as @svgr/webpack when you want to import files as components. The correct setup also depends on whether your Next.js project uses Turbopack or webpack.
Choose the right SVG approach
| Requirement | Recommended approach | Why |
|---|---|---|
| Display a logo, illustration, or diagram | <img>, next/image, or a CSS background |
No build configuration and the SVG remains an ordinary asset. |
| Change fills, strokes, or paths from React | Inline SVG or an SVGR component | The SVG elements are available to JSX and CSS. |
| Reuse an SVG file as a component | Configure an SVG loader | You can write <Logo /> and pass props. |
| Accept SVG files from users or third parties | Sanitize, isolate, or convert them before display | SVG can contain capabilities similar to HTML and CSS. |
Start with the least configuration that meets the requirement. A static illustration does not become more useful because it was converted into a React component.

1. Display a static SVG from the public directory
For artwork that does not need internal styling, create public/images/diagram.svg. Anything in public is served from the site root, so the browser URL is /images/diagram.svg.
import Image from 'next/image'
export default function Diagram() {
return (
<Image
src="/images/diagram.svg"
alt="Request, rendering, and response flow"
width={960}
height={540}
unoptimized
/>
)
}
Next.js documents that SVGs are not optimized by default. When the source is known to be SVG, use the documented unoptimized path; current behavior also applies this automatically when the source ends in .svg. Check the version-specific Image documentation if your project uses a different Next.js release.
Using a plain image element
export default function BrandMark() {
return (
<img
src="/images/brand-mark.svg"
alt="Acme logo"
width="160"
height="40"
/>
)
}
A plain <img> is appropriate when you do not need the Image component’s layout features. Always provide meaningful alternative text for informative artwork. For a purely decorative mark, use alt="".
2. Import an SVG as a React component with SVGR
Component imports are useful when the SVG’s internal paths need props or CSS. Install SVGR as a development dependency:
npm install --save-dev @svgr/webpack
The current Next.js Turbopack documentation shows @svgr/webpack as a supported loader and maps SVG files to JavaScript modules.
Turbopack configuration
Add the rule to next.config.js (or the equivalent TypeScript configuration):
/** @type {import('next').NextConfig} */
const nextConfig = {
turbopack: {
rules: {
'*.svg': {
loaders: ['@svgr/webpack'],
as: '*.js',
},
},
},
}
module.exports = nextConfig
Import the file and render it like any other component:
import Logo from '@/assets/logo.svg'
export default function Header() {
return (
<header>
<Logo aria-label="Acme home" role="img" />
</header>
)
}
If you need to style the root SVG, give it a class or use inherited CSS values:
import Check from '@/assets/check.svg'
import styles from './Status.module.css'
export function Status() {
return <Check className={styles.icon} aria-hidden="true" />
}
.icon {
width: 1.25rem;
height: 1.25rem;
color: #16803c;
}
.icon path {
fill: currentColor;
}
Webpack projects
The rule above is a Turbopack example. A project still using webpack needs a webpack rule compatible with its existing configuration, and it may also need a resource-query convention if the same file must work as both a URL and a component. Do not copy a webpack rule into a Turbopack project unchanged. Confirm your installed Next.js version and active bundler before changing next.config.js. Restart the development server after configuration changes.
3. Write inline SVG in JSX
For a small icon with a few paths, inline markup avoids a loader entirely:
export function SearchIcon({ size = 20 }) {
return (
<svg
width={size}
height={size}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
>
<circle cx="11" cy="11" r="7" />
<path d="m20 20-4-4" />
</svg>
)
}
Use a stable viewBox so the icon scales independently of its rendered width and height. For a meaningful standalone graphic, provide a title or accessible label instead of aria-hidden. The exact accessibility treatment depends on whether the SVG conveys information or is decorative; test the resulting control with a keyboard and screen reader.
4. Use SVG with CSS backgrounds
Background SVGs work well for decorative patterns and icons that are not part of the document’s meaning:
.hero {
background: url('/images/grid.svg') center / cover no-repeat;
}
CSS backgrounds do not provide an accessible name. Do not use this technique for an image that communicates data, status, or an action.
5. Remote SVGs and the Image Optimization API
A remote source requires an allowed host in next.config.js when used with next/image. SVG handling still has security implications:
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'cdn.example.com',
pathname: '/assets/**',
},
],
},
}
module.exports = nextConfig
Next.js does not optimize SVGs by default partly because they scale without quality loss and can carry capabilities resembling HTML and CSS. If you deliberately enable dangerouslyAllowSVG, follow the documentation’s recommendations for a restrictive Content Security Policy and a content disposition that forces download. Do not treat arbitrary uploaded SVG files as inert raster images.
const nextConfig = {
images: {
dangerouslyAllowSVG: true,
contentDispositionType: 'attachment',
contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;",
},
}
module.exports = nextConfig
These settings protect the image delivery path; they do not sanitize untrusted SVG content. Validate and sanitize uploads at ingestion, or convert them to a safe raster format.
6. Understand the embedding security boundary
SVG behavior changes with its embedding context. In an HTML <img> or CSS image, browsers may restrict JavaScript and external resource loading. Those restrictions do not apply in the same way when an SVG is opened directly or embedded as a document through <iframe>, <object>, or <embed>. MDN documents these differences in its SVG as an image guide.
- Keep trusted artwork in
publicor a controlled asset pipeline. - Do not embed user supplied SVG as a document without isolation and sanitization.
- Use a restrictive CSP when your application serves SVG responses.
- Prefer raster conversion for files that must be displayed but cannot be trusted.
7. Common errors and fixes
| Error | Likely cause | Fix |
|---|---|---|
Module parse failed for .svg |
No SVG loader is configured for a component import. | Use a public URL, inline the SVG, or configure SVGR for your active bundler. |
Unexpected token < |
The import resolved to raw XML while JavaScript expected a component. | Match the import style to the loader output; restart the dev server after editing config. |
Invalid src prop |
A remote hostname is not allowed. | Add a precise remotePatterns entry and restart Next.js. |
| SVG appears blank | The file has no usable viewBox, has transparent artwork, or CSS sets its dimensions to zero. |
Inspect the SVG, set width and height, and verify the viewBox and inherited colors. |
| Styles do not change internal paths | The SVG is rendered as an external image. | Inline it or import it through SVGR so its elements are in the DOM. |
| Build works locally but fails in CI | Different Next.js version, bundler, or config format. | Pin dependencies, inspect the CI build command, and use the matching version’s docs. |
| Security scanner flags an SVG | The file contains scripts, event attributes, or external references. | Remove active content, sanitize it, isolate it, or convert it to PNG/WebP. |
8. Performance and reliability checklist
- Use SVG for line art and icons; use raster formats for photographs and complex textures.
- Remove editor metadata and unused definitions from large files.
- Set explicit dimensions or an aspect ratio to prevent layout shifts.
- Inline only small, frequently reused icons. Large inline SVG increases HTML size on every page.
- Keep a static file as a URL when it does not need React state or styling.
- Use a stable
viewBoxand avoid deeply nested groups when optimizing manually. - Test production builds with the same bundler used in deployment.
- Cache immutable SVG assets with hashed filenames when your deployment pipeline supports them.

Or skip the browser setup
If your goal is to capture a rendered Next.js page that contains SVG, ScreenshotNeo provides a website screenshot API and MCP server. It accepts one GET request and returns PNG, JPEG, WebP, or PDF. Cookie and consent banners, newsletter popups, and chat widgets are removed before capture. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status.
See the ScreenshotNeo API documentation for all options. A basic request is:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
You can also request full-page captures with lazy images loaded, select one element by CSS selector, choose dark mode or a device preset, set a viewport and retina scale, wait for a selector or network idle, inject CSS or JavaScript, hide selectors, block resource types, supply headers, cookies, a user agent, timezone, geolocation, or authorization, and produce PDFs with paper size, margins, orientation, and page ranges. Caching TTLs, signed image links, asynchronous jobs with signed webhooks, bulk capture for up to 100 URLs, usage reporting, and an OpenAPI specification are available. An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.
There are 1,000 free screenshots each month with no card. Paid plans start at $5 for 3,000 screenshots; every feature is included on every plan. Create a free ScreenshotNeo account.
FAQ
Can I use an SVG without installing a package?
Yes. Put it in public and reference its URL, use next/image, or write the SVG inline in JSX.
Should every icon be an SVGR component?
No. Use a component when the icon needs props, dynamic styling, or reuse through React. A static image is simpler for artwork that never changes.
Does Turbopack use the old experimental.turbo key?
Current documentation uses turbopack. Older Next.js releases may use different configuration names, so match the docs to your installed version.
Is an SVG automatically safe because it is inside an image tag?
Image embedding applies browser restrictions, but it does not make an untrusted file safe in every context. Sanitize uploads and follow CSP and content-disposition guidance.
Why does next/image not make my SVG smaller?
SVG is already vector based and is not optimized like a raster image. Optimize the source file itself or serve it as a static asset.


