Skip to content
David Benson edited this page Oct 7, 2026 · 1 revision

This page explains how cell styles work in the draw.io code: the style string syntax, the stylesheet, how a cell's style is resolved, how to read and change styles in code, colours and dark mode, and the defaults for new cells. It is for plugin authors, forkers and anyone generating diagrams. The full list of style keys and values is maintained in the style reference; this page groups the important ones and points to the code that reads them.

To see the style of any shape, select it and use Edit > Edit Style (Ctrl+E).

Syntax

A cell style is a string stored in the cell's style attribute:

rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;
ellipse;whiteSpace=wrap;html=1;aspect=fixed;
text;html=1;align=center;verticalAlign=middle;

mxStylesheet.prototype.getCellStyle (mxStylesheet.js) parses it:

  • Entries are separated by ;. An entry with = is a key=value pair; an entry without = is the name of a style in the stylesheet (ellipse, text, swimlane, ...), whose keys are merged in. Unknown names are ignored.
  • Entries apply from left to right, starting from the default vertex or edge style. By convention named styles come first.
  • A value of none removes the key from the resolved style, for example to remove a key that a named style or the defaults set. Colour keys treat a missing value as no colour, so fillColor=none means no fill.
  • A style that starts with ; does not start from the default vertex or edge style.
  • Numeric values are converted to numbers; everything else stays a string. Booleans are 0 and 1.
  • The format has no escaping, so values cannot contain ; or =. Values that need them are URI-encoded (fontSource, the JSON form of childLayout), and images are stored as data URIs without ;base64: image=data:image/png,iVBORw0.... EditorUi.prototype.convertDataUri removes ;base64 when an image is added, and mxGraph.prototype.postProcessCellStyle puts it back when the style is resolved.

The key names are the values of the STYLE_* constants in mxConstants.js (mxConstants.STYLE_FILLCOLOR is 'fillColor'), plus many keys that draw.io adds and reads by name in Graph.js, Shapes.js and diagramly/Editor.js. Graph.cellStyles, Graph.textStyles and Graph.edgeStyles list the keys that the default style for new cells carries over (see Defaults for new cells). Editor.commonVertexProperties, Editor.commonEdgeProperties and the customProperties arrays of the shape classes (in diagramly/Editor.js) list further keys with their types and defaults; they drive the Property table in the Format panel.

The stylesheet

The stylesheet maps style names to sets of keys. draw.io's is styles/default.xml:

<mxStylesheet>
	<add as="defaultVertex">
		<add as="shape" value="label"/>
		<add as="fillColor" value="default"/>
		...
	</add>
	<add as="defaultEdge"> ... </add>
	<add as="text"> ... </add>
	<add as="edgeLabel" extend="text"> ... </add>
	...
</mxStylesheet>

defaultVertex and defaultEdge are the starting point for every vertex and edge. The other named styles are text, edgeLabel, label, icon, swimlane, group, ellipse, rhombus, triangle, line, image, roundImage, rhombusImage, arrow, plus the colour presets fancy, gray, blue, ... and plain-gray, plain-blue, ....

How it is loaded:

  • Graph.prototype.loadStylesheet decodes this.themes[this.defaultThemeName] into the graph's mxStylesheet with mxCodec (mxStylesheetCodec). In draw.io, defaultThemeName is default-style2.
  • The build turns default.xml into Graph-Stylesheet.js, which sets Graph.prototype.defaultThemes['default-style2'] (and 'darkTheme' to the same document) and is compiled into app.min.js. In development mode (?dev=1) App.main loads styles/default.xml instead. Edits to default.xml therefore need a rebuild (see Building).
  • There is no separate dark stylesheet: dark mode uses the same styles and resolves default colours at render time (see Colours and dark mode). styles/dark-default.xml and styles/default-old.xml are in the repository but the current code does not load them.

The stylesheet is not saved in diagram files. A plugin can add named styles at runtime:

var style = {};
style[mxConstants.STYLE_FILLCOLOR] = '#fff2cc';
style[mxConstants.STYLE_STROKECOLOR] = '#d6b656';
graph.getStylesheet().putCellStyle('myHighlight', style);   // style="myHighlight;html=1;"

but the names only resolve where the plugin is loaded; in other draw.io installations, the viewer or exports made elsewhere those cells lose the keys. Write the keys into the cell style instead when the file leaves your environment.

How a cell's style is resolved

graph.getCellStyle(cell, resolve) (mxGraph.js) builds a style object:

  1. Start with a copy of defaultEdge for edges, otherwise defaultVertex.
  2. Apply the cell's style string with mxStylesheet.getCellStyle (named styles, key=value, none).
  3. If resolve is not false, run graph.postProcessCellStyle(cell, style):
    • mxGraph: resolves image bundle keys and turns short data URIs back into valid ones.
    • Graph.replaceDefaultColors: replaces the colour value default with the theme colours (see below).
    • Graph.updateHorizontalStyle: sets horizontal for collapsed cells in a stack layout.
    • Graph.processFontStyle (diagramly): loads a web font when fontSource is set.

graph.getCurrentCellStyle(cell) returns the style cached in the cell state (graph.view.getState(cell).style) when the cell is rendered, otherwise getCellStyle(cell). Use it for reading effective values; it is what the editor uses. Use graph.getCellStyle(cell, false) for the unresolved values (default and none kept as written). Values are numbers or strings, so read them with mxUtils.getValue(style, key, defaultValue) and compare booleans with == '1' or == 1 as the code does.

The view uses the resolved style to pick the shape (shape), the perimeter (perimeter, by name through mxStyleRegistry), the edge router (edgeStyle) and everything the shape paints.

Reading and changing styles in code

Call Purpose
model.getStyle(cell) The raw style string.
graph.getCurrentCellStyle(cell), graph.getCellStyle(cell, resolve) Resolved style object (see above).
graph.setCellStyles(key, value, cells) Set one key on several cells; null removes it. Undoable. Defaults to the selection.
graph.toggleCellStyles(key, defaultValue, cells) Toggle a 0/1 key.
graph.setCellStyleFlags(key, flag, value, cells) Set or clear a bit, for example in fontStyle.
graph.setCellStyle(style, cells) Replace the whole style string.
graph.updateCellStyles(styleObject, cells) Set every key of an object on the cells.
model.setStyle(cell, style) Replace one cell's style string (undoable).
mxUtils.setStyle(style, key, value) String helper: returns the style with the key set or removed.
mxUtils.getStylenames(style), mxUtils.addStylename(style, name), mxUtils.removeStylename(style, name) Named-style helpers.

Graph.prototype.setCellStyles adds special handling for some keys (curved also sets curveGeometry on edges; tableRender converts table borders), so prefer it to editing strings yourself.

Draw.loadPlugin(function(ui)
{
	var graph = ui.editor.graph;
	var cells = graph.getSelectionCells();

	graph.getModel().beginUpdate();
	try
	{
		graph.setCellStyles(mxConstants.STYLE_FILLCOLOR, '#f8cecc', cells);
		graph.setCellStyles('rounded', '1', cells);
		graph.setCellStyleFlags(mxConstants.STYLE_FONTSTYLE, mxConstants.FONT_BOLD, true, cells);
	}
	finally
	{
		graph.getModel().endUpdate();
	}

	var style = graph.getCurrentCellStyle(cells[0]);
	console.log(mxUtils.getValue(style, 'fillColor', 'none'));
});

Important keys

Grouped by purpose; see the style reference for values and the complete list.

Group Keys
Shape shape, perimeter, direction (north, south, east, west), rotation, flipH, flipV, aspect=fixed, rounded, arcSize, absoluteArcSize
Fill and line fillColor, gradientColor, gradientDirection, strokeColor, strokeWidth, dashed, dashPattern, opacity, fillOpacity, strokeOpacity, shadow, glass, sketch (hand-drawn, with fillStyle, hachureGap, ...)
Text html=1 (HTML label), whiteSpace=wrap, fontFamily, fontSize, fontColor, fontStyle (bit flags: 1 bold, 2 italic, 4 underline, 8 strikethrough), fontSource, align, verticalAlign, labelPosition, verticalLabelPosition, spacing, spacingTop / Left / Bottom / Right, labelBackgroundColor, labelBorderColor, textOpacity, horizontal=0 (vertical text), overflow, noLabel, autosize
Image shape=image, image, imageAspect, imageWidth, imageHeight, imageAlign, imageBackground, imageBorder, clipPath
Edge routing edgeStyle, elbow, curved, rounded, orthogonalLoop, jettySize, jumpStyle, jumpSize
Edge ends startArrow, endArrow, startFill, endFill, startSize, endSize, exitX, exitY, exitDx, exitDy, exitPerimeter, entryX, entryY, entryDx, entryDy, entryPerimeter, sourcePerimeterSpacing, targetPerimeterSpacing
Containers container, collapsible, swimlane shape with startSize, childLayout, expand, recursiveResize, dropTarget, part
Behaviour locked, movable, resizable, rotatable, deletable, editable, cloneable, bendable, connectable, pointerEvents, points

Some of these need explanation:

  • html: with html=1 the label is HTML, sanitised when rendered; without it, the label is plain text. graph.isHtmlLabel(cell) also returns true for whiteSpace=wrap, but the text is then escaped and only wrapped.
  • edgeStyle: the name of a registered router, for example orthogonalEdgeStyle, elbowEdgeStyle (with elbow=vertical or horizontal), entityRelationEdgeStyle, segmentEdgeStyle, isometricEdgeStyle, or none for straight segments through the waypoints. curved=1 draws curves through the route; rounded=1 rounds the corners. The routers are in mxEdgeStyle.js and Shapes.js and are registered by name with mxStyleRegistry.putValue (mxStyleRegistry.js).
  • exitX / exitY / entryX / entryY: fixed connection points as fractions (0..1) of the terminal's bounds, with exitDx / exitDy pixel offsets; exitPerimeter=0 keeps the point where it is instead of projecting it onto the perimeter. Without them the edge floats.
  • points: the connection points offered on a vertex, as a JSON array of [x, y] or [x, y, perimeter, dx, dy], for example points=[[0,0],[0.5,0],[1,0,0,-10,10]] (Graph.prototype.getAllConnectionConstraints). Without it the shape's or stencil's own constraints are used.
  • container / collapsible: a vertex accepts children when dropped onto it if container=1; swimlanes are containers unless container=0. collapsible=0 hides the fold icon of a container; collapsible=1 enables folding for a non-container.
  • childLayout: runs a layout on the children whenever they change (Graph.prototype.initLayoutManager): stackLayout (with horizontalStack, resizeParent, resizeLast, stackSpacing, marginTop, ...), treeLayout, flowLayout, circleLayout, organicLayout, tableLayout, rack, or a URI-encoded JSON layout array (see the JSON layout specification).
  • locked: locked=1 locks the cell and its descendants (Graph.isCellLocked checks ancestors). Edit > Lock/Unlock (Ctrl+L) sets locked, movable, resizable, rotatable, deletable, editable and connectable together.
  • part=1: the cell is part of its parent vertex; dragging it moves the parent (Graph.prototype.getCompositeParent).

Links, tooltips, tags and placeholders are not style keys; they are attributes of the cell's user object (see Graph model).

Shapes and stencils

shape names either a registered shape class or a stencil:

  • Registered shapes are JavaScript classes registered with mxCellRenderer.registerShape(name, constructor). The core shapes (rectangle, ellipse, rhombus, cylinder, connector, actor, triangle, hexagon, cloud, line, arrow, arrowConnector, doubleEllipse, swimlane, image, label) are registered in mxCellRenderer.js; draw.io adds about a hundred more in Shapes.js (cylinder3, note, process, umlLifeline, table, ...).
  • Stencils are shapes defined in XML and looked up with mxStencilRegistry.getStencil(name) when no class is registered under the name. The shape libraries use names such as shape=mxgraph.aws4.resourceIcon, loaded on demand by prefix.
  • Inline stencils: shape=stencil(...) holds a compressed stencil XML in the style itself (Edit Shape in the editor writes these).

A stencil can define connection points; points= in the style overrides them. Writing and registering shapes is covered in Shapes and stencils.

Colours and dark mode

A colour value is one of:

Value Meaning
#rrggbb, rgb(...), a CSS colour name Fixed colour.
none No colour.
default Theme colour, resolved by Graph.prototype.replaceDefaultColors: the foreground Graph.prototype.shapeForegroundColor (light-dark(#000000, #ffffff)) for strokeColor, fontColor, gradientColor, labelBorderColor, imageBorder, and the background Graph.prototype.shapeBackgroundColor (white in light mode, Editor.darkColor #121212 in dark mode) for fillColor, labelBackgroundColor, swimlaneFillColor, imageBackground.
light-dark(<light>, <dark>) Separate colours for light and dark mode, as written by the colour dialog when a dark colour is set.

The defaults in default.xml use default, which is why a shape without colours adapts to dark mode. For a key set to default, the style key default<Key> changes the result: invert swaps the light and dark values, and page uses the page background colour (for example labelBackgroundColor=default;defaultLabelBackgroundColor=page), see Graph.prototype.getDefaultColor.

The adaptiveColors attribute of each page (the Adaptive Colors submenu in the editor; Graph.prototype.getAdaptiveColors) decides what happens to fixed colours when the diagram is shown in dark mode:

Value Effect
auto (default) Every fixed colour gets a computed dark variant (mxUtils.getInverseColor, through mxUtils.parseLightDarkColor).
simple Fixed colours stay as they are; only default and light-dark() colours change.
none The diagram is always drawn in light mode.

The default for pages without the attribute is Graph.defaultAdaptiveColors, set with the defaultAdaptiveColors configuration option.

Defaults for new cells

There are three layers of defaults:

  1. The stylesheet (defaultVertex, defaultEdge) applies to every cell, also in files, the viewer and exports.
  2. Graph.prototype.defaultVertexStyle and Graph.prototype.defaultEdgeStyle are the editor's defaults for new cells. defaultVertexStyle is empty and defaultEdgeStyle is {edgeStyle: 'orthogonalEdgeStyle', rounded: '0', jettySize: 'auto', orthogonalLoop: '1'}. Set them with the defaultVertexStyle and defaultEdgeStyle configuration options.
  3. graph.currentVertexStyle and graph.currentEdgeStyle start as copies of those and change when the user runs Set as Default Style, picks a theme in the Format panel or a connector style in the toolbar with no edge selected, or, with the updateDefaultStyle configuration option, on every style change. Graph.prototype.pasteCellStyles applies them to cells inserted from the sidebar and by paste (the cellsInserted event), skipping keys the cell's own style already sets. New edges drawn in the editor get graph.createCurrentEdgeStyle().

Cells you add with graph.insertVertex or graph.insertEdge in code get none of the editor defaults in (2) and (3), only the stylesheet; pass a complete style string, for example whiteSpace=wrap;html=1; for vertices and edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1; for edges, or call graph.pasteCellStyles(cells) to apply the current defaults.

Themes and colour schemes

  • Themes in the Format panel (nothing selected, the Style tab) come from Editor.styles: an array of {commonStyle, vertexStyle, edgeStyle, graph: {background, gridColor}} objects. Picking one sets the current styles, restyles all cells on the page and sets the page background and grid colour. Replace the list with the styles configuration option.
  • Colour schemes (the quick colour buttons for selected shapes) come from StyleFormatPanel.prototype.defaultColorSchemes, replaced or extended with the defaultColorSchemes and customColorSchemes options.

The options are described in Configure the diagram editor and Configuration.

Style compression

With the compressStyles configuration option, a data-URI image or inline stencil that several cells use is stored once in the file and referenced as image=def(n) / shape=def(n). This is a file-level feature of mxModelCodec; the resolved styles are the same. See File format and Style compression.

See also

Clone this wiki locally