SVG to Svelte: $restProps, runes and dynamic fills
Svelte sits at the opposite pole from Vue: the markup needs no rewriting at all, and absolutely nothing is forwarded for you. A Svelte component that renders an svg and ignores what the caller passes is the default; making it behave like an element takes exactly one spread — written one way in Svelte 4 and another in Svelte 5's runes mode.
1. The markup survives untouched
Svelte components are HTML files with extra powers, so SVG attributes stay exactly as the exporter wrote them — stroke-width, fill-rule, clip-path, all kebab-case, all legal. The component's name is the file's name; nothing inside the markup carries it. The SVG to Svelte tool therefore changes only one thing about your file: it adds the spread that the next section is about.
2. Nothing falls through until you spread
Pass class or aria-hidden to a bare Svelte component and it lands nowhere — Svelte drops attributes the component does not handle. The fix is declared once, on the root:
<!-- Icon.svelte --> <svg viewBox="0 0 24 24" fill="currentColor" {...$restProps}>…</svg> <!-- caller --> <Icon class="nav__icon" aria-hidden="true" />
Position is meaning: the spread comes after the file's own attributes, so anything the caller passes wins a clash — pass fill="none" and the file's currentColor loses, which is what a caller expects. Unlike Vue, nothing merges: a caller class replaces the file's class rather than joining it, so keep structural styling off the root or accept the override.
3. The same component in Svelte 5 runes
$restProps works in Svelte 3, 4 and 5's legacy mode. A component written for runes mode declares its inputs with $props() and collects the remainder explicitly:
<script> let { accent = '#2f6df6', ...rest } = $props(); </script> <svg viewBox="0 0 24 24" {...rest}> <path fill={accent} d="…" /> </svg>
Same behaviour, different declaration: named things are consumed as props, the rest rides through to the root. TypeScript is lang="ts" on the script tag plus a type on the destructure — no separate interface ceremony required.
4. Dynamic fills, in the same order as everywhere
The hierarchy does not change because the framework did: first currentColor, which needs no prop and follows the CSS colour of the container (the colour guide explains why that beats every alternative); then a single accent prop for a genuinely independent second tone — export let accent = '#2f6df6' in Svelte 4, the $props() default above in runes mode. Convert a file's hard-coded fills to currentColor with the Color Replacer before converting the file itself — the order matters, because afterwards the fills are code you would be editing by hand.
5. Gradient ids are still document-global
Two instances of an icon that defines id="g1" still fight over one definition — components do not scope SVG ids, in Svelte or anywhere. Strip the ids nothing references with the Optimizer; for defs the file actually uses, derive a per-instance id ($props.id() in Svelte 5, or a module-level counter in 4) and reference it as url(#${id}). The full anatomy of the collision — two icons, one gradient, both wrong — is in the React guide and applies unchanged.