Repository navigation
Shapes and Stencils
This page explains how draw.io draws a shape from the shape key of a cell style, and how to add your own: JavaScript shape classes, XML stencils, inline stencils and image shapes, plus the user-level custom libraries. It is for developers who write shapes for a fork, a plugin or a shape library. For the style syntax itself see Styles; for putting shapes into the sidebar see Extending the editor.
| Kind | Defined by | Style | Strengths | Limits |
|---|---|---|---|---|
| JavaScript shape | A class registered with mxCellRenderer.registerShape
|
shape=myShape |
Any drawing logic, style-driven parameters, custom handles, computed connection points | Must be loaded wherever the diagram is rendered |
| XML stencil | A <shape> element in a stencil set, registered with mxStencilRegistry
|
shape=mxgraph.lib.name, or shape=stencil(...) inline |
Declarative, scales with the cell, colours from the style, editable in the Edit Shape dialog | Fixed geometry, no logic |
| Image | Any image URL or data URI | shape=image;image=... |
Icons and logos, no code | Not styleable beyond the image itself (SVG colours can be edited) |
The built-in libraries use all three: JavaScript shapes for parametric shapes (for example in mxBasic.js), stencils for most icon sets, and image files under src/main/webapp/img/lib for others (clip art, azure2, ibm, sap and more).
mxCellRenderer.prototype.createShape (mxCellRenderer.js) reads style.shape and:
- If the name starts with
stencil(, the override in Graph.js decompresses the stencil XML in the parentheses and draws it (inline stencils). - If a class is registered under the name (
mxCellRenderer.defaultShapes), it creates an instance. - Otherwise it asks
mxStencilRegistry.getStencil(name). draw.io's version of this function inGraph.jsloads stencil sets on demand: for a namemxgraph.<lib>.<...>.<shape>,getBasenameForStencilreturns<lib>/<...>, and the files listed inmxStencilRegistry.libraries[<basename>]are loaded (.xmlstencil sets and.jsshape files), or, if there is no entry,STENCIL_PATH/<basename>.xml. - If nothing matches, the cell is drawn with the default shape (a rectangle for vertices).
Stencil names are registered in lower case with spaces replaced by _ (mxStencilRegistry.parseStencilSet), so the style must use the lower-case form. An unknown shape is not an error: the diagram opens and the cell is a rectangle. This is what users see when a diagram with custom shapes is opened somewhere the shape is not loaded.
Connection points come from, in this order: the points style key, the shape's getConstraints(style, w, h), the stencil's <connections>, and the shape's constraints array (see the getAllConnectionConstraints override in Graph.js).
The core shapes are in mxgraph/src/shape and registered in mxCellRenderer.js; draw.io adds about a hundred in grapheditor/Shapes.js (cube, step, note, table, umlLifeline, ...). The shape libraries add their own in src/main/webapp/shapes (mxBasic.js registers mxgraph.basic.*, mxDFD.js mxgraph.dfd.*, and so on). The merge target of etc/build/build.xml concatenates shapes/**/*.js into js/shapes-14-6-5.min.js, which production mode, the desktop app and viewer-static.min.js load as a whole; in development mode the files are loaded on demand through mxStencilRegistry.libraries (see Building).
A shape class:
function TagShape()
{
mxActor.call(this);
};
mxUtils.extend(TagShape, mxActor);
// Default for the 'size' style key
TagShape.prototype.size = 20;
// mxActor.paintVertexShape translates to (x, y), begins a path,
// calls redrawPath and fills and strokes the path
TagShape.prototype.redrawPath = function(c, x, y, w, h)
{
var s = Math.max(0, Math.min(w, parseFloat(
mxUtils.getValue(this.style, 'size', this.size))));
c.moveTo(0, 0);
c.lineTo(w - s, 0);
c.lineTo(w, h / 2);
c.lineTo(w - s, h);
c.lineTo(0, h);
c.close();
};
// Fixed connection points (x and y relative to the bounds)
TagShape.prototype.constraints = [
new mxConnectionConstraint(new mxPoint(0, 0.5), true),
new mxConnectionConstraint(new mxPoint(1, 0.5), true)];
mxCellRenderer.registerShape('myTag', TagShape);Use it with the style shape=myTag;whiteSpace=wrap;html=1;size=30;. The parts:
-
Base class. Extend
mxShapeand implementpaintVertexShape(c, x, y, w, h), or extendmxActorand implementredrawPath(c, x, y, w, h)for a single filled path.mxCylinder,mxRectangleShapeand the classes inShapes.jsare further bases. -
Canvas.
cis anmxAbstractCanvas2D(SVG on screen, other canvases for export):moveTo,lineTo,quadTo,curveTo,arcTo,close,begin,end,fill,stroke,fillAndStroke,rect,roundrect,ellipse,text,image,translate,rotate,save,restore, and theset...functions for colours, dashes, alpha and shadows. Callc.setShadow(false)before details that should not cast a shadow. -
Style values. Read your own keys with
mxUtils.getValue(this.style, key, default).this.fill,this.strokeandthis.strokewidthalready hold the resolved colours and width. -
Connection points. A
constraintsarray ofmxConnectionConstraint(point, perimeter, name, dx, dy), orgetConstraints(style, w, h)to compute them from the size. -
Handles. In the editor,
Graph.handleFactory[name] = function(state) { return [Graph.createHandle(state, keys, getPositionFn, setPositionFn)]; }adds yellow handles that edit style keys.Graph.handleFactoryandGraph.createHandleare defined in every bundle (as empty stand-ins where the handle code is not included), so the assignment is safe in the viewer too. -
Format panel.
customPropertieson the prototype (an array of{name, dispName, type, min, max, defVal}) lists style keys in the Property table of the Style tab (seemxBasic.js).
A handle for the size key of the example:
Graph.handleFactory['myTag'] = function(state)
{
return [Graph.createHandle(state, ['size'], function(bounds)
{
var size = Math.max(0, Math.min(bounds.width, parseFloat(
mxUtils.getValue(this.state.style, 'size', TagShape.prototype.size))));
return new mxPoint(bounds.x + bounds.width - size, bounds.y);
}, function(bounds, pt)
{
this.state.style['size'] = Math.round(Math.max(0,
Math.min(bounds.width, bounds.x + bounds.width - pt.x)));
})];
};A JavaScript shape must be loaded everywhere the diagram is drawn: in the editor (a fork, or a plugin that registers it at load time, as rackF5.js does), in the viewer and lightbox, and in exports. For a fork, add the file to src/main/webapp/shapes/, register the shapes with an mxgraph.<lib>. prefix, add an entry to mxStencilRegistry.libraries (diagramly/Editor.js) for development mode and the viewer.min.js bundle, and rebuild so that the file is part of shapes-14-6-5.min.js. Diagrams opened on app.diagrams.net or viewer.diagrams.net show your shapes as rectangles, so prefer stencils for shapes that travel with the file.
A stencil describes a shape as drawing commands in its own coordinate system (w by h, default 100 by 100), scaled to the cell. The language is documented in the header of mxStencil.js, in the stencil schema and, element by element, on drawio.com.
<shapes name="mxgraph.mylib">
<shape name="Ticket" w="100" h="50" aspect="variable" strokewidth="inherit">
<connections>
<constraint x="0" y="0.5" perimeter="0" name="W"/>
<constraint x="1" y="0.5" perimeter="0" name="E"/>
</connections>
<background>
<path>
<move x="0" y="0"/>
<line x="100" y="0"/>
<line x="100" y="18"/>
<arc rx="7" ry="7" x-axis-rotation="0" large-arc-flag="0" sweep-flag="0" x="100" y="32"/>
<line x="100" y="50"/>
<line x="0" y="50"/>
<line x="0" y="32"/>
<arc rx="7" ry="7" x-axis-rotation="0" large-arc-flag="0" sweep-flag="0" x="0" y="18"/>
<close/>
</path>
</background>
<foreground>
<fillstroke/>
<dashed dashed="1"/>
<path>
<move x="25" y="0"/>
<line x="25" y="50"/>
</path>
<stroke/>
</foreground>
</shape>
</shapes>This set registers mxgraph.mylib.ticket: a ticket with notched sides, a dashed tear-off line and connection points on the left and right.
| Element | Attributes | Notes |
|---|---|---|
shapes |
name |
A stencil set; the name is the prefix of every shape in it |
shape |
name, w, h, aspect (variable or fixed), strokewidth (number or inherit) |
One shape |
connections > constraint
|
x, y (0 to 1), perimeter (0 or 1), name
|
Connection points |
background |
At most one path, rect, roundrect or ellipse, without fill or stroke; used for the shadow and the outline |
|
foreground |
Everything else; if there is a background, the first element must be fill, stroke or fillstroke to paint it |
|
path |
rounded, arcSize
|
Contains move (x, y), line (x, y), quad (x1, y1, x2, y2), curve (x1 to y3), arc (rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y, as in SVG) and close. rounded="1" only rounds paths made of move and line. |
rect, roundrect, ellipse
|
x, y, w, h; arcsize for roundrect
|
Must be followed by fill, stroke or fillstroke
|
fill, stroke, fillstroke
|
Paint the current path | |
fillcolor, strokecolor, fontcolor
|
color, default
|
A colour, or fill, stroke, font for the cell's colours. Any other value is read as a style key, with default as the fallback; such keys appear in the format panel. |
strokewidth |
width, fixed
|
fixed="1" does not scale the width |
dashed, dashpattern
|
dashed; pattern
|
For example pattern="5 3"
|
linecap, linejoin, miterlimit
|
cap (flat, round, square); join; limit
|
|
alpha, fillalpha, strokealpha
|
alpha |
0 to 1. In the current code all three set the overall alpha. |
fontsize, fontstyle, fontfamily
|
size; style (bits: 1 bold, 2 italic, 4 underline); family
|
|
text |
str, x, y, align, valign, localized, vertical, rotation, align-shape, flip-shape
|
Fixed text inside the shape (the cell label is separate) |
image |
src, x, y, w, h, flipH, flipV
|
An image URL or data URI |
include-shape |
name, x, y, w, h
|
Draws another stencil by its registered name |
labelBounds |
if, x, y, w, h
|
draw.io extension (grapheditor/Shapes.js): the label area in stencil coordinates, used while the style key named in if is 1 (for example boundedLbl); the key appears as a checkbox in the Property table |
save, restore
|
Push and pop the drawing state |
| Situation | How |
|---|---|
| Built-in libraries | Bundled in js/stencils.min.js for the editor; the committed viewer bundles fetch them from STENCIL_PATH (see Embedding) |
| Your own file in a deployment or fork | Put <shapes name="mxgraph.mylib"> in src/main/webapp/stencils/mylib.xml and use shape=mxgraph.mylib.<name>; it is loaded from STENCIL_PATH the first time it is needed |
| A library with stencils and JavaScript | mxStencilRegistry.libraries['mylib'] = [STENCIL_PATH + '/mylib.xml', SHAPES_PATH + '/mxMyLib.js']; |
| From code (for example a plugin) |
mxStencilRegistry.parseStencilSet(xmlDoc.documentElement) for a parsed set, mxStencilRegistry.loadStencilSet(url) for a URL, or mxStencilRegistry.addStencil(name, new mxStencil(shapeNode)) for one shape |
| A sidebar palette of all shapes in a set | Sidebar.prototype.addStencilPalette(id, title, stencilFile, style, ...) |
The built-in stencil XML is not part of this repository. The library sources are built outside it into js/stencils.min.js, which holds every stencil set in a compact encoding, keyed by its path below STENCIL_PATH, and replaces mxStencilRegistry.loadStencil so that those paths are decoded from the bundle while every other path is still fetched from the server. The public build.xml therefore keeps the shipped stencils.min.js (it only runs the converter when stencil XML is present). You cannot rebuild the built-in sets, but you can add sets of your own as above; the dynamic loading works in production and development mode. To see the XML of a built-in shape, select it and open Edit Shape.
A style can carry the stencil itself: shape=stencil(<data>), where <data> is the XML compressed with Graph.compress (URI-encoded, deflated without a header, then Base64, the same encoding as compressed diagrams). The Edit Shape dialog (EditShapeDialog in diagramly/Dialogs.js) writes this: Arrange > Insert > Shape creates a new shape, and Edit Shape (in the Edit menu of the format panel's Style tab, and under Edit in the context menu) opens the XML of a selected stencil-based shape, including the built-in ones. Preview draws the XML inside the dialog; applying it writes the shape=stencil(...) style to the cell.
var xml = '<shape name="Badge" w="40" h="40"><background>' +
'<ellipse x="0" y="0" w="40" h="40"/></background><foreground>' +
'<fillstroke/><rect x="10" y="10" w="20" h="20"/><stroke/>' +
'</foreground></shape>';
graph.insertVertex(graph.getDefaultParent(), null, '', 20, 20, 40, 40,
'shape=stencil(' + Graph.compress(xml) + ');html=1;');Inline stencils need no files and render wherever the diagram is opened, including app.diagrams.net, the viewer and exports, which makes them the best choice for shapes in custom libraries and templates.
The image named style in styles/default.xml sets shape=image with the label below the image, so image;image=https://example.com/logo.svg; is a complete style. Useful keys are imageAspect (0 to stretch), aspect=fixed (keep the ratio when resizing), imageBackground, imageBorder and clipPath; see the style reference.
Because ; separates style entries, data URIs are written without ;base64: image=data:image/png,iVBORw0.... mxGraph.prototype.postProcessCellStyle restores the full form before rendering. For SVG images stored as data URIs (data:image/svg+xml,...), the Style tab of the format panel offers colour editing. Images referenced by URL are loaded by the browser of whoever views the diagram, so they must be reachable from there; use data URIs for diagrams that must be self-contained.
A custom library is a file of reusable entries (shapes, groups, images or whole diagrams) that appears in the sidebar. It is the way to share shapes without code: an <mxlibrary> element with a JSON array of entries, each with w, h, an optional title and either xml (a compressed or escaped mxGraphModel) or data (an image data URI).
- Create one with File > New Library, drag cells or image files into it and export it. Format details: custom shape library format; user guide: custom shape libraries.
- Load libraries by URL with
clibs=U<url-encoded URL>(several separated by;), or for a deployment with thelibrariesanddefaultCustomLibrariesconfiguration keys (see Configuration). -
jgraph/drawio-libs collects libraries in this format, with ready-made
clibslinks. - Entries that use stencils should use inline stencils, and entries must not depend on JavaScript shapes, so that they work on any draw.io installation.
To turn a set of SVG files into a stencil set, jgraph/svg2xml is a Java tool that converts SVG files or folders into stencil XML (one library per folder). It was written for internal use and its README lists the options that are not implemented. Check the result in the Edit Shape dialog.
Palettes refer to shapes only by style: createVertexTemplateEntry('shape=mxgraph.mylib.ticket;whiteSpace=wrap;html=1;', 100, 50, '', 'Ticket'). The palette files in js/diagramly/sidebar show the conventions (a common style prefix per library, getTagsForStencil for search tags, setCurrentSearchEntryLibrary to group the search results). How to register a palette is described in Extending the editor and, for plugins, in Plugins.
Stencils and shapes are rendered for every viewer of a diagram, so treat them like diagram content. Inline stencils and stencil files can come from untrusted diagrams and libraries. mxStencil.allowEval (default false) controls whether the text content of text and image elements is evaluated as a function; keep it off. mxStencilRegistry.allowEval controls whether .js files listed in mxStencilRegistry.libraries are fetched and evaluated on demand; production mode and the desktop app turn it off and load the shape bundle instead. JavaScript shapes that draw text must not pass diagram text to c.text(..., format='html', ...) without Graph.sanitizeHtml. See Security.
Describes the dev branch of jgraph/drawio as of release 32.3.0 (October 2026). Internal JavaScript APIs change between releases; check the code of the version you use. · Questions: Discussions · Bugs: Issues · Vulnerabilities: report privately · User docs: drawio.com/docs
Get started
Concepts
Guides
- Self-hosting
- Configuration
- Storage backends
- OneDrive app registration
- Embedding
- Diagrams in GitHub
- Plugins
- Extending the editor
- Shapes and stencils
- Import and export
- Desktop app
- Internationalization
Reference
Project
Elsewhere