Skip to content

Architecture

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

This page is a bird's-eye view of how the draw.io code is put together: the three layers and the rules between them, the objects that exist at runtime, what happens from index.html to an open diagram, and how edits, saving and rendering flow through the code. Read it before you change the editor, write a non-trivial plugin or try to locate a bug. For "which file does X", see the Code map; for the vocabulary, the Glossary.

Bird's-eye view

draw.io is a diagram editor that runs entirely in the browser. A diagram is a tree of cells (vertices and edges) with geometry, a style string and a label. The editor turns that tree into SVG, lets the user change it with the mouse and keyboard, and reads and writes it as XML (an <mxfile> with one <diagram> per page). Everything else is built around that loop: storage backends that load and save the XML, importers and exporters that convert other formats to and from it, and viewers that only render it.

Some properties shape the whole code base:

  • Plain browser JavaScript. No ES modules, no npm, no bundler. Every file attaches classes and functions to the global scope, and inheritance uses mxUtils.extend. Load order is the dependency mechanism.
  • Sources run as they are. index.html?dev=1 loads the individual source files. Production loads a few large bundles that Ant concatenates and minifies with the Closure Compiler (etc/build/build.xml). The bundles are committed to the repository. See Building.
  • The server is optional. src/main/webapp can be served by any static web server. The Java servlets in src/main/server/java are helpers (proxy, OAuth callbacks, export proxy); the editor works without them. See Self-hosting.
  • One code base, several products. The same files make the web app, the desktop app (Electron), the embeddable editor (embed mode) and the read-only viewer (GraphViewer).
  • Diagrams are untrusted input. Labels, styles, links and imported files come from users and are rendered for other users. See Security.

The three layers

flowchart TB
    subgraph diagramly["js/diagramly: the draw.io application"]
        A["App, DrawioFile and the storage clients, Pages, GraphViewer,<br/>Extensions (importers), Minimal / Simple themes, ElectronApp"]
    end
    subgraph grapheditor["js/grapheditor: generic diagram editor"]
        B["EditorUi, Editor, Graph, Sidebar, Format, Toolbar,<br/>Menus, Actions, Shapes, Dialogs, InlineToolbar"]
    end
    subgraph mxgraph["mxgraph: graph library"]
        C["mxGraph, mxGraphModel, mxGraphView, mxCellRenderer,<br/>shapes, handlers, layouts, mxCodec, mxUtils"]
    end
    diagramly --> grapheditor --> mxgraph
    V["Vendored bundles in js/: DOMPurify, pako, rough.js, perfect-freehand,<br/>JSZip, elk, mermaid, plantuml, libavoid-js, vsd, gliffy"]
    diagramly -.-> V
Loading
Layer Directory Responsibility Main types
Graph library mxgraph/src Cell model and transactions, view and cell states, SVG rendering, interaction handlers, layouts, XML codecs, utilities mxGraph, mxGraphModel, mxCell, mxGeometry, mxGraphView, mxCellState, mxCellRenderer, mxShape, mxSvgCanvas2D, mxCodec, mxUndoManager, mxEvent, mxUtils
Generic editor js/grapheditor A complete editor UI on top of mxGraph: the Graph subclass with draw.io's styles and behaviour, menus, toolbar, shape sidebar, format panel, dialogs, custom shapes EditorUi, Editor, Graph, Actions, Menus, Toolbar, Sidebar, Format, HoverIcons, Dialog
Application js/diagramly Files and storage backends, pages, realtime collaboration, import and export, themes, embed mode, the viewer, the desktop hooks, the shape libraries App, DrawioFile and subclasses, DrawioClient and subclasses, DiagramPage, DrawioFileSync, GraphViewer, mxSettings

Invariants between the layers

  • mxgraph never references the layers above it. It knows nothing about Graph, Editor, EditorUi, App or urlParams. Behaviour that draw.io needs is added by overriding mxGraph prototypes in Graph.js, Shapes.js or diagramly/Editor.js, never by editing mxGraph to call up.
  • grapheditor is meant to know nothing about the application. It is no longer built or shipped without diagramly, and a few references leak upward: App.ERROR_TIMEOUT (in EditorUi.js), mxSettings (in EditorUi.js and Actions.js), and EditorUi.prototype.isOffline, EditorUi.debug and EditorUi.isElectronApp, which are defined in diagramly/EditorUi.js but called from Dialogs.js, Sidebar.js, Format.js and Editor.js. Treat these as debt, not as a pattern. Optional code from the extensions bundle is only reached through feature detection, for example typeof LibavoidRouting !== 'undefined' in Format.js, Menus.js and Graph.js, and typeof ElkLayout in Graph.prototype.createLayouts.
  • diagramly extends; it does not subclass. Apart from App, there is exactly one Editor, one EditorUi, one Graph and one Menus class at runtime. The files in diagramly/ with the same names add statics and replace prototype methods of the classes defined in grapheditor/ (see the example below). Because of this, load order matters: diagramly/Editor.js must load after grapheditor/Editor.js, and so on. The order is fixed in Devel.js (development) and etc/build/build.xml (bundles).
  • Optional engines are global objects, checked before use. ELK (ElkLayout), Mermaid (mxMermaidToDrawio, gated by EditorUi.isMermaidSupported()), PlantUML (mxPlantUmlToDrawio), Gliffy (mxGliffyToDrawio), libavoid (LibavoidRouting, AvoidRouting), the binary Visio converter (DrawioVsd) and MathJax (DrawioMathJax) may be absent, for example in the viewer or before a lazy load has finished. Code that uses them checks first.
  • The vendored engine bundles are built outside this repository. js/elk/, js/mermaid/, js/plantuml/, js/libavoid-js/libavoid.min.js, js/vsd/ and js/gliffy/ are committed build outputs; do not edit them by hand.

Most replacements in diagramly/ keep the original method and call it. Plugins use the same pattern (see Plugins and Extending the editor):

// js/diagramly/EditorUi.js (inside an IIFE)
var editorUiInit = EditorUi.prototype.init;

EditorUi.prototype.init = function()
{
    // ... draw.io-specific setup ...
    editorUiInit.apply(this, arguments);
    // ...
};

Objects at runtime

flowchart LR
    ui["ui: App (EditorUi)"] -->|editor| editor["Editor"]
    editor -->|graph| graph["Graph (extends mxGraph)"]
    editor -->|undoManager| undo["mxUndoManager"]
    graph -->|model| model["mxGraphModel"]
    graph -->|view| view["mxGraphView"]
    graph -->|cellRenderer| renderer["mxCellRenderer"]
    graph -->|stylesheet| ss["mxStylesheet"]
    graph -->|"getSelectionModel()"| sel["mxGraphSelectionModel"]
    ui -->|"getCurrentFile()"| file["DrawioFile subclass"]
    file -->|sync| sync["DrawioFileSync"]
    ui -->|"pages, currentPage"| page["DiagramPage"]
Loading
From To Expression
Plugin the UI the ui argument of Draw.loadPlugin(function(ui) { ... })
UI editor, graph ui.editor, ui.editor.graph
Graph model, view graph.model (or graph.getModel()), graph.view (or graph.getView())
Graph interaction handlers graph.graphHandler, graph.connectionHandler, graph.panningHandler, graph.popupMenuHandler, graph.tooltipHandler, graph.selectionCellsHandler, graph.cellEditor
Graph rubberband graph.getRubberband()
UI chrome ui.actions, ui.menus, ui.toolbar, ui.sidebar, ui.format, ui.keyHandler, ui.hoverIcons
UI current file ui.getCurrentFile() (a LocalFile, DriveFile, GitHubFile, ...)
UI pages ui.pages, ui.currentPage, ui.selectPage(page)
Cell state graph state.view.graph

Classes and their parents:

  • App extends EditorUi (mxUtils.extend(App, EditorUi) in App.js).
  • EditorUi, Editor, DrawioFile, DrawioClient, DrawioFileSync, GraphViewer and HoverIcons extend mxEventSource, so they all have addListener and fireEvent.
  • Graph extends mxGraph.
  • Every storage backend has a *File class that extends DrawioFile, a *Library class for shape libraries and, for cloud backends, a *Client class that extends DrawioClient. GitLabFile and GitLabClient extend their GitHub counterparts. See Storage backends.

Startup sequence

1. index.html and bootstrap.js

index.html loads styles/grapheditor.css and js/bootstrap.js, shows a splash logo in #geInfo and runs js/main.js at the end of the body.

bootstrap.js runs before any editor code. It:

  1. Parses the query string into the global urlParams. A hash that starts with #P carries more parameters as JSON.
  2. Sets mxIsElectron from the user agent.
  3. Defines the script loaders mxscript(src, onLoad, ...) and mxinclude(src), used everywhere later to load code on demand.
  4. Loads the code:
    • Development (?dev=1): js/PreConfig.js, js/diagramly/Init.js, js/grapheditor/Init.js, mxgraph/src/mxClient.js (which includes every mxGraph source file), js/diagramly/Devel.js (every grapheditor and diagramly source file, the shape libraries, the vendored libraries and engines) and js/PostConfig.js. main.js then calls App.main() directly.
    • Production: js/app.min.js. On hosts outside the *.draw.io and *.diagrams.net domains (that is, on your own server) and in Electron, js/PreConfig.js is loaded before it and js/PostConfig.js after it. When both the bundle and the window have loaded, checkAllLoaded() calls App.main(). In Electron it also loads DesktopLibrary.js, ElectronApp.js, extensions.min.js, the stencils, the shapes and the PlantUML bundle.

The two Init.js files set the defaults for the global configuration variables (EXPORT_URL, PROXY_URL, DRAWIO_BASE_URL, RESOURCES_PATH, ALLOW_CUSTOM_PLUGINS, DOM_PURIFY_CONFIG, ...), the UI language mxLanguage and the UI theme uiTheme. PreConfig.js runs before them, so a self-hosted deployment overrides the defaults there. See Configuration.

Note that in the public repository, dev mode does not load js/diagramly/Simple.js (Devel.js skips it when DRAWIO_PUBLIC_BUILD is set, which PreConfig.js does). The simple, sketch and atlas themes are therefore only available in the built app.min.js.

2. App.main

App.main(callback, createUi) prepares everything that must exist before the UI is created, in this order:

  1. Guards against a second call, installs a global window.onerror and adds the geEmbed CSS class in embed mode.
  2. Service worker. If Editor.enableServiceWorker is true, registers or updates service-worker.js. With offline=0, and in dev mode unless offline=1 or enableSW=1 is set, it removes it instead (App.clearServiceWorker). Editor.enableServiceWorker is only true on *.diagrams.net and *.draw.io hosts or with the offline=1 or enableSW=1 URL parameters, and pwa=0 turns it off.
  3. Realtime scripts for collaboration, when the conditions in App.main allow it.
  4. Plugins. App.initPluginCallback() defines the global Draw.loadPlugin, which queues callbacks until the UI exists. Plugins come from the p URL parameter (only the short ids in App.pluginRegistry, see App.loadPlugins) and from the plugin list saved in mxSettings. Saved plugins must be on the same domain, and unless ALLOW_CUSTOM_PLUGINS is set they must also be built-in (App.isBuiltInPlugin). plugins=0 and offline=1 load none. Each plugin is loaded with mxscript.
  5. The Google API script, if the Drive client is part of the bundle and not disabled.
  6. Math. Editor.initMath() installs the MathJax hooks unless math=0. MathJax itself is loaded later, on the first typeset.
  7. Configuration. With configure=1, the app posts {event: 'configure'} to its parent window and waits for an {action: 'configure', config: ...} message. Otherwise it applies window.DRAWIO_CONFIG and then the JSON stored under Editor.configurationKey in localStorage (not in embed mode). Each is passed to Editor.configure(config), followed by mxSettings.load(). See Configuration and configure-diagram-editor.
  8. doMain() applies the settings overrides (autosaveDelay, defaultEdgeLength), prefetches custom default fonts and calls doLoad() with the language file for mxLanguage.
  9. doLoad() fetches resources/dia.txt or resources/dia_<lang>.txt and passes it to mxResources.parse. It handles a #_CONFIG_ configuration link, then in production loads js/shapes-14-6-5.min.js, js/stencils.min.js and js/extensions.min.js with App.loadScripts before it calls realMain(). In dev mode and Electron those are already loaded.
  10. realMain() picks the theme (Editor.currentTheme, falling back to kennedy if the theme is not in Editor.themes) and creates the UI with new App(new Editor(urlParams['chrome'] == '0' || uiTheme == 'min', null, null, null, urlParams['chrome'] != '0')). It then registers the PWA launchQueue consumer for files opened from the operating system, loads the Dropbox, OneDrive and Trello SDKs if those backends are enabled, and calls callback(ui).

3. Building the UI

  • Editor (grapheditor/Editor.js): initStencilRegistry(), then createGraph() (a new Graph(...)), then createUndoManager(). A model CHANGE listener calls setModified(true) for every edit that is not flagged ignoreEdit.
  • EditorUi (grapheditor/EditorUi.js): creates ui.actions = new Actions(this) and ui.menus = this.createMenus(), then createDivs() (menubar, toolbar, sidebar, format, diagram and page tab containers), createUi() (menubar, Sidebar, Format, Toolbar, split handle), refresh(), graph.init(this.diagramContainer), the hover icons, the inline toolbar, the popup menu factory, this.keyHandler = this.createKeyHandler(editor) and editor.resetGraph(). Finally it calls this.init() and this.open(). The diagramly override of init adds pages, the clipboard handlers, extra keyboard shortcuts, live orthogonal routing and much more. The min, simple, sketch and atlas themes replace parts of this UI (Minimal.js, Simple.js).
  • App constructor: installs the drop handler for files, runs every queued plugin callback with the new ui, replaces Draw.loadPlugin so that plugins loaded later run immediately, and calls this.load().

4. Opening the first diagram

In embed mode (embed=1), App.prototype.load only restores the libraries. App.prototype.initializeEmbedMode, called once all plugins have loaded, waits for the diagram from the host page through the embed protocol (see Embed mode). Otherwise load calls App.prototype.start, after loading the Google API if the Drive client is enabled.

App.prototype.start restores the libraries, installs the final error handler and a hashchange listener, and then opens the first diagram:

  • desc URL parameter: a CSV import descriptor (loadDescriptor).
  • url URL parameter: loadFile('U' + url).
  • client=1: waits for XML from the opener window.
  • create parameter or #create= hash: the create dialog or executeCreateObject.
  • A hash with a file id: App.prototype.loadFile(id). The first character selects the source: L browser storage, U a URL, R raw (optionally compressed) data, E embedded file, and G, D, W, M, H, A, T map to the cloud backends through App.prototype.getModeForChar and the client's getFile. The prefixes are documented in supported-location-hash-properties.
  • No id: drafts (checkDrafts), the splash screen (loadFile() without an id ends in fileLoaded(null), which shows it) or a new blank file.

Every path ends in EditorUi.prototype.fileLoaded(file). It closes the previous file, clears the model and the undo history, makes file the current file and calls file.open(). DrawioFile.prototype.open passes the file's XML to ui.setFileData, installs the change listeners and starts the realtime sync if the backend supports it.

Data flow

Open: XML to cells

  1. EditorUi.prototype.setFileData(data) parses the XML. For an <mxfile> it wraps every <diagram> node in a DiagramPage, stores the pages in ui.pages, keeps the <mxfile> element in ui.fileNode and picks the current page (from the hash, the page-id or page URL parameter, or the first page).
  2. It calls editor.setGraphXml() with the current page's <diagram> node. The diagramly override extracts the <mxGraphModel> (Editor.extractGraphModel, which uses Editor.parseDiagramNode and decompresses the page with Graph.decompress if it is not plain XML) and reads draw.io's graph attributes such as math and adaptiveColors.
  3. The grapheditor Editor.prototype.setGraphXml reads the remaining graph state (grid, page format, background, ...) and decodes the cells with mxCodec inside a model.beginUpdate()/endUpdate() pair.
  4. The model fires CHANGE, mxGraph.graphModelChanged invalidates the view, and the view validates and renders (see Rendering).

The File format page describes the XML.

Edit: transactions, undo, modified flag, autosave

var graph = ui.editor.graph;
graph.model.beginUpdate();
try
{
    var v = graph.insertVertex(graph.getDefaultParent(), null, 'Hello', 20, 20, 120, 60);
    graph.setCellStyles('fillColor', '#dae8fc', [v]);
}
finally
{
    graph.model.endUpdate();
}
  • Every change to the model (mxChildChange, mxGeometryChange, mxStyleChange, mxValueChange, mxTerminalChange, ...) is executed through mxGraphModel.prototype.execute and collected in model.currentEdit.
  • When the outermost endUpdate() runs, the model fires mxEvent.CHANGE with the list of changes and then mxEvent.UNDO with the mxUndoableEdit. Nested beginUpdate() calls are merged into one undoable edit.
  • Editor.prototype.createUndoManager listens to UNDO and adds the edit to editor.undoManager (an mxUndoManager). Undo and redo restore the selection from the changes.
  • Editor's change listener sets editor.modified. In the app, DrawioFile.prototype.installListeners listens to the same CHANGE event and to UI events (grid, page format, background, math, ...) and calls DrawioFile.prototype.fileChanged, which marks the file as modified, schedules autosave() if isAutosave() is true, and passes the edit to the realtime sync (file.sync.localFileChanged) if one is running.
  • An edit with ignoreEdit = true changes neither the modified flag nor triggers autosave; page selection uses this.
  • autosave(delay, maxDelay) saves autosaveDelay ms after the last change (1500 ms in DrawioFile, less for some backends), but at the latest after maxAutosaveDelay of continuous editing.

The Graph model page covers cells, changes and events in detail.

Save: cells to XML to storage

  1. The save action (Ctrl+S) calls App.prototype.saveFile, which asks for a name and a location if the file needs them and then calls App.prototype.save, which calls file.save(...). Autosave calls file.save directly.
  2. DrawioFile.prototype.save waits for the fonts used in the file and calls updateFileData(), which calls createData(), which calls EditorUi.prototype.getFileData(...).
  3. getFileData writes the current page back into its <diagram> node (Editor.prototype.getGraphXml encodes the model with mxCodec) and serialises the whole <mxfile>. Pages are compressed with Graph.compressNode (deflate plus Base64) if the file is compressed: the compressed attribute of <mxfile> decides, and new files are uncompressed by default (Editor.defaultCompressed). For .svg files getFileData embeds the XML in the SVG; for .png files the backends embed it with ui.getEmbeddedPng.
  4. The subclass writes the data: DriveFile.prototype.saveFile calls ui.drive.saveFile, LocalFile downloads it or writes it through the File System Access handle, GitHubFile commits it, and so on.
  5. On success the backend calls fileSaved(). On a conflict (the stored file changed since it was loaded), handleConflictError either synchronises (synchronizeFile, which merges the remote version with mergeFile and the page diffs in DiffSync.js) and saves again, or shows a conflict dialog.

Rendering

The view turns cells into cell states and the renderer turns cell states into SVG:

  1. mxGraph.graphModelChanged(changes) calls processChange for each change, which invalidates the affected cells with view.invalidate(cell, ...), and then view.validate().
  2. mxGraphView.prototype.validate walks the visible cells. validateCell creates or removes an mxCellState per cell; validateCellState resolves the style (graph.getCellStyle: stylesheet defaults, named styles and the cell's key=value pairs), computes the state with updateCellState (vertex bounds from the geometry and the parent's origin; for edges the terminal points, the edge style from mxEdgeStyle and the perimeter points) and calls graph.cellRenderer.redraw(state).
  3. mxCellRenderer.prototype.redraw creates or updates the shape and the label. createShape looks the shape style up in the shapes registered with mxCellRenderer.registerShape (built-ins in mxGraph, draw.io's in Shapes.js and shapes/) or as a stencil in mxStencilRegistry. mxShape.redraw paints through an mxSvgCanvas2D into the SVG drawPane.
  4. Labels are mxText shapes. HTML labels (html=1) are rendered in a foreignObject. Labels are stored raw in the model; the cellRenderer.getLabelValue override in the Graph constructor sanitises HTML labels with Graph.sanitizeHtml each time they are rendered.
  5. The renderer also creates the cell overlays and the folding icons (createCellOverlays, createControl). Selection handles are not rendered here; they belong to the handlers (mxVertexHandler, mxEdgeHandler).

Model coordinates. By default the editor paints cells in model units and applies the zoom and the scroll offset as one transform on the draw pane (mxGraphView.prototype.modelCoordinates, set in EditorUi.prototype.initCanvas from Editor.fastRendering). Zooming and panning then update only the screen fields of the cell states (updateScreenStates, updateDrawPaneTransform) without repainting the cells. Chromeless views (viewer, lightbox) always use this mode. {"fastRendering": false} in the configuration switches the editor back to the classic path, where every scale or translate change repaints all states.

Sketch style. Shapes with sketch=1 are drawn through a rough.js canvas (mxShape.prototype.createHandJiggle, Editor.createRoughCanvas in diagramly/Editor.js).

Pages

A file with several <diagram> elements has several pages, but there is only one Graph and one mxGraphModel. ui.pages holds a DiagramPage per page (in Pages.js); each has the <diagram> node, its root cell once it has been loaded and its view state. EditorUi.prototype.selectPage(page) executes a SelectPage change: it stores the current page's root and view state (graph.getViewState()), swaps the new page's root into the model (model.rootChanged) and restores its view state. The change is added to the undo history with ignoreEdit set, so switching pages does not mark the file as modified. Adding, removing, renaming and moving pages are the undoable changes ChangePage, RenamePage and MovePage. The tab bar at the bottom is built by EditorUi.prototype.updateTabContainer.

Viewers and chromeless mode

  • Chromeless editor. new Editor(chromeless, ...) with chromeless true (chrome=0 or the min theme) creates no menubar, sidebar, format panel or toolbar. With editable false the graph is read-only (graph.isEnabled returns false).
  • Lightbox. lightbox=1 (which diagramly/Init.js turns into chrome=0) creates App in lightbox mode: a chromeless, read-only full-window view with a small toolbar. The viewer opens it in a new window on DRAWIO_LIGHTBOX_URL (EditorUi.lightboxHost).
  • Viewer. GraphViewer.js renders diagrams in other pages without an EditorUi. GraphViewer.processElements() finds every div.mxgraph, reads its data-mxgraph JSON (the XML or a url, plus options such as toolbar, lightbox, auto-fit) and creates a GraphViewer, which creates a plain Graph in the element. Clicking it opens the lightbox, either in-page (showLocalLightbox, a chromeless EditorUi) or in a new window. The viewer bundles viewer.min.js and viewer-static.min.js end by calling Editor.initMath() and then window.onDrawioViewerLoad() if it exists, else GraphViewer.processElements(). See Embedding.

Bundles and lazy loading

Bundle Contents Loaded by
js/app.min.js spin.js, DOMPurify, CryptoJS AES, pako, rough.js, perfect-freehand, mxGraph, grapheditor, the sidebar palettes, the diagramly app with the cloud clients bootstrap.js
js/shapes-14-6-5.min.js the JS shapes in shapes/ App.main before the UI is created
js/stencils.min.js the stencil libraries, binary-encoded App.main before the UI is created
js/extensions.min.js Extensions.js (Lucidchart import), the VSDX importer, EMF, GraphML, Miro, JSZip, the binary Visio converter, ELK with ElkLayout.js, Mermaid, the org chart layout, libavoid with LibavoidRouting.js App.main before the UI is created. Where it has not been loaded (for example in the viewer), EditorUi.loadMermaid, importVisio, importGraphML, exportVisio, convertLucidChart and others load it on demand with mxscript(window.DRAWIO_SERVER_URL + 'js/extensions.min.js', ...)
js/plantuml/drawio-plantuml.min.js PlantUML converter (mxPlantUmlToDrawio) EditorUi.loadPlantUml, on first use; preloaded in Electron
js/gliffy/drawio-gliffy.min.js Gliffy converter (mxGliffyToDrawio) EditorUi.loadGliffy, on first Gliffy import
js/orgchart.min.js org chart layout EditorUi.loadOrgChartLayouts if it is not already loaded
math4/es5/drawio-mathjax.min.js MathJax for SVG output Editor.loadMath, on the first typeset of a diagram with math enabled
js/viewer.min.js, js/viewer-static.min.js the viewer (viewer-static adds the shapes) the host page
Dropbox, OneDrive, Trello and Google SDKs storage backend APIs App.main, only if that backend is enabled
plugins/*.js plugins App.main via mxscript

The exact composition of each bundle is in etc/build/build.xml; see Building.

Cross-cutting concerns

Internationalisation

UI strings are looked up with mxResources.get('key') (mxgraph/src/util/mxResources.js). resources/dia.txt holds the English strings and resources/dia_<lang>.txt the translations; App.main loads one file for mxLanguage (from the lang URL parameter or the saved setting). The supported languages are listed in mxLanguageMap in diagramly/Init.js. The viewer bundles compile the English strings in. See Internationalization.

Security and sanitisation

All HTML from a diagram goes through Graph.sanitizeHtml (DOMPurify, configured by DOM_PURIFY_CONFIG in grapheditor/Init.js). Labels are sanitised at render time, not when they are stored. Links go through Graph.sanitizeLink and the link handling in Graph (isCustomLink, customLinkClicked, labelLinkClicked). Plugins from other domains are refused, and custom plugins need ALLOW_CUSTOM_PLUGINS. Read Security before you change any of this.

Themes, UI modes and dark mode

  • Themes. Editor.currentTheme (from uiTheme, which comes from the ui URL parameter or the saved setting) is one of kennedy (the classic UI built by grapheditor), min (Minimal.js, EditorUi.initMinimalTheme), or simple, sketch and atlas (Simple.js). Editor.themes lists the themes that are installed. EditorUi.prototype.setCurrentTheme saves the choice; switching between kennedy and simple happens at once (doSetCurrentTheme), other changes need a reload.
  • Dark mode. Editor.darkMode and EditorUi.prototype.setDarkMode add the geDarkMode class and color-scheme: dark to the container. The dark URL parameter (1, 0 or auto) and the darkMode setting decide the initial state.
  • Adaptive colours. Diagram colours are emitted as CSS light-dark() values so that a diagram can follow the dark mode. graph.adaptiveColors (auto, simple or none, saved with the diagram, default Graph.defaultAdaptiveColors) controls how.
  • Sketch mode. Editor.sketchMode and EditorUi.prototype.setSketchMode switch the default styles for new cells to the hand-drawn look (updateDefaultStyles).

Offline, PWA and the service worker

service-worker.js is a precache service worker that is committed to the repository; the public build does not regenerate it. App.main registers it only where Editor.enableServiceWorker is true (see step 2 of App.main). images/manifest.json makes the app installable and registers the file handlers that arrive through window.launchQueue. EditorUi.prototype.isOffline is true with offline=1, stealth=1 or lockdown=1 and when the browser has no network (except in Electron); code that would contact a network service checks it. See supported-url-parameters.

Desktop (Electron) hooks

mxIsElectron (set in bootstrap.js) and EditorUi.isElectronApp identify the desktop app. js/diagramly/ElectronApp.js overrides the file handling (App.mode = App.MODE_DEVICE, local files, drafts, plugins, Editor.configure, menus) and talks to the Electron main process through the global electron object (electron.request, electron.sendMessage, electron.registerMsgListener). The main process lives in jgraph/drawio-desktop. See Desktop app.

Embed mode

embed=1 turns the app into an editor controlled by a parent window through postMessage. EditorUi.prototype.initializeEmbedMode (once the plugins have loaded) calls EditorUi.prototype.installMessageHandler, which posts the init event to the parent (ready without proto=json) and then handles actions such as load, merge, export and exit. The editor posts events such as save, autosave, export and exit back. The diagram received with load becomes an EmbedFile. configure=1 adds the configuration handshake described in App.main. See Embedding and embed-mode.

Realtime collaboration

For storage backends that support it, DrawioFile.prototype.startSync creates a DrawioFileSync, which sends the local changes to the other clients and applies theirs. Changes are exchanged as page diffs: EditorUi.prototype.diffPages and patchPages in DiffSync.js, applied with DrawioFile.prototype.patch. The same diffs merge remote changes after a save conflict.

Settings and configuration

mxSettings (diagramly/Settings.js) stores per-user preferences (language, theme, recent colours, plugins, libraries) in localStorage under Editor.settingsKey (.drawio-config). Editor.configure applies an administrator configuration; Editor.config keeps it. These are different things: settings are the user's choices, configuration is the deployment's.

See also

Clone this wiki locally