Skip to content

Convert DOM elements to clean, self-contained SVG files.

License

Notifications You must be signed in to change notification settings

milanofthe/dom2svg

Repository files navigation

dom2svg

Convert DOM elements to clean, self-contained SVG files.

Built for exporting node-based editors (SvelteFlow, React Flow, etc.) to vector graphics. Handles mixed HTML/SVG structures that existing libraries fail on.

Demo

A node-based pipeline editor — DOM on the left, exported SVG on the right:

Pipeline editor export

Real-world UI components (stat cards, avatars, alerts, buttons, progress bars, charts, palettes, tables, toasts, tags, toggles, breadcrumbs, donut charts, skeleton loaders, keyboard shortcuts):

Real-world components

55+ CSS features rendered as DOM elements and their SVG counterparts:

Feature showcase

Install

npm install github:milanofthe/dom2svg

CLI — Vector Screenshots

Capture any web page as a vector SVG from the command line. Uses headless Chrome via Puppeteer to load the page, then runs dom2svg against it.

Requires Chrome/Chromium installed locally. Puppeteer-core is included as a dev dependency.

# Full page
npx dom2svg https://example.com -o page.svg

# Specific element with background and padding
npx dom2svg https://news.ycombinator.com -s "#hnmain" -b white -p 12 -o hn.svg

# Pipe to stdout
npx dom2svg https://example.com > page.svg

# Wait for JS-rendered content
npx dom2svg https://app.example.com --wait 3000 -o app.svg

# Custom viewport size
npx dom2svg https://example.com --width 1920 --height 1080 -o wide.svg

Options:

Flag Description Default
-o, --output <file> Output file (default: stdout) stdout
-s, --selector <css> CSS selector for target element body
-b, --background <color> SVG background color transparent
-p, --padding <px> Padding around the element 0
--width <px> Viewport width 1280
--height <px> Viewport height 800
--wait <ms> Extra wait after page load 0
--chrome <path> Chrome executable path auto-detect

Chrome is auto-detected on Windows, macOS, and Linux. Override with --chrome or the CHROME_PATH environment variable.

Quick Start

import { domToSvg } from "dom2svg";

const element = document.querySelector("#my-editor");
const result = await domToSvg(element);

// Download as .svg file
result.download("export.svg");

Examples

Export with white background and padding

const result = await domToSvg(element, {
  background: "#ffffff",
  padding: 20,
});

Download or get the SVG as a string/blob

const result = await domToSvg(element);

// Trigger browser download
result.download("diagram.svg");

// Get SVG markup (e.g. for saving to a server)
const svgString = result.toString();

// Get a Blob (e.g. for FormData upload)
const blob = result.toBlob();

Exclude elements from export

// By CSS selector
const result = await domToSvg(element, {
  exclude: ".toolbar, .minimap, [data-no-export]",
});

// By predicate
const result = await domToSvg(element, {
  exclude: (el) => el.tagName === "BUTTON",
});

Text-to-path (font-independent output)

Convert text to <path> elements so the SVG renders identically without any fonts installed. Requires opentype.js (bundled dependency).

const result = await domToSvg(element, {
  textToPath: true,
  fonts: {
    // Simple: family → URL
    "Inter": "/fonts/Inter-Regular.woff2",

    // Detailed: family → config with weight/style
    "Inter": {
      url: "/fonts/Inter-Bold.woff2",
      weight: "700",
      style: "normal",
    },
  },
});

Custom element handler

Override rendering for specific elements:

const result = await domToSvg(element, {
  handler: (el, ctx) => {
    // Replace a placeholder with a custom SVG shape
    if (el.classList.contains("chart-placeholder")) {
      const circle = ctx.svgDocument.createElementNS(
        "http://www.w3.org/2000/svg",
        "circle",
      );
      circle.setAttribute("cx", "50");
      circle.setAttribute("cy", "50");
      circle.setAttribute("r", "40");
      circle.setAttribute("fill", "#3b82f6");
      return circle;
    }
    return null; // fall through to default rendering
  },
});

Export a SvelteFlow editor

<script>
  import { domToSvg } from "dom2svg";

  async function exportDiagram() {
    const editor = document.querySelector(".svelte-flow");
    const result = await domToSvg(editor, {
      background: "#ffffff",
      padding: 24,
      exclude: ".svelte-flow__controls, .svelte-flow__minimap",
    });
    result.download("diagram.svg");
  }
</script>

<button onclick={exportDiagram}>Export SVG</button>

Export a React Flow editor

import { domToSvg } from "dom2svg";

function ExportButton() {
  const handleExport = async () => {
    const editor = document.querySelector(".react-flow");
    const result = await domToSvg(editor, {
      background: "#ffffff",
      padding: 24,
      exclude: ".react-flow__controls, .react-flow__minimap",
    });
    result.download("flowchart.svg");
  };

  return <button onClick={handleExport}>Export SVG</button>;
}

API

domToSvg(element, options?)

Converts a DOM element tree into a self-contained SVG.

Parameters:

Parameter Type Description
element Element The root DOM element to convert
options DomToSvgOptions Optional configuration (see below)

Returns: Promise<DomToSvgResult>

Property Type Description
svg SVGSVGElement The generated SVG element
toString() string Serialized SVG with XML declaration
toBlob() Blob SVG as image/svg+xml blob
download(filename?) void Triggers browser download

Options

interface DomToSvgOptions {
  /** Background color for the SVG (default: transparent) */
  background?: string;

  /** Padding around the captured area in px (default: 0) */
  padding?: number;

  /** CSS selector or predicate to exclude elements */
  exclude?: string | ((element: Element) => boolean);

  /** Convert text to <path> elements using opentype.js (default: false) */
  textToPath?: boolean;

  /** Font mapping for text-to-path (family name → URL or config) */
  fonts?: Record<string, string | { url: string; weight?: string; style?: string }>;

  /** Custom element handler — return SVGElement to override, null for default */
  handler?: (element: Element, context: RenderContext) => SVGElement | null;
}

Supported CSS Features

Feature Support
Backgrounds
Background colors Full
Linear gradients Full (correct diagonal angles on non-square elements)
Radial gradients Full (circle and ellipse, rasterized via Canvas)
Conic gradients Full (rasterized via Canvas)
Multiple backgrounds Full (layered in correct CSS order)
Background size/position Full (contain, cover, explicit sizes)
Background images (url()) Full (inlined as data URLs)
Borders & Outlines
Borders (uniform and per-side) Full (solid, dashed, dotted)
Border radius (uniform and non-uniform) Full (including pill shapes)
Outline Full (solid, dashed, dotted with offset)
Shadows
Box shadow Full (outer and inset, multiple, spread, blur)
Text shadow Full (single and multiple, via SVG filters)
CSS Filters
blur() Full (via feGaussianBlur)
brightness() Full (via feComponentTransfer)
contrast() Full (via feComponentTransfer)
drop-shadow() Full (via feDropShadow)
grayscale() Full (via feColorMatrix)
hue-rotate() Full (via feColorMatrix)
invert() Full (via feComponentTransfer)
opacity() Full (via feComponentTransfer)
saturate() Full (via feColorMatrix)
sepia() Full (via feColorMatrix)
Filter chaining Full (multiple filters compose in order)
Layout & Clipping
CSS transforms Full (translate, rotate, scale, skew, matrix)
Transform origin Full
Opacity Full
Overflow clipping Full (hidden, clip, scroll, auto)
clip-path Full (inset, circle, ellipse, polygon, path)
Z-index / stacking contexts Full (CSS 2.2 paint order)
Elements
Inline SVGs Full (deep clone with ID namespacing)
<img> elements Full (inlined as data URLs, border-radius clipping)
<canvas> elements Full (via toDataURL())
Form elements Full (<input>, <select>, <textarea> with multiline)
Pseudo-elements (::before, ::after) Full (text content, browser-measured positioning)
List markers Full (disc, circle, square, decimal)
Text
Text rendering Full (<text> elements, or <path> with textToPath)
Text decoration Full (underline, line-through)
Text transform Full (uppercase, lowercase, capitalize)
Text overflow (ellipsis) Full (appends ellipsis character)
Letter spacing Full
Visibility
visibility: hidden Correctly skipped (children still rendered)
display: none Correctly skipped
opacity: 0 Rendered as group with opacity="0" (subtree preserved)

Architecture

src/
├── index.ts              # domToSvg() entry point
├── types.ts              # All TypeScript interfaces
├── core/
│   ├── traversal.ts      # DOM tree walking
│   └── styles.ts         # CSS property parsing
├── renderers/
│   ├── html-element.ts   # HTML → SVG (backgrounds, borders, overflow, pseudo)
│   ├── svg-element.ts    # SVG cloning with ID namespacing
│   └── text-node.ts      # Text → <text> or <path>
├── assets/
│   ├── images.ts         # Image/canvas → data URL inlining
│   ├── fonts.ts          # Font loading + text-to-path (opentype.js)
│   ├── gradients.ts      # CSS gradient → SVG gradient / rasterized image
│   ├── filters.ts        # CSS filters → SVG filters (blur, grayscale, sepia, etc.)
│   ├── box-shadow.ts     # box-shadow → SVG filter / mask
│   ├── clip-path.ts      # clip-path → SVG clipPath
│   └── text-shadow.ts    # text-shadow → SVG filter
├── transforms/
│   ├── parse.ts          # CSS transform string parsing
│   ├── matrix.ts         # 2D affine matrix operations
│   └── svg.ts            # CSS transform → SVG transform attribute
└── utils/
    ├── dom.ts            # SVG namespace helpers, DOM type guards
    ├── id-generator.ts   # Unique ID generation
    └── geometry.ts       # Bounding box and rounded-rect path utilities

Single runtime dependency: opentype.js (only used when textToPath is enabled).

The CLI additionally uses puppeteer-core for headless Chrome automation.

Development

npm install
npm run build        # ESM + CJS + CLI bundles via tsup
npm run type-check   # TypeScript strict mode
npm run test         # 211 unit tests via Vitest
npm run test:watch   # Watch mode
npm run demo         # Visual demo at localhost:5173

License

MIT

About

Convert DOM elements to clean, self-contained SVG files.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published