Using a World Map SVG in Next.js Projects

Using a World Map SVG in Next.js Projects

Dropping a world map SVG into a Next.js project trips up more developers than it should, mostly because next/image — the component everyone reaches for first — doesn't handle SVG the way it handles a photo. Once you know why, the fix takes a few minutes. Start with a clean export from World in Dots so you're not also fighting a messy source file while you sort out the integration.

Why next/image Isn't the Default Answer

next/image is built around optimizing raster formats: resizing, converting to WebP/AVIF, and serving the right size per device. SVG doesn't benefit from any of that — it's already resolution-independent — so Next.js's image optimizer largely ignores it. Pass a local SVG to next/image and it will render, but you're not getting any real benefit from the component, and you still need to set unoptimized on some configurations to avoid a build error or a mismatched aspect ratio.

For a static, non-interactive map (say, a hero background), next/image with unoptimized is fine:

import Image from "next/image";
import worldMap from "@/public/images/world-dots.png";

<Image src={worldMap} alt="Dotted world map" unoptimized />

But if you exported your map as SVG and want to style or interact with individual dots or countries, you need a different approach entirely.

Inlining the SVG as a Component

The most flexible pattern is importing the SVG's markup directly as a React component, rather than treating it as an image file. Two common ways to get there:

1. SVGR (via @svgr/webpack) — configure Next's webpack settings so .svg imports become React components automatically:

// next.config.js
module.exports = {
  webpack(config) {
    config.module.rules.push({
      test: /\.svg$/,
      use: ["@svgr/webpack"],
    });
    return config;
  },
};
import WorldMap from "@/public/maps/world.svg";

<WorldMap className="w-full h-auto [&_path]:fill-slate-300" />

This gives you a real component with real props, and every <path> or <circle> in the map becomes addressable with CSS, exactly like customizing SVG map colors with CSS already describes — the only difference is the SVG now lives in your component tree instead of a static file.

2. Raw string import + dangerouslySetInnerHTML — useful if the SVG is generated dynamically (for example, fetched from an API or built at request time) rather than bundled at build time. This avoids a webpack loader change but means you're responsible for sanitizing the source, since injecting untrusted SVG markup this way is a real XSS vector.

Generate vector dotted maps

Create vector dotted maps with custom options and download them as SVG or PNG files

App Router: Server vs. Client Components

If your map needs hover states, tooltips, click handlers, or any state at all, the component that renders it must be a Client Component — add "use client" at the top of the file. A common mistake in the App Router is building an interactive map component and forgetting that its parent page is still a Server Component by default; Next.js will error the moment you try to attach an event handler in a server-rendered tree.

A clean split looks like this: keep the page itself as a Server Component (for fast initial load and SEO), and isolate only the interactive map into its own client-marked component that the page imports. This keeps the rest of the page — text, metadata, layout — server-rendered while the map handles its own interactivity on the client.

For a purely decorative background map with no interaction at all, there's no reason to add "use client" anywhere; a plain inline SVG or an <Image> component works fine inside a Server Component.

Performance Notes

A world map with a few thousand dots produces a correspondingly large SVG. A few things matter more in Next.js specifically than in a plain static site:

Final Thoughts

The short version: use next/image for a map you'll never touch again, and inline the SVG as a component the moment you need to style, animate, or interact with it. Getting this distinction right up front saves you from the classic symptom of a Next.js map project — a map that looks fine but silently ignores every CSS rule you throw at it because it's still sitting behind an <img> tag.