Repository navigation
Architecture
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.
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=1loads 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/webappcan be served by any static web server. The Java servlets insrc/main/server/javaare 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.
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
| 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
|
-
mxgraphnever references the layers above it. It knows nothing aboutGraph,Editor,EditorUi,ApporurlParams. Behaviour that draw.io needs is added by overriding mxGraph prototypes inGraph.js,Shapes.jsordiagramly/Editor.js, never by editing mxGraph to call up. -
grapheditoris meant to know nothing about the application. It is no longer built or shipped withoutdiagramly, and a few references leak upward:App.ERROR_TIMEOUT(inEditorUi.js),mxSettings(inEditorUi.jsandActions.js), andEditorUi.prototype.isOffline,EditorUi.debugandEditorUi.isElectronApp, which are defined indiagramly/EditorUi.jsbut called fromDialogs.js,Sidebar.js,Format.jsandEditor.js. Treat these as debt, not as a pattern. Optional code from the extensions bundle is only reached through feature detection, for exampletypeof LibavoidRouting !== 'undefined'inFormat.js,Menus.jsandGraph.js, andtypeof ElkLayoutinGraph.prototype.createLayouts. -
diagramlyextends; it does not subclass. Apart fromApp, there is exactly oneEditor, oneEditorUi, oneGraphand oneMenusclass at runtime. The files indiagramly/with the same names add statics and replace prototype methods of the classes defined ingrapheditor/(see the example below). Because of this, load order matters:diagramly/Editor.jsmust load aftergrapheditor/Editor.js, and so on. The order is fixed inDevel.js(development) andetc/build/build.xml(bundles). -
Optional engines are global objects, checked before use. ELK (
ElkLayout), Mermaid (mxMermaidToDrawio, gated byEditorUi.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/andjs/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);
// ...
};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"]
| 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:
-
AppextendsEditorUi(mxUtils.extend(App, EditorUi)inApp.js). -
EditorUi,Editor,DrawioFile,DrawioClient,DrawioFileSync,GraphViewerandHoverIconsextendmxEventSource, so they all haveaddListenerandfireEvent. -
GraphextendsmxGraph. - Every storage backend has a
*Fileclass that extendsDrawioFile, a*Libraryclass for shape libraries and, for cloud backends, a*Clientclass that extendsDrawioClient.GitLabFileandGitLabClientextend their GitHub counterparts. See Storage backends.
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:
- Parses the query string into the global
urlParams. A hash that starts with#Pcarries more parameters as JSON. - Sets
mxIsElectronfrom the user agent. - Defines the script loaders
mxscript(src, onLoad, ...)andmxinclude(src), used everywhere later to load code on demand. - 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) andjs/PostConfig.js.main.jsthen callsApp.main()directly. -
Production:
js/app.min.js. On hosts outside the*.draw.ioand*.diagrams.netdomains (that is, on your own server) and in Electron,js/PreConfig.jsis loaded before it andjs/PostConfig.jsafter it. When both the bundle and the window have loaded,checkAllLoaded()callsApp.main(). In Electron it also loadsDesktopLibrary.js,ElectronApp.js,extensions.min.js, the stencils, the shapes and the PlantUML bundle.
-
Development (
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.
App.main(callback, createUi) prepares everything that must exist before the UI is created, in this order:
- Guards against a second call, installs a global
window.onerrorand adds thegeEmbedCSS class in embed mode. -
Service worker. If
Editor.enableServiceWorkeris true, registers or updatesservice-worker.js. Withoffline=0, and in dev mode unlessoffline=1orenableSW=1is set, it removes it instead (App.clearServiceWorker).Editor.enableServiceWorkeris only true on*.diagrams.netand*.draw.iohosts or with theoffline=1orenableSW=1URL parameters, andpwa=0turns it off. -
Realtime scripts for collaboration, when the conditions in
App.mainallow it. -
Plugins.
App.initPluginCallback()defines the globalDraw.loadPlugin, which queues callbacks until the UI exists. Plugins come from thepURL parameter (only the short ids inApp.pluginRegistry, seeApp.loadPlugins) and from the plugin list saved inmxSettings. Saved plugins must be on the same domain, and unlessALLOW_CUSTOM_PLUGINSis set they must also be built-in (App.isBuiltInPlugin).plugins=0andoffline=1load none. Each plugin is loaded withmxscript. - The Google API script, if the Drive client is part of the bundle and not disabled.
-
Math.
Editor.initMath()installs the MathJax hooks unlessmath=0. MathJax itself is loaded later, on the first typeset. -
Configuration. With
configure=1, the app posts{event: 'configure'}to its parent window and waits for an{action: 'configure', config: ...}message. Otherwise it applieswindow.DRAWIO_CONFIGand then the JSON stored underEditor.configurationKeyinlocalStorage(not in embed mode). Each is passed toEditor.configure(config), followed bymxSettings.load(). See Configuration and configure-diagram-editor. -
doMain()applies the settings overrides (autosaveDelay,defaultEdgeLength), prefetches custom default fonts and callsdoLoad()with the language file formxLanguage. -
doLoad()fetchesresources/dia.txtorresources/dia_<lang>.txtand passes it tomxResources.parse. It handles a#_CONFIG_configuration link, then in production loadsjs/shapes-14-6-5.min.js,js/stencils.min.jsandjs/extensions.min.jswithApp.loadScriptsbefore it callsrealMain(). In dev mode and Electron those are already loaded. -
realMain()picks the theme (Editor.currentTheme, falling back tokennedyif the theme is not inEditor.themes) and creates the UI withnew App(new Editor(urlParams['chrome'] == '0' || uiTheme == 'min', null, null, null, urlParams['chrome'] != '0')). It then registers the PWAlaunchQueueconsumer for files opened from the operating system, loads the Dropbox, OneDrive and Trello SDKs if those backends are enabled, and callscallback(ui).
-
Editor(grapheditor/Editor.js):initStencilRegistry(), thencreateGraph()(anew Graph(...)), thencreateUndoManager(). A modelCHANGElistener callssetModified(true)for every edit that is not flaggedignoreEdit. -
EditorUi(grapheditor/EditorUi.js): createsui.actions = new Actions(this)andui.menus = this.createMenus(), thencreateDivs()(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)andeditor.resetGraph(). Finally it callsthis.init()andthis.open(). The diagramly override ofinitadds pages, the clipboard handlers, extra keyboard shortcuts, live orthogonal routing and much more. Themin,simple,sketchandatlasthemes replace parts of this UI (Minimal.js,Simple.js). -
Appconstructor: installs the drop handler for files, runs every queued plugin callback with the newui, replacesDraw.loadPluginso that plugins loaded later run immediately, and callsthis.load().
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:
-
descURL parameter: a CSV import descriptor (loadDescriptor). -
urlURL parameter:loadFile('U' + url). -
client=1: waits for XML from the opener window. -
createparameter or#create=hash: the create dialog orexecuteCreateObject. - A hash with a file id:
App.prototype.loadFile(id). The first character selects the source:Lbrowser storage,Ua URL,Rraw (optionally compressed) data,Eembedded file, andG,D,W,M,H,A,Tmap to the cloud backends throughApp.prototype.getModeForCharand the client'sgetFile. The prefixes are documented in supported-location-hash-properties. - No id: drafts (
checkDrafts), the splash screen (loadFile()without an id ends infileLoaded(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.
-
EditorUi.prototype.setFileData(data)parses the XML. For an<mxfile>it wraps every<diagram>node in aDiagramPage, stores the pages inui.pages, keeps the<mxfile>element inui.fileNodeand picks the current page (from the hash, thepage-idorpageURL parameter, or the first page). - It calls
editor.setGraphXml()with the current page's<diagram>node. The diagramly override extracts the<mxGraphModel>(Editor.extractGraphModel, which usesEditor.parseDiagramNodeand decompresses the page withGraph.decompressif it is not plain XML) and reads draw.io's graph attributes such asmathandadaptiveColors. - The grapheditor
Editor.prototype.setGraphXmlreads the remaining graph state (grid, page format, background, ...) and decodes the cells withmxCodecinside amodel.beginUpdate()/endUpdate()pair. - The model fires
CHANGE,mxGraph.graphModelChangedinvalidates the view, and the view validates and renders (see Rendering).
The File format page describes the XML.
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 throughmxGraphModel.prototype.executeand collected inmodel.currentEdit. - When the outermost
endUpdate()runs, the model firesmxEvent.CHANGEwith the list of changes and thenmxEvent.UNDOwith themxUndoableEdit. NestedbeginUpdate()calls are merged into one undoable edit. -
Editor.prototype.createUndoManagerlistens toUNDOand adds the edit toeditor.undoManager(anmxUndoManager). Undo and redo restore the selection from the changes. -
Editor's change listener setseditor.modified. In the app,DrawioFile.prototype.installListenerslistens to the sameCHANGEevent and to UI events (grid, page format, background, math, ...) and callsDrawioFile.prototype.fileChanged, which marks the file as modified, schedulesautosave()ifisAutosave()is true, and passes the edit to the realtime sync (file.sync.localFileChanged) if one is running. - An edit with
ignoreEdit = truechanges neither the modified flag nor triggers autosave; page selection uses this. -
autosave(delay, maxDelay)savesautosaveDelayms after the last change (1500 ms inDrawioFile, less for some backends), but at the latest aftermaxAutosaveDelayof continuous editing.
The Graph model page covers cells, changes and events in detail.
- The
saveaction (Ctrl+S) callsApp.prototype.saveFile, which asks for a name and a location if the file needs them and then callsApp.prototype.save, which callsfile.save(...). Autosave callsfile.savedirectly. -
DrawioFile.prototype.savewaits for the fonts used in the file and callsupdateFileData(), which callscreateData(), which callsEditorUi.prototype.getFileData(...). -
getFileDatawrites the current page back into its<diagram>node (Editor.prototype.getGraphXmlencodes the model withmxCodec) and serialises the whole<mxfile>. Pages are compressed withGraph.compressNode(deflate plus Base64) if the file is compressed: thecompressedattribute of<mxfile>decides, and new files are uncompressed by default (Editor.defaultCompressed). For.svgfilesgetFileDataembeds the XML in the SVG; for.pngfiles the backends embed it withui.getEmbeddedPng. - The subclass writes the data:
DriveFile.prototype.saveFilecallsui.drive.saveFile,LocalFiledownloads it or writes it through the File System Access handle,GitHubFilecommits it, and so on. - On success the backend calls
fileSaved(). On a conflict (the stored file changed since it was loaded),handleConflictErroreither synchronises (synchronizeFile, which merges the remote version withmergeFileand the page diffs inDiffSync.js) and saves again, or shows a conflict dialog.
The view turns cells into cell states and the renderer turns cell states into SVG:
-
mxGraph.graphModelChanged(changes)callsprocessChangefor each change, which invalidates the affected cells withview.invalidate(cell, ...), and thenview.validate(). -
mxGraphView.prototype.validatewalks the visible cells.validateCellcreates or removes anmxCellStateper cell;validateCellStateresolves the style (graph.getCellStyle: stylesheet defaults, named styles and the cell'skey=valuepairs), computes the state withupdateCellState(vertex bounds from the geometry and the parent's origin; for edges the terminal points, the edge style frommxEdgeStyleand the perimeter points) and callsgraph.cellRenderer.redraw(state). -
mxCellRenderer.prototype.redrawcreates or updates the shape and the label.createShapelooks theshapestyle up in the shapes registered withmxCellRenderer.registerShape(built-ins in mxGraph, draw.io's inShapes.jsandshapes/) or as a stencil inmxStencilRegistry.mxShape.redrawpaints through anmxSvgCanvas2Dinto the SVGdrawPane. - Labels are
mxTextshapes. HTML labels (html=1) are rendered in aforeignObject. Labels are stored raw in the model; thecellRenderer.getLabelValueoverride in theGraphconstructor sanitises HTML labels withGraph.sanitizeHtmleach time they are rendered. - 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).
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.
-
Chromeless editor.
new Editor(chromeless, ...)withchromelesstrue (chrome=0or themintheme) creates no menubar, sidebar, format panel or toolbar. Witheditablefalse the graph is read-only (graph.isEnabledreturns false). -
Lightbox.
lightbox=1(whichdiagramly/Init.jsturns intochrome=0) createsAppin lightbox mode: a chromeless, read-only full-window view with a small toolbar. The viewer opens it in a new window onDRAWIO_LIGHTBOX_URL(EditorUi.lightboxHost). -
Viewer.
GraphViewer.jsrenders diagrams in other pages without anEditorUi.GraphViewer.processElements()finds everydiv.mxgraph, reads itsdata-mxgraphJSON (the XML or aurl, plus options such astoolbar,lightbox,auto-fit) and creates aGraphViewer, which creates a plainGraphin the element. Clicking it opens the lightbox, either in-page (showLocalLightbox, a chromelessEditorUi) or in a new window. The viewer bundlesviewer.min.jsandviewer-static.min.jsend by callingEditor.initMath()and thenwindow.onDrawioViewerLoad()if it exists, elseGraphViewer.processElements(). See Embedding.
| 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.
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.
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.
Editor.currentTheme(fromuiTheme, which comes from theuiURL parameter or the saved setting) is one ofkennedy(the classic UI built bygrapheditor),min(Minimal.js,EditorUi.initMinimalTheme), orsimple,sketchandatlas(Simple.js).Editor.themeslists the themes that are installed.EditorUi.prototype.setCurrentThemesaves the choice; switching betweenkennedyandsimplehappens at once (doSetCurrentTheme), other changes need a reload. -
Dark mode.
Editor.darkModeandEditorUi.prototype.setDarkModeadd thegeDarkModeclass andcolor-scheme: darkto the container. ThedarkURL parameter (1,0orauto) and thedarkModesetting 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,simpleornone, saved with the diagram, defaultGraph.defaultAdaptiveColors) controls how. -
Sketch mode.
Editor.sketchModeandEditorUi.prototype.setSketchModeswitch the default styles for new cells to the hand-drawn look (updateDefaultStyles).
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.
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=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.
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.
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.
- Code map: where the code for each feature lives
- Glossary: the terms used on this page
- Graph model: cells, geometry, changes, events and undo
- File format: the XML that is loaded and saved
- Styles: style strings and the stylesheet
- Building: how the bundles are made
- Plugins and Extending the editor: changing behaviour without forking
- Embedding: embed mode and the viewer
- Security: rules for code that renders diagrams
- Debugging: dev mode and browser tools
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