Paste SVG or choose a file, then clean it and select a component target. Review the preview because removing metadata, active elements or attributes can change the SVG.
Runs locally in your browserThis page cleans SVG markup in the browser and prints the same drawing as a React, Vue or Svelte component. Paste markup or choose a local .svg file, and the preview, the byte counters and the generated code all come from the cleaned element. Nothing is uploaded, and the page keeps working with the network switched off.
The cleanup is conservative about the drawing and strict about everything that can run or fetch: coordinates, ids, class names and local CSS are kept exactly as written, while scripts, animation elements, event attributes and remote references are removed. Check the preview before you use the result, because dropping an animation or a remote style rule changes what the file does.
Removed: <script>, <foreignObject>, <iframe>, <object>, <embed>, <audio>, <video> and the animation elements <animate>, <animateMotion>, <animateTransform> and <set>; every on* event attribute; href and xlink:href values that are not internal #id references; any attribute whose value calls a remote url(); <style> blocks that @import or call a remote url(); comments, <metadata> and <desc>. Kept: path data, ids, class names, presentation attributes such as rx, opacity and transform, and CSS that only points inside the file. In testing url(#g1) survived while style="fill:url(https://…)" did not.
Rewritten: a root without a viewBox gets one from its width and height, or 0 0 24 24 when neither is present; width and height are then dropped, xmlns is set, and whitespace between tags is collapsed. It is not a minifier — numbers are left alone, so a path that reads "M 4.000 4.0" keeps its zeros, and a small icon can grow once viewBox and xmlns are added. The example icon went from 310 to 248 bytes, and the tiles count UTF-8 bytes, so a CJK label is counted as the bytes it really takes.
React output is a function component that spreads {...props} onto the root, and attribute names are converted where the framework expects it: class becomes className and stroke-linecap, fill-opacity, font-size, text-anchor, stop-color or clip-path become camelCase, so the component does not print invalid-DOM-property warnings. Quoted values and CSS inside <style> are left as they are, and an icon with no children becomes a self-closing <svg … {...props} />.
Vue output is a single-file component with <script setup>, defineProps({ size }) and :width/:height bound to size; Svelte output uses export let size with width={size} height={size}. Both add aria-hidden, and React sets no size at all — its root keeps only the viewBox, so pass width and height as props or size it with CSS. The name field becomes a PascalCase identifier: an empty field is KivIcon, and a name that cannot start an identifier is prefixed, so 3d-icon exports Svg3dIcon.
There is no path optimisation and no id rewriting: two icons that both define id="g1" still collide on one page, exactly like hand-written SVG. Animation is deleted rather than preserved, and a <style> block that mixes a local rule with a remote url() is dropped whole, so check the preview when the original relied on a linked stylesheet.
The preview is an <img> fed by a blob URL, so scripts inside the markup cannot run; that makes it a faithful view of the cleaned file rather than a security audit. Invalid input is reported and the previous result is cleared instead of left on screen: the text needs exactly one <svg> root, and an undefined XML entity such as is rejected because an SVG is parsed as XML — é and the five predefined entities are accepted.