โšกToolZip
๋ธ”๋กœ๊ทธ๐Ÿ› ๏ธ ๋„๊ตฌ ์‚ฌ์šฉํ•˜๊ธฐ
โ† Blog
๐Ÿ”ง utilOctober 1, 2026

Building a Live SVG Editor in the Browser โ€” Syntax Highlighting Without a Library

#javascript#svg#webdev#toolzip

Building a Live SVG Editor in the Browser โ€” Syntax Highlighting Without a Library

A live SVG editor sounds simple: textarea on the left, preview on the right, update on change. In practice, there are several sharp edges.

Rendering SVG Safely

The naive approach โ€” innerHTML = userInput โ€” is an XSS vector. SVG can contain <script> tags and event handlers.

The safer approach uses DOMParser:

function renderSVG(svgString, container) {
  const parser = new DOMParser();
  const doc = parser.parseFromString(svgString, 'image/svg+xml');

  const parserError = doc.querySelector('parsererror');
  if (parserError) {
    return { ok: false, error: parserError.textContent };
  }

  // Remove script elements
  doc.querySelectorAll('script').forEach(el => el.remove());

  // Remove event handlers
  doc.querySelectorAll('*').forEach(el => {
    [...el.attributes].forEach(attr => {
      if (attr.name.startsWith('on')) el.removeAttribute(attr.name);
    });
  });

  container.innerHTML = '';
  container.appendChild(doc.documentElement.cloneNode(true));
  return { ok: true };
}

Export to PNG

SVG โ†’ PNG conversion in the browser requires an intermediate step through an <img> element:

async function svgToPng(svgString, width, height) {
  const blob = new Blob([svgString], { type: 'image/svg+xml' });
  const url = URL.createObjectURL(blob);

  return new Promise((resolve) => {
    const img = new Image();
    img.onload = () => {
      const canvas = document.createElement('canvas');
      canvas.width = width;
      canvas.height = height;
      canvas.getContext('2d').drawImage(img, 0, 0, width, height);
      URL.revokeObjectURL(url);
      resolve(canvas.toDataURL('image/png'));
    };
    img.src = url;
  });
}

ViewBox and Zoom

SVG has its own coordinate system defined by viewBox. When you zoom, you're scaling the container, not changing the viewBox. This keeps the SVG coordinates stable:

function setZoom(containerEl, scale) {
  containerEl.style.transform = `scale(${scale})`;
  containerEl.style.transformOrigin = 'top left';
}

Snippet Insertion

For quick edits, I added insert buttons for common shapes. The trick is inserting at the cursor position in the textarea:

function insertAtCursor(textarea, snippet) {
  const start = textarea.selectionStart;
  const end = textarea.selectionEnd;
  const before = textarea.value.slice(0, start);
  const after = textarea.value.slice(end);
  textarea.value = before + snippet + after;
  textarea.selectionStart = textarea.selectionEnd = start + snippet.length;
  textarea.dispatchEvent(new Event('input'));
}

Live at toolzip.app/tools/svg-preview.

๐Ÿ› ๏ธ
Try the tool now
No install ยท Free ยท No server upload
Open tool โ†’
Building a Live SVG Editor in the Browser โ€” Syntax Highlighting Without a Library โ€” ToolZip | ToolZip