From 9fc4f3c778260eefe34859fb3fe4484eda4e5b14 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 14 Jan 2026 07:41:24 +0000 Subject: [PATCH 1/2] Update SVG to React converter with improved output format - Change output to use TypeScript types with separate imports - Use arrow function with forwardRef pattern - Format SVG attributes on separate lines with ref and props spread - Change layout to stack textareas vertically --- html/svg-to-react.html | 79 +++++++++++++++++++++++++++++++++--------- 1 file changed, 63 insertions(+), 16 deletions(-) diff --git a/html/svg-to-react.html b/html/svg-to-react.html index c0a093b..2ed6938 100644 --- a/html/svg-to-react.html +++ b/html/svg-to-react.html @@ -32,8 +32,8 @@ } .container { - display: grid; - grid-template-columns: 1fr 1fr; + display: flex; + flex-direction: column; gap: 20px; margin-bottom: 20px; } @@ -118,11 +118,6 @@ text-decoration: underline; } - @media (max-width: 768px) { - .container { - grid-template-columns: 1fr; - } - } @@ -260,23 +255,75 @@

SVG to React

reactSvg = reactSvg.replace(/\sxmlns(:\w+)?="[^"]*"/g, ''); reactSvg = reactSvg.trim(); - // Add {...props} and ref to the SVG element - // Props come before ref so hardcoded attributes can be overridden, but ref is always preserved - reactSvg = reactSvg.replace(/^]*?)>/, ''); + // Format SVG with proper indentation + reactSvg = formatSvg(reactSvg); - const component = `import { forwardRef } from 'react'; + const component = `import type { Ref, SVGProps } from "react"; +import { forwardRef } from "react"; -export default forwardRef>( - function Icon(props, ref) { +const Icon = forwardRef( + (props: SVGProps, ref: Ref) => { return ( - ${reactSvg} +${reactSvg} ); - } -);`; + }, +); + +export default Icon;`; return component; } + function formatSvg(svgString) { + const parser = new DOMParser(); + const doc = parser.parseFromString(svgString, 'image/svg+xml'); + const svg = doc.querySelector('svg'); + + function formatElement(element, indent) { + const tagName = element.tagName; + const attrs = Array.from(element.attributes); + const children = Array.from(element.childNodes).filter( + node => node.nodeType === 1 || (node.nodeType === 3 && node.textContent.trim()) + ); + + let result = `${indent}<${tagName}`; + + // For SVG element, add attributes on separate lines, then ref and props + if (tagName === 'svg') { + attrs.forEach(attr => { + result += `\n${indent} ${attr.name}="${attr.value}"`; + }); + result += `\n${indent} ref={ref}`; + result += `\n${indent} {...props}`; + result += `\n${indent}>`; + } else { + // For other elements, put attributes inline + attrs.forEach(attr => { + result += `\n${indent} ${attr.name}="${attr.value}"`; + }); + if (attrs.length > 0) { + result += `\n${indent}/>`; + return result; + } + result += ' />'; + return result; + } + + children.forEach(child => { + if (child.nodeType === 1) { + result += '\n' + formatElement(child, indent + ' '); + } else { + result += child.textContent.trim(); + } + }); + + result += `\n${indent}`; + return result; + } + + return formatElement(svg, ' '); + } + function updateOutput() { const input = inputEl.value.trim(); From 4ff45605da5e74eb07f778cbe5bcc518f3ea8447 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 14 Jan 2026 08:30:43 +0000 Subject: [PATCH 2/2] Fix SVG formatting logic for child elements - Put {...props} before ref={ref} to maintain ref precedence - Use inline attributes for elements with 2 or fewer attributes - Put many attributes on separate lines for readability - Properly handle elements with and without children --- html/svg-to-react.html | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/html/svg-to-react.html b/html/svg-to-react.html index 2ed6938..cd89303 100644 --- a/html/svg-to-react.html +++ b/html/svg-to-react.html @@ -288,25 +288,36 @@

SVG to React

let result = `${indent}<${tagName}`; - // For SVG element, add attributes on separate lines, then ref and props + // For SVG element, add attributes on separate lines, then props and ref if (tagName === 'svg') { attrs.forEach(attr => { result += `\n${indent} ${attr.name}="${attr.value}"`; }); - result += `\n${indent} ref={ref}`; result += `\n${indent} {...props}`; + result += `\n${indent} ref={ref}`; result += `\n${indent}>`; } else { - // For other elements, put attributes inline - attrs.forEach(attr => { - result += `\n${indent} ${attr.name}="${attr.value}"`; - }); - if (attrs.length > 0) { - result += `\n${indent}/>`; + // For other elements + if (attrs.length === 0) { + result += ' />'; + return result; + } else if (attrs.length <= 2 && children.length === 0) { + // Put few attributes inline for simple elements + attrs.forEach(attr => { + result += ` ${attr.name}="${attr.value}"`; + }); + result += ' />'; return result; + } else { + // Many attributes on separate lines + attrs.forEach(attr => { + result += `\n${indent} ${attr.name}="${attr.value}"`; + }); + result += children.length > 0 ? `\n${indent}>` : `\n${indent}/>`; + if (children.length === 0) { + return result; + } } - result += ' />'; - return result; } children.forEach(child => {