Using SVGO's prefixIds Plugin to Prevent ID Collisions in Inline SVGs
Inline SVGs share one global ID namespace, so duplicate ids collide. SVGO's prefixIds plugin rewrites ids and their internal references with a per-file prefix — here's the config, the ordering pitfall, and how to verify it.
25 Dec 2025, 04:30 UTC

When you embed two or more SVG files inline in the same HTML document, their internal id attributes share one global namespace. Two files that both contain <linearGradient id="grad"> will collide: the second file's <use href="#grad"> or fill="url(#grad)" may resolve to the first file's element, producing wrong colors, broken clip paths, or animations targeting the wrong node. SVGO's prefixIds plugin solves this at build time by rewriting every id — and every internal reference to it — with a unique prefix per file.
What prefixIds actually rewrites
The plugin walks the parsed SVG tree and prepends a string you choose to each id attribute. Critically, it also updates the references that point at those IDs: href="#..." on <use> and <animate>, url(#...) values in attributes like fill and clip-path, and begin="id.end"-style animation timing references. Because it operates on the parsed tree rather than on raw text, the renames stay consistent — you don't get a renamed ID with a dangling reference.
The plugin is available in SVGO v2 and later. If you're pinned to v1, prefixIds doesn't exist and you'd need to rename IDs yourself before optimizing.
A worked configuration
The most reliable pattern is one prefix per source file, so uniqueness is guaranteed by construction. With the Node API (run this in your build script, with svgo installed as a dependency):
const { optimize } = require('svgo');
const fs = require('fs');
const files = ['search.svg', 'cart.svg', 'user.svg'];
for (const file of files) {
const input = fs.readFileSync(file, 'utf8');
const name = file.replace(/\.svg$/, '');
const result = optimize(input, {
plugins: [
{
name: 'prefixIds',
params: { prefix: `${name}-` },
},
],
});
fs.writeFileSync(`dist/${file}`, result.data);
}After this runs, search.svg's id="grad" becomes id="search-grad" and its fill="url(#grad)" becomes fill="url(#search-grad)". The equivalent CLI form (run in a shell where the svgo binary is installed, e.g. via npx svgo) is:
svgo input.svg -o output.svg \
--config='{"plugins":[{"name":"prefixIds","params":{"prefix":"icon-"}}]}'The CLI is fine for one-off checks, but for a set of icons the Node loop is what you want, because the prefix can be derived from the filename automatically.
Plugin ordering: the mistake that breaks this
SVGO runs plugins in the order listed in your config. If cleanupIDs (which removes or minifies IDs it considers unused) runs before prefixIds, it may strip or shorten IDs that your markup or CSS actually depends on, and prefixing afterward can't recover them. Put prefixIds first, or disable cleanupIDs for files whose IDs are referenced externally:
plugins: [
{ name: 'prefixIds', params: { prefix: 'icon-' } },
// cleanupIDs runs after, or omit it entirely
]Note that if you use a preset such as preset-default, plugin ordering is governed by the preset's internal list; check the SVGO documentation for your major version if you override individual plugins inside a preset, since the override mechanics changed between v2 and v3.
Limits and common mistakes
- External references are not rewritten. A
<use href="other.svg#shape">pointing into a different file is left alone — and if you prefixother.svgseparately, that reference breaks. Only IDs referenced within the same document are kept in sync. - CSS and JS outside the SVG must be updated by hand. A stylesheet rule like
#grad { stop-color: red; }in your page will silently stop matching after prefixing. Either prefix your selectors too, or style via classes. - Don't double up on uniqueness. If your templating pipeline already injects unique IDs, adding
prefixIdsjust lengthens every ID and reference, increasing file size for no benefit. - Malformed input confuses the parser. IDs inside
<script>or<style>CDATA blocks are only handled when the SVG parses cleanly as XML. Run the optimizer on well-formed source files.
How to verify the result
Three quick checks cover most failure modes:
- Diff the output. Run the config on a test SVG containing
<rect id="a">and<use href="#a"/>. A diff should show changes only toidvalues and their references — nothing structural. - Grep for leftovers. Search the output for unprefixed references:
grep -o 'url(#[a-z-]*)' output.svgshould return only prefixed names. - Render two files together. Embed two optimized SVGs inline in a test page and confirm gradients, clips, and animations resolve to their own file's elements. If a rule in your page CSS used to target an ID, confirm whether it still applies — it shouldn't, unless you updated the selector.
The takeaway: prefixIds is the right tool when you inline several SVGs into one document, as long as you give each file a distinct prefix, run it before ID-removing plugins, and handle external CSS/JS references yourself.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.