- ANALYSIS.md: Full architecture analysis of draw.io shape systems (XML stencils vs JS shapes, plugin loading, desktop/web support) - Example plugin with 5 custom shapes demonstrating JS logic: - StatusIndicator: conditional rendering based on status param - ProgressBar: computed geometry (percentage-based fill) - DataFlowArrow: dynamic arrows with direction/speed params - HexagonCluster: loop-based hexagonal grid generation - MetricGauge: radial gauge with arc computation + zones - Webpack build pipeline (npm run build / build:dev / watch) - Pre-built dist/drawio-custom-shapes.js ready for installation - Full README with installation instructions for desktop + web
draw.io Custom Shapes Example Plugin
A fully buildable example plugin demonstrating how to create custom draw.io shapes with JavaScript logic.
What This Is
A draw.io plugin that registers custom shapes with programmatic rendering — shapes that use conditionals, loops, computed geometry, and style-reactive behavior. Unlike XML stencils (which are purely declarative), these shapes have full JavaScript power.
Quick Start
# Install dependencies
npm install
# Development build (with source maps)
npm run build:dev
# Production build (minified)
npm run build
# Watch mode (rebuild on changes)
npm run watch
Output: dist/drawio-custom-shapes.js
Installation in draw.io
Desktop App (Electron)
- Build the plugin:
npm run build - Open draw.io desktop
- Extras > Plugins
- Enable external plugins if prompted
- Click "Select File..." → navigate to
dist/drawio-custom-shapes.js - Restart draw.io
- Find "Custom Shapes Example" palette in the sidebar
Self-Hosted (Docker)
- Build the plugin:
npm run build - Copy
dist/drawio-custom-shapes.jsto your draw.io plugins directory - Set environment:
DRAWIO_PLUGINS_ALLOW_CUSTOM=true - Add to configuration:
{ "plugins": ["/plugins/drawio-custom-shapes.js"] } - Restart the container
Web (URL parameter)
If you host the plugin file at a URL:
https://your-drawio.com/?p=YOUR_PLUGIN_KEY
Project Structure
├── src/
│ ├── index.js # Plugin entry point (Draw.loadPlugin wrapper)
│ ├── shapes/
│ │ ├── StatusIndicator.js # Conditional rendering based on status
│ │ ├── ProgressBar.js # Computed geometry (percentage-based)
│ │ ├── DataFlowArrow.js # Dynamic arrow with animation markers
│ │ ├── HexagonCluster.js # Loop-based hexagonal grid
│ │ └── MetricGauge.js # Radial gauge with computed arcs
│ └── palette.js # Sidebar palette registration
├── dist/ # Build output
├── webpack.config.js # Build configuration
├── package.json
├── ANALYSIS.md # Full architecture analysis
└── README.md
Shape Catalog
1. Status Indicator (customShapes.statusIndicator)
A shape that changes color and icon based on a status style parameter.
- Styles:
status=ok|warning|error|unknown - Demonstrates: conditional rendering, style reading
2. Progress Bar (customShapes.progressBar)
A horizontal bar that fills based on a percentage value.
- Styles:
progress=0..100,barColor=#4CAF50 - Demonstrates: computed geometry, value-based rendering
3. Data Flow Arrow (customShapes.dataFlowArrow)
A directional arrow with animated flow indicators.
- Styles:
direction=right|left|both,flowSpeed=fast|medium|slow - Demonstrates: dynamic path generation, pattern rendering
4. Hexagon Cluster (customShapes.hexCluster)
A grid of hexagons that adapts to the cell size.
- Styles:
hexCount=7,hexFill=#E3F2FD - Demonstrates: loop-based drawing, mathematical geometry
5. Metric Gauge (customShapes.metricGauge)
A radial gauge (speedometer-style) showing a value within a range.
- Styles:
value=75,minVal=0,maxVal=100,zones=green,yellow,red - Demonstrates: arc computation, gradient zones, text rendering
Customizing
Adding a New Shape
- Create a new file in
src/shapes/YourShape.js:
import { registerShape, getValue } from '../utils';
export function YourShapeName() {
mxShape.call(this);
}
mxUtils.extend(YourShapeName, mxShape);
YourShapeName.prototype.paintVertexShape = function(c, x, y, w, h) {
c.translate(x, y);
// Read style parameters
var myParam = getValue(this.style, 'myParam', 'default');
// Draw with full JS logic
c.begin();
c.moveTo(0, 0);
c.lineTo(w, h);
c.stroke();
};
// Register the shape
registerShape('customShapes.yourShape', YourShapeName);
- Import it in
src/index.js - Add a palette entry in
src/palette.js - Rebuild:
npm run build
Style Parameters
Shapes read parameters from the cell's style string:
shape=customShapes.yourShape;myParam=value;fillColor=#fff;
Access in code:
var val = mxUtils.getValue(this.style, 'myParam', 'default');
Architecture Notes
See ANALYSIS.md for the full technical analysis of how draw.io's shape systems work, including the stencil registry, cell renderer, and plugin loading mechanisms.
License
MIT