How to Optimize SVG in Next.js

·6 min read

Next.js does not support SVG in next/image by default because it can’t optimize SVG the way it does raster images (no width/height intrinsic dimensions, potential XSS via inline scripts). That doesn’t mean SVG is second-class in Next.js — you just need to use the right patterns.

Pattern 1: SVG in the public/ folder

Place optimized SVG files in public/ and load them with a standard<img> tag:

export default function Logo() {
  return (
    <img
      src="/logo.svg"
      alt="Acme Inc."
      width={120}
      height={40}
      // No next/image — SVG doesn't need raster optimization
    />
  );
}

This works fine. SVG in public/ is served with the correct MIME type, cached by the browser, and benefits from CDN compression. The only requirement: the file must be optimized before it lands in public/.

Set up a pre-build script or CI pipeline to run SVGO on every SVG in public/ before deployment.

Pattern 2: Inline SVG as a React component

For icons that need CSS theming (color via currentColor), create small React components that return inline SVG:

// components/icons/ArrowRight.tsx
export function ArrowRight({ size = 24, className }: { size?: number; className?: string }) {
  return (
    <svg
      width={size}
      height={size}
      viewBox="0 0 24 24"
      fill="none"
      aria-hidden="true"
      className={className}
    >
      <path
        d="M5 12h14M12 5l7 7-7 7"
        stroke="currentColor"
        strokeWidth={2}
        strokeLinecap="round"
        strokeLinejoin="round"
      />
    </svg>
  );
}

The advantage: zero HTTP requests, full CSS theming, and TypeScript props. The downside: icon code lives in your JS bundle.

Pattern 3: SVGR with @svgr/webpack

To import SVG files directly as React components (like Create React App), configure SVGR in next.config.ts:

// next.config.ts
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
  webpack(config) {
    config.module.rules.push({
      test: /.svg$/,
      use: ['@svgr/webpack'],
    });
    return config;
  },
};

export default nextConfig;
npm install --save-dev @svgr/webpack

Then import SVGs as components:

import ArrowIcon from './arrow.svg';

export default function Page() {
  return <ArrowIcon width={24} height={24} />;
}

Note: SVGR performs basic SVG-to-JSX conversion (attribute renaming likeclassclassName), but it’s not an optimizer. Still run SVGO on source files before they reach SVGR.

Optimizing SVG files in Next.js build

Add a pre-build optimization step in package.json:

{
  "scripts": {
    "prebuild": "node scripts/optimize-svgs.js",
    "build": "next build"
  }
}
// scripts/optimize-svgs.js
import { optimize } from 'svgo';
import fs from 'node:fs';
import path from 'node:path';

function processDir(dir) {
  if (!fs.existsSync(dir)) return;
  for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
    const full = path.join(dir, entry.name);
    if (entry.isDirectory()) {
      processDir(full);
    } else if (entry.name.endsWith('.svg')) {
      const input = fs.readFileSync(full, 'utf-8');
      const result = optimize(input, { multipass: true });
      fs.writeFileSync(full, result.data);
    }
  }
}

processDir('./public');
processDir('./src/assets');
console.log('SVGs optimized.');

Handling SVG in next/image (workaround)

If you really need SVG in next/image — for example, to use its lazy loading or sizes attributes — you can opt in via next.config.ts:

const nextConfig = {
  images: {
    dangerouslyAllowSVG: true,
    contentDispositionType: 'attachment',
    contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;",
  },
};

The name says it all — only use this if you control the SVG source and know it contains no scripts. For untrusted user-uploaded SVGs, never use this option.

App Router vs Pages Router

The patterns above work in both. In the App Router, icon components are naturally Server Components (no 'use client' needed unless they use hooks), which keeps them out of the client bundle entirely. That’s a free win for inline SVG performance.

Summary

  • Use public/ + <img> for logos and illustrations
  • Use inline React components for theming icons with currentColor
  • Optimize SVG files before they enter your project with a pre-build script
  • Run SVGR if you want to import SVGs as React components via webpack
  • Avoid dangerouslyAllowSVG unless you fully trust the SVG source

For the broader React context, see SVG optimization in React.

Optimize SVGs instantly — no setup needed

500 free optimizations per month. No credit card required.

Get a free API key →