Transform any SVG into an interactive form with embedded input/output fields. Works with draw.io, Figma, Inkscape, and custom SVGs.
npm install svg-interactiveTransform ANY SVG diagram into an interactive form with embedded input/output fields.




Works seamlessly with draw.io, Figma, Inkscape, or custom SVGs. No vendor lock-in.
---
Ever wanted to make your SVG diagrams interactive but faced these problems?
- ❌ Locked into one tool's proprietary format
- ❌ Can't embed HTML inputs directly in SVG
- ❌ Complex setup just to add a few interactive fields
- ❌ Have to rebuild your diagram from scratch
This library solves all of that:
- ✅ Works with ANY SVG tool - no vendor lock-in
- ✅ Uses SVG foreignObject to embed real HTML inputs
- ✅ Dead simple setup - just add IDs or data attributes
- ✅ Keep using your favorite design tool
``bash`
npm install svg-interactiveor
yarn add svg-interactive
1. Add IDs to your SVG (in Inkscape, Figma, or any editor):
`svg`
2. Use in React:
`tsx
import { SvgInteractive, parseSVG } from 'svg-interactive';
// Step 1: Parse your SVG to extract field mappings
const svgContent = await fetch('/diagram.svg').then(r => r.text());
const { mappings } = parseSVG(svgContent, {
patterns: [
{ prefix: 'input-', type: 'input' },
{ prefix: 'output-', type: 'output' }
]
});
// Step 2: Render the interactive SVG
svgContent={svgContent}
onOutputCompute={(inputs) => ({
result: You entered: ${inputs.temperature}`
})}
/>
That's it! Your SVG now has interactive input/output fields.
The library uses SVG's element to overlay HTML inputs at exact positions:
1. Load your SVG
2. Find elements with matching IDs
3. Get their bounding boxes
4. Create overlays
5. Render HTML inputs/outputs inside
The library matches SVG elements using attribute-based patterns - you can match on any SVG attribute (id, class, data-*, etc.):
`svg`
`tsx`
const { mappings } = parseSVG(svgContent, {
patterns: [
{ prefix: 'input-', type: 'input' }, // Matches id="input-*"
{ prefix: 'output-', type: 'output' }
]
});
For draw.io SVGs, use the dedicated parser which automatically looks for custom metadata:
In draw.io:
1. Right-click shape → Edit Data
2. Add property: data-id = input-field-temp
3. Export as SVG
`tsx
import { parseDrawIoSVG } from 'svg-interactive';
const { mappings } = parseDrawIoSVG(svgContent, {
patterns: [
{ prefix: 'input-field-', type: 'input' },
{ prefix: 'output-field-', type: 'output' }
]
});
`
`tsx`
const { mappings } = parseSVG(svgContent, {
patterns: [
{ attribute: 'class', prefix: 'field-input-', type: 'input' },
{ attribute: 'data-field', regex: /^output-(.+)$/, type: 'output' }
]
});
When you have a fixed set of elements with specific IDs:
`svg`
`tsx`
const { mappings } = parseSVG(svgContent, {
patterns: [
{ ids: ['temperature', 'pressure', 'volume'], type: 'input' }
]
});
Use cases for ids array:
- Fixed set of known elements
- Legacy SVGs with inconsistent naming
- When you don't control the ID naming convention
- Maximum explicitness and control (e.g., 100+ specific sensors in a complex diagram)
- 🎨 Universal SVG Support - draw.io, Figma, Inkscape, Adobe Illustrator, hand-coded
- 🔄 Flexible Matching - Match any SVG attribute (id, class, data-*, etc.)
- 🎯 Pattern-Based - Prefix or regex matching with per-pattern configuration
- ⚛️ React 18+ - Built with modern React and createRoot API
- 🎨 Fully Customizable - Themes, CSS variables, custom components
- 📊 Debug Mode - Built-in debugging panel
- 💪 TypeScript - Complete type definitions with strict mode
- 🚀 Next.js Ready - Works out of the box
This repository now ships with a polished React + Vite site (see /site) that showcases every example and includes a fully interactive playground:
- npm run site:dev — run the landing page locally with hot reloadnpm run site:build
- — produce the static assets under site/distnpm run site:preview
- — preview the production build
The site imports the real components and presets from /examples so content always stays in sync with the package. A dedicated workflow (.github/workflows/site.yml) builds the site on every push to main and deploys it to GitHub Pages—just enable “GitHub Actions” under Settings → Pages and you’ll have a live playground/landing page with zero manual steps.
`tsx
import { SvgInteractive, parseSVG } from 'svg-interactive';
const svgContent = await fetch('/calculator.svg').then(r => r.text());
const { mappings } = parseSVG(svgContent, {
patterns: [
{ prefix: 'input-', type: 'input' },
{ prefix: 'output-', type: 'output' }
]
});
svgContent={svgContent}
onOutputCompute={(inputs) => ({
sum: (parseFloat(inputs.a || '0') + parseFloat(inputs.b || '0')).toString()
})}
theme="bordered"
inputClassName="px-2 py-1 border-2 border-blue-500 rounded"
outputClassName="px-2 py-1 bg-green-50 border-2 border-green-500 rounded"
/>
`
`tsx
import { SvgInteractive, parseDrawIoSVG } from 'svg-interactive';
const svgContent = await fetch('/diagram.svg').then(r => r.text());
const { mappings } = parseDrawIoSVG(svgContent, {
patterns: [
{ prefix: 'input-', type: 'input' },
{ prefix: 'output-', type: 'output' }
]
});
svgContent={svgContent}
renderInput={(props) => (
type="number"
value={props.value}
onChange={(e) => props.onChange(e.target.value)}
className="custom-input"
min="0"
max="100"
/>
)}
renderOutput={(props) => (
$3
`tsx
mappings={mappings}
svgContent={svgContent}
theme="none"
inputClassName="w-full h-full px-3 py-2 border-2 border-blue-500 rounded-lg focus:ring-2"
outputClassName="w-full h-full px-3 py-2 bg-green-50 border-2 border-green-500 rounded-lg"
/>
`📐 SVG Preparation
Quick guides for each tool:
- draw.io / diagrams.net - Use data-id attributes
- Figma - Rename layers with prefixes
- Inkscape - Set element IDs via Object Properties
- Adobe Illustrator - Use Layers panel names
- Hand-coded SVG - Just add
id attributes🔧 API Reference
$3
First, parse your SVG to extract field mappings.
parseSVG will auto-detect Draw.io SVGs.`typescript
// Generic parser for all SVG types
parseSVG(svgContent: string, options: ParseOptions): ParseResult// Optimized parser for Draw.io SVGs
parseDrawIoSVG(svgContent: string, options: ParseOptions): ParseResult
`ParseOptions:
`typescript
interface ParseOptions {
patterns: FieldPattern[]; // Field matching patterns
mode?: 'data-id' | 'direct-id'; // Optional: force specific mode
}// FieldPattern is a discriminated union - must have exactly ONE matching strategy
type FieldPattern =
| { prefix: string; type: 'input' | 'output'; attribute?: string } // Match by prefix
| { regex: RegExp; type: 'input' | 'output'; attribute?: string } // Match by regex
| { ids: string[]; type: 'input' | 'output'; attribute?: string }; // Match by ID list
`ParseResult:
`typescript
interface ParseResult {
mappings: FieldMapping[]; // Use this for SvgInteractive
errors: string[]; // Any parsing errors encountered
metadata: {
tool: 'drawio' | 'generic'; // drawio or generic (Figma, Inkscape, etc.)
detectedMode: 'data-id' | 'direct-id';
attributesUsed: string[]; // Attributes queried during parsing
};
}
`$3
| Prop | Type | Required | Description |
|------|------|----------|-------------|
|
mappings | FieldMapping[] | Yes | Field mappings from parser |
| svgContent | string | Yes | Raw SVG string |
| onInputChange | (name, value, all) => void | No | Fired when any input changes |
| onOutputCompute | (inputs) => outputs | No | Compute all outputs from inputs |
| outputValues | Record | No | Controlled output values |
| onOutputUpdate | Record | No | Per-field output callbacks |
| renderInput | (props) => ReactNode | No | Custom input renderer |
| renderOutput | (props) => ReactNode | No | Custom output renderer |
| theme | 'default' \| 'minimal' \| 'bordered' \| 'none' | No | Built-in theme |
| inputClassName | string | No | CSS class for inputs |
| outputClassName | string | No | CSS class for outputs |
| inputStyle | CSSProperties | No | Inline styles for inputs |
| outputStyle | CSSProperties | No | Inline styles for outputs |
| debug | boolean | No | Show debug panel |
| onDebugInfo | (info) => void | No | Debug callback |🎨 Styling
$3
`tsx
// Blue inputs, green outputs
// Simple borders
// Bold borders with shadows
// No default styling
`$3
`css
:root {
--svg-input-border: #3B82F6;
--svg-input-bg: #FFFFFF;
--svg-output-border: #10B981;
--svg-output-bg: #F0FDF4;
--svg-field-font-size: 12px;
}
`$3
`tsx
import 'svg-interactive/styles';
`🐛 Debug Mode
`tsx
debug={true}
onDebugInfo={(info) => {
console.log('Mode:', info.matchingMode); // 'data-id' or 'direct-id'
console.log('Fields:', info.totalFields); // Total fields found
console.log('Inputs:', info.inputFields); // Input fields
console.log('Outputs:', info.outputFields); // Output fields
}}
/>
`🌐 Browser Support
✅ Chrome/Edge | ✅ Firefox | ✅ Safari
Requires SVG
foreignObject support (all modern browsers).🤝 Contributing
Contributions are welcome! Please read CONTRIBUTING.md for details.
$3
`bash
git clone https://github.com/m98/svg-interactive
cd svg-interactive
npm install
npm run dev # Watch mode
npm test # Run tests
npm run quality # Full quality check
``MIT © Mohammad
- NPM Package
- GitHub Repository
- Issue Tracker
- Changelog
If this library helped you, please consider giving it a ⭐ on GitHub!
---
Built with ❤️ for the open-source community