diff --git a/html/svg-to-react.html b/html/svg-to-react.html
index c0a093b..cd89303 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,86 @@ 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 props and ref
+ if (tagName === 'svg') {
+ attrs.forEach(attr => {
+ result += `\n${indent} ${attr.name}="${attr.value}"`;
+ });
+ result += `\n${indent} {...props}`;
+ result += `\n${indent} ref={ref}`;
+ result += `\n${indent}>`;
+ } else {
+ // 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;
+ }
+ }
+ }
+
+ children.forEach(child => {
+ if (child.nodeType === 1) {
+ result += '\n' + formatElement(child, indent + ' ');
+ } else {
+ result += child.textContent.trim();
+ }
+ });
+
+ result += `\n${indent}${tagName}>`;
+ return result;
+ }
+
+ return formatElement(svg, ' ');
+ }
+
function updateOutput() {
const input = inputEl.value.trim();