drawio
is a powerful open-source online flowchart editor that supports drawing various types of diagrams. It provides support for both web and client-side editing, and offers the export of multiple resource types.
In our daily writing of papers and documents, we often have the need to draw flowcharts in order to better illustrate specific steps and processes. At such times, we might think of using Visio
or ProcessOn
, but be daunted by the large size of Visio
or the limitations of the free version of ProcessOn
. This is when we turn to our protagonist - drawio
. For regular users, drawio
provides a simple, free, and unlimited space for advanced drawing tools. For advanced developers, it offers a straightforward and quick way to build a free, powerful drawing tool for themselves and their team. It's a win-win situation.
The history of the drawio
project can be traced back to 2005 when the JGraph
team started developing mxGraph
, a JavaScript and SVG-based chart library for creating interactive charts in web applications, supporting Firefox 1.5 and Internet Explorer 5.5. In 2012, the JGraph
team removed the Java applet-related parts of the existing program and changed its domain from diagram.ly
to draw.io
, because the founder thought that io
sounded cooler than ly
. Thus, drawio
became a chart editor based on mxGraph
that could run in a browser and, initially, was an internal tool. Later, the mxGraph
team decided to release it as an open-source project. In 2020, due to security and copyright considerations, the JGraph
team moved draw.io
to the diagrams.net
domain, which remains an active open-source project with a large user base and contributors, supporting various types of charts including flowcharts, organizational charts, UML diagrams, and various file formats such as XML, PNG, JPEG, and PDF.
Integrating drawio
into our own projects has many advantages, including but not limited to out-of-the-box functionality, suitability for production environments, open-source nature, support for customization, and a strong community. However, drawio
also has its limitations. As can be seen from the brief summary, the project's history dates back quite far and it lacks support for ESM
. There are a significant number of prototype chain modifications, making the code quite complex, with poor readability and maintainability. It also doesn't support TypeScript
, all of which are issues that need solving. In fact, a more modern approach favored in modern browsers would be to use a completely Canvas
-based drawing board. However, this approach comes with a considerable cost. Therefore, if we want to integrate a flowchart editor into our own projects at a low cost, drawio
is one of the best choices.
The question is, how do we integrate drawio
into our own projects? We offer two approaches here. One is as a standalone editor, by bundling the Npm
package into our own projects. The other is to embed drawio
using an iframe
and communicate with the deployed drawio
project. Both approaches can be used to accomplish the integration of the flowchart. The related content described in this article is available on Github | Editor DEMO.
First, let's explore how to integrate a standalone editor into our own projects. Let's first take a look at the mxGraph
project, whose documentation is available at https://jgraph.github.io/mxgraph/
. We can see that mxGraph
supports three languages: .NET
, Java
, and JavaScript
. Here, we are primarily interested in its support for JavaScript
. In the documentation, we can find numerous examples, with the “Graph Editor” being the one we should focus on. When we open this example at https://jgraph.github.io/mxgraph/javascript/examples/grapheditor/www/index.html
, we find a complete editor project. Furthermore, we can see that the link has a .html
extension and is deployed on Github
's Git Pages
. This means that the .html
suffix is not generated by the backend but is a complete frontend project. Therefore, in theory, we can integrate it into our own projects as a pure frontend package.
Given that frontend development nowadays heavily relies on Npm
packages, we would prefer to integrate this package directly as a dependency into our project. However, upon reviewing the related code, we find that it is not a simple task. For example, when we open the Graph.js
file, we are surprised to find that this single file contains as many as 11,941
lines of code, not to mention that the core part actually includes 10
core classes such as:
And if we carefully observe the relevant variable naming, we can find that these ten core classes are not packed or obfuscated code. In other words, they are written in this form originally, which could make it quite difficult to maintain during our secondary development. Also, we can't really expect support for TS
because this is indeed a very old project. When it was first developed, TypeScript
might not have even started yet. On top of that, if there is a current need to use mxGraph
as a basis to develop a new project from scratch instead of integrating an existing project, it is currently more recommended to use maxGraph
. mxGraph
has long ceased maintenance, while maxGraph
aims to provide as much as possible the same functionality as mxGraph
. It supports TypeScript
, Tree Shaking
, and ES Module
as a modern vector graphics library.
Returning to the issue of integrating the independent editor, our goal is to create a Graph Editor
, and this editor is based on mxGraph
. So, our first step now is to install mxGraph
as a dependency. mxGraph
has an npm
package, so just install this dependency directly. For TS
projects, there is also a @typed-mxgraph/typed-mxgraph
package. Then specify the typeRoots
configuration in the tsconfig.json
, and you're good to go. In fact, we are not too concerned about the TS
definitions here, because as we described earlier, the main modules are defined in JS
. However, it's still very useful when fixing some BUG
. So, after installing the main package of mxGraph
and the TS
definitions, let's first define the modules to be referenced. Of course, actually here because mxGraph
doesn't have ESM
, there is no support for Tree Shaking
. The main purpose here is to facilitate the subsequent module references and the configuration of initializing the modules.
When writing this referencing module, because mxGraph
does not have ESM
support, I considered using maxGraph
as a substitute, but after some attempts, it ultimately failed. There seems to be a certain GAP
between the two packages, so I ended up choosing to use mxGraph
after all. Additionally, if necessary, you can configure externals
to avoid the need for a complete package of mxGraph
, but I won't go into detail about this configuration here. So, the next main task is to bring in the Graph Editor
. This part is the most time-consuming and troublesome. During the integration process, we primarily did the following:
Separate the main module and integrate it into our current project. This part of the work is actually relatively simple. It just involves downloading all the necessary code into our own project. Of course, at the beginning, it's a bit confusing because it's difficult to organize this part of the code without understanding it. Additionally, the project uses a lot of values on the window
object, making it hard to find so many undefined variables without the help of some tools. Simply copying the code over won't make it run directly, so we need to address all these issues such as undef
and external resource references.
Deal with all resource files, including images and style modules, and remove all dependency path resource references. This part of the work involves handling external resource references. The Graph Editor actually has many external resource references, including multilingual support and images. The configurations we made such as mxBasePath
and mxResourceExtension
are for handling external resources. However, since we prefer to use it as an npm package, dealing with resource path issues is relatively tricky. Therefore, our approach here is to process all image resources into Base64 and integrate them directly. In the process, we also modified the related code to prevent it from making requests to load external resources. Additionally, due to some objective reasons during the modification process, the project's image resources are divided into two types: one is converted into a Base64 TS file, and the other is loaded using a loader. However, both are essentially Base64 resources. The goal here is to no longer make requests for external resources.
Utilize ESLint to streamline some of the code, remove support for some IE browsers, and use Prettier to format the code of the various modules. This part of the work is quite complex. First, we use ESLint to streamline the code, meaning progressively relaxing ESLint rules for the core modules. We modify the related code based on these rules. For example, using no-undef
helps to find all undefined modules, and then we handle the references to these modules. The no-unused-vars
rule helps to identify unused variables, allowing us to streamline the code. We are currently more focused on modern browsers and no longer want to provide additional support for IE browsers, so we also removed some IE-compatible code. With the help of Prettier and the prettier/prettier
rule, we can format the code. After formatting the code, we can see that the implementation of the related modules is more comfortable, and it also solves some implicit issues. For example, taking the core class Graph.js
as an example, the code was streamlined from 11,941 lines to 10,637 lines.
Handle multilingual support, currently supporting EN and ZH-CN. This part of the work primarily involves multilingual support. We no longer want to load external resources, and multilingual support is no exception. We have defined the relevant languages here. To load a specific language, you only need to pass the language module's configuration when starting the editor. Additionally, not all language modules need to be loaded. We have implemented a method to load them on demand to reduce the package size. In fact, we also recommend lazy loading the main package into your project.
After completing the integration mentioned above, we were able to successfully launch the project in its entirety. However, during actual usage, we discovered some bugs. For example, when we opened the latest online link for Graph Editor, we found that the Sketch style was not working. Therefore, we still need to fix some bugs in the entire package. Here, we mainly listed three bug fixes for reference.
External module loading issue. It is well known (or maybe not that well known) that many modules of mxGraph
are attached to the window
. These modules come in various types, such as graphic modules like mxGraphModel
, mxGeometry
, mxCell
, and so on, as well as utility modules like mxUtils
, mxEvent
, mxCodec
, and so on. However, since we are importing it as an npm package, we do not want to pollute the global variables. Moreover, when loading graphics via XML, we need to find these graphic modules; otherwise, the graphics won't be displayed. After analyzing the source code, we found that the dynamic loading occurs in the decode
method of mxCodec
. Therefore, we need to handle the loading function of these modules here. While directly attaching the mxGraph
module package to the window
using the external
approach may also be a feasible solution, we chose to override the relevant modules to achieve this.
The "Sketch" issue is not valid. If we open the latest online link of the "Graph Editor," we can see that the "Sketch" style is invalid. Since "mxGraph" is no longer maintained, reporting the bug is ineffective. In fact, dealing with this issue is relatively simple, we can just use git
to revert to the version where the functionality is normal.
There is a problem with the mounting sub-container of "Scroll" and the menu. This problem is quite embarrassing because "mxGraph" has always been designed as a whole application. However, when we need to embed it into another application, since our scroll container may be the body
, when we have already scrolled the page down, if we then open the flowchart editor, we will find that we cannot drag the canvas or select shapes normally. Moreover, the menu position calculation is also incorrect. Therefore, it is necessary to ensure that the relevant position calculation is correct here.
Finally, in fact, due to the lack of TreeShaking
, and the need for dynamic loading of graphics, our entire package has a relatively large volume. Therefore, in order not to affect the core ability of the application, we still recommend using lazy loading to load the editor. Specifically, you can import types using import type
and then load modules using import()
.
As we finished the flowchart editor NPM package based on mxGraph Example
, we noticed that mxGraph
is no longer maintained, and JGraph
has further developed drawio
based on mxGraph Example
. This is a long-term maintained project. Even though drawio
does not accept contributions, it is still active. You can experience the deployment version of drawio
here: https://app.diagrams.net/
.
What we are more concerned about here is how to embed drawio
into our application. drawio
provides an embed
way to help us integrate it into our own application. By using iframe
and postMessage
for communication, we won't be subject to cross-domain restrictions, thus achieving a series of functions such as editing, importing, and exporting.
Here, we will implement the embedding of drawio
through simple encapsulated communication. Specifically, we will load drawio
using the iframe
. Of course, due to network issues, it is still necessary to deploy a private set for actual production environments. After private deployment, customization is also feasible. Of course, if the network supports it, using the deployment version of drawio
directly is also feasible. Ultimately, the data storage will be in our own application.
When we use it, we just need to instantiate the object and enter the editing mode directly. In addition, drawio
supports exporting multiple types of data, but here we still recommend xmlsvg
. Simply put, this data structure is based on the svg
tag and carries xml
data. In this way, some redundant fields can be directly displayed as svg
or imported into drawio
for re-editing. If exported as svg
, it cannot be re-imported for editing. If only xml
is exported, it can be edited again, but if you want to display it as svg
, you need to use viewer.min.js
for rendering, which depends on the suitable export type according to the demand.