Save and Load Diagrams in React Diagram Component

21 Oct 202524 minutes to read

Serialization is the process of converting the diagram’s current state into a storage format that can be saved and later restored. This feature ensures that all diagram elements, including nodes, connectors, and their configurations, persist across application sessions.

The serialization process converts the diagram into a JSON string format, which can be stored in databases, files, or other storage systems. When needed, this serialized data can be deserialized to recreate the diagram exactly as it was previously configured.

Use serialization when you need to:

  • Save user-created diagrams for future editing.
  • Implement undo/redo functionality.
  • Create diagram templates.
  • Transfer diagrams between different sessions or users.

To save and load the diagram in React, refer to the below video link.

Saving Diagrams

Basic Save Operation

The saveDiagram method serializes the entire diagram configuration into a JSON string. This method captures all diagram elements, their properties, and the current state.

The following code illustrates how to save the diagram:

let saveData: string;
//returns serialized string of the Diagram
saveData = diagramInstance.saveDiagram();

The serialized JSON string can be stored in various storage systems. The following example demonstrates local storage implementation:

//Saves the string in to local storage
localStorage.setItem('fileName', saveData);

// Retrieve the saved string from local storage
saveData = localStorage.getItem('fileName');

Alternative Save Formats

The diagram can also be saved as raster or vector image files. For more information about saving the diagram as images, refer to the Print and Export sections.

Loading Diagrams

Basic Load Operation

The loadDiagram method recreates the diagram from serialized JSON data. This method accepts the previously saved JSON string as a parameter.

/*
 * Loads the diagram from saved JSON data.
 * parameter 1 - The string representing the diagram model JSON to be loaded.
 * parameter 2 - Whether it is ej1 data or not (optional)
 */
diagramInstance.loadDiagram(saveData);

Note: Before loading a new diagram, the existing diagram content is automatically cleared.

Handling Load Completion

The loaded event triggers when all diagram elements finish loading through the loadDiagram method. Use this event to perform post-load customizations or validations.

  return (
    loaded={(args) => {
            //You can use this event to customize diagram elements during the loading process
        }}/>
  )

The loaded event provides the following arguments:

name

Type: String

Description: Returns the event name.

diagram

Type: Diagram

Description: Returns the diagram model properties.

Users can perform customizations or modifications to the diagram elements once the loading process is complete.

Prevent Default Values

The preventDefaults property of serializationSettings reduces the size of serialized data by excluding default properties. This optimization improves performance when handling large diagrams or frequent save operations.

When enabled, only explicitly set properties are included in the JSON output, significantly reducing file size and improving load times.

  return (
    <div>
      <DiagramComponent
        serializationSettings = 
      ></DiagramComponent>
    </div>
  )

File-Based Save and Load Operations

21 Oct 202524 minutes to read

Using Uploader Component

JSON files can be uploaded and processed using the uploader component. Configure the uploader with appropriate server endpoints to handle file operations, then parse the uploaded JSON data to load diagrams.

The uploader requires:

  • saveUrl property for receiving and storing uploaded files.
  • removeUrl property for handling file deletion operations.
  • File parsing logic to extract JSON data from uploaded files.
import * as React from "react";
import * as ReactDOM from "react-dom";
import { DiagramComponent } from '@syncfusion/ej2-react-diagrams';
import { UploaderComponent } from '@syncfusion/ej2-react-inputs';
import { ButtonComponent } from '@syncfusion/ej2-react-buttons';

export default function App() {
    let diagramInstance;

    // Initialize Uploader Object
    let uploadObject = {
        saveUrl:
            'https://services.syncfusion.com/js/production/api/FileUploader/Save',
        removeUrl:
            'https://services.syncfusion.com/js/production/api/FileUploader/Remove',
    }


    // Callback function for when the upload is successful
    function onUploadSuccess(args) {
        var file = args.file.rawFile;
        var reader = new FileReader();
        reader.readAsText(file);
        reader.onloadend = loadDiagram;
    }

    // Function to load the diagram from the uploaded file
    function loadDiagram(event) {
        diagramInstance.loadDiagram(event.target.result);
    }

    // Function to save the current diagram
    function saveDiagram() {
        download(diagramInstance.saveDiagram());
    }

    // Function to download the diagram data as a JSON file
    function download(data) {
        if (window.navigator.msSaveBlob) {
            let blob = new Blob([data], { type: 'data:text/json;charset=utf-8,' });
            window.navigator.msSaveBlob(blob, 'Diagram.json');
        } else {
            let dataStr = 'data:text/json;charset=utf-8,' + encodeURIComponent(data);
            let a = document.createElement('a');
            a.href = dataStr;
            a.download = 'Diagram.json';
            document.body.appendChild(a);
            a.click();
            a.remove();
        }
    }

    // Initialize nodes for the diagram
    let nodes = [
        {
            id: 'Start',
            width: 140,
            height: 50,
            offsetX: 300,
            offsetY: 50,
            annotations: [{ id: 'label1', content: 'Start' }],
            shape: { type: 'Flow', shape: 'Terminator' }
        },
    ];

    return (
        // Initialize Diagram component
        <div>
            <label>Browse to load</label>
            <UploaderComponent asyncSettings={uploadObject} success={onUploadSuccess} />
            <ButtonComponent content="Save Diagram" onClick={saveDiagram} />
            <DiagramComponent
                id="container"
                ref={(diagram) => (diagramInstance = diagram)}
                width={'100%'}
                height={'600px'}
                nodes={nodes}
            ></DiagramComponent>
        </div>
    );
}

// Render the App component into the 'diagram' element in the DOM
const root = ReactDOM.createRoot(document.getElementById("diagram"));
root.render(<App />);
import * as React from "react";
import * as ReactDOM from "react-dom";
import { DiagramComponent, NodeModel } from '@syncfusion/ej2-react-diagrams';
import { UploaderComponent, FileInfo } from '@syncfusion/ej2-react-inputs';
import { ButtonComponent } from '@syncfusion/ej2-react-buttons';

export default function App() {
  let diagramInstance: DiagramComponent;

  // Initialize Uploader Object
  let uploadObject = {
    saveUrl:
      'https://services.syncfusion.com/js/production/api/FileUploader/Save',
    removeUrl:
      'https://services.syncfusion.com/js/production/api/FileUploader/Remove',
  }

  // Callback function for when the upload is successful
  function onUploadSuccess(args: { file: FileInfo }) {
    var file: any = args.file.rawFile;
    var reader = new FileReader();
    reader.readAsText(file);
    reader.onloadend = loadDiagram;
  }

  // Function to load the diagram from the uploaded file
  function loadDiagram(event: any) {
    diagramInstance.loadDiagram(event.target.result);
  }

  // Function to save the current diagram
  function saveDiagram() {
    download(diagramInstance.saveDiagram());
  }

  // Function to download the diagram data as a JSON file
  function download(data: string) {
    if ((window.navigator as any).msSaveBlob) {
      let blob = new Blob([data], { type: 'data:text/json;charset=utf-8,' });
      (window.navigator as any).msSaveBlob(blob, 'Diagram.json');
    } else {
      let dataStr = 'data:text/json;charset=utf-8,' + encodeURIComponent(data);
      let a: HTMLAnchorElement = document.createElement('a');
      a.href = dataStr;
      a.download = 'Diagram.json';
      document.body.appendChild(a);
      a.click();
      a.remove();
    }
  }

  // Initialize nodes for the diagram
  let nodes: NodeModel[] = [
    {
      id: 'Start',
      width: 140,
      height: 50,
      offsetX: 300,
      offsetY: 50,
      annotations: [{ id: 'label1', content: 'Start' }],
      shape: { type: 'Flow', shape: 'Terminator' }
    },
  ];

  return (
    // Initialize Diagram component
    <div>
      <UploaderComponent asyncSettings={uploadObject} success={onUploadSuccess} />
      <ButtonComponent content="Save Diagram" onClick={saveDiagram} />
      <DiagramComponent
        id="container"
        ref={(diagram: any) => (diagramInstance = diagram)}
        width={'100%'}
        height={'600px'}
        nodes={nodes}
      />
    </div>
  );
}

// Render the App component into the 'diagram' element in the DOM
const root = ReactDOM.createRoot(document.getElementById("diagram") as HTMLElement);
root.render(<App />);

Mermaid Syntax Integration

Overview

The Diagram component supports importing and exporting diagrams using Mermaid syntax. Mermaid is a markdown-inspired syntax for creating diagrams programmatically, enabling easy diagram creation and sharing across different platforms.

This functionality supports:

  • Mind maps
  • Flowcharts
  • UML sequence diagrams

Saving Diagrams as Mermaid Syntax

The saveDiagramAsMermaid method converts compatible diagrams into Mermaid syntax format. This method works specifically with Flowchart and Mind map layouts.

//returns the serialized Mermaid string of the Diagram
let data = diagramInstance.saveDiagramAsMermaid();

Load Diagram from Mermaid Syntax

The loadDiagramFromMermaid method creates diagrams from Mermaid syntax data, automatically generating the appropriate layout and styling.
data.

Load Flowchart Layout

The following example shows how to load flowchart diagram from mermaid syntax.

import * as React from "react";
import * as ReactDOM from "react-dom";
import { DiagramComponent, Inject, FlowchartLayout } from "@syncfusion/ej2-react-diagrams";

export default function App() {
    let diagramInstance;

    // Mermaid syntax for the flowchart data
    let mermaidFlowchartData = `flowchart TD
        A[Start] --> B(Process)
        B -.- C{Decision}
        C --Yes--> D[Plan 1]
        C ==>|No| E[Plan 2]
        style A fill:#90EE90,stroke:#333,stroke-width:2px;
        style B fill:#4682B4,stroke:#333,stroke-width:2px;
        style C fill:#FFD700,stroke:#333,stroke-width:2px;
        style D fill:#FF6347,stroke:#333,stroke-width:2px;
        style E fill:#FF6347,stroke:#333,stroke-width:2px;`;

    // Function to set default properties for nodes
    function getNodeDefaults(node) {
        node.width = 120;
        node.height = 50;
        if (node.shape.shape === 'Decision') {
            node.height = 80;
        }
        node.annotations[0].style.bold = true;
        return node;
    };

    // Function to set default properties for connectors
    function getConnectorDefaults(connector) {
        connector.type = 'Orthogonal';
        return connector;
    };

    // Function to load the Mermaid flowchart data into the diagram
    function loadMermaidFlowchart() {
        diagramInstance.loadDiagramFromMermaid(mermaidFlowchartData);
    };

    return (
      // Initialize Diagram component
        <div>
            <button onClick={loadMermaidFlowchart} id="loadMermaidFlowchart">Load Mermaid Flowchart</button>
            <DiagramComponent
                id="container"
                ref={(diagram) => (diagramInstance = diagram)}
                width={"100%"}
                height={"600px"}
                layout={{ type: 'Flowchart' }}
                getNodeDefaults={getNodeDefaults}
                getConnectorDefaults={getConnectorDefaults}
            >
                <Inject services={[FlowchartLayout]} />
            </DiagramComponent>
        </div>
    );
}

// Render the App component into the 'diagram' element in the DOM
const root = ReactDOM.createRoot(document.getElementById("diagram"));
root.render(<App />);
import * as React from "react";
import * as ReactDOM from "react-dom";
import { DiagramComponent, Inject, FlowchartLayout, ConnectorModel, NodeModel } from "@syncfusion/ej2-react-diagrams";

export default function App() {
  let diagramInstance: DiagramComponent;

  // Mermaid syntax for the flowchart data
  let mermaidFlowchartData: string = `flowchart TD
        A[Start] --> B(Process)
        B -.- C{Decision}
        C --Yes--> D[Plan 1]
        C ==>|No| E[Plan 2]
        style A fill:#90EE90,stroke:#333,stroke-width:2px;
        style B fill:#4682B4,stroke:#333,stroke-width:2px;
        style C fill:#FFD700,stroke:#333,stroke-width:2px;
        style D fill:#FF6347,stroke:#333,stroke-width:2px;
        style E fill:#FF6347,stroke:#333,stroke-width:2px;`;

  // Function to set default properties for nodes
  function getNodeDefaults(node: NodeModel | any): NodeModel {
    node.width = 120;
    node.height = 50;
    if (node.shape.shape === 'Decision') {
      node.height = 80;
    }
    node.annotations[0].style.bold = true;
    return node;
  };

  // Function to set default properties for connectors
  function getConnectorDefaults(connector: ConnectorModel): ConnectorModel {
    connector.type = 'Orthogonal';
    return connector;
  };

  // Function to load the Mermaid flowchart data into the diagram
  function loadMermaidFlowchart(): void {
    diagramInstance.loadDiagramFromMermaid(mermaidFlowchartData);
  };

  return (
    // Initialize Diagram component
    <div>
      <button onClick={loadMermaidFlowchart} id="loadMermaidFlowchart">Load Mermaid Flowchart</button>
      <DiagramComponent
        id="container"
        ref={(diagram: any) => (diagramInstance = diagram)}
        width={"100%"}
        height={"600px"}
        layout={{ type: 'Flowchart' }}
        getNodeDefaults={getNodeDefaults}
        getConnectorDefaults={getConnectorDefaults}
      >
        <Inject services={[FlowchartLayout]} />
      </DiagramComponent>
    </div>
  );
}

// Render the App component into the 'diagram' element in the DOM
const root = ReactDOM.createRoot(document.getElementById("diagram") as HTMLElement);
root.render(<App />);

Loading Mind Map Layout

The following example demonstrates loading a mind map diagram from Mermaid syntax:

import * as React from "react";
import * as ReactDOM from "react-dom";
import { DiagramComponent, Inject, MindMap } from "@syncfusion/ej2-react-diagrams";

export default function App() {
    let diagramInstance;

    // Mermaid syntax for the mindmap data
    let mermaidMindmapData = `mindmap
                              root((mindmap))
                                Origins
                                  Popularisation
                                Research
                                  On effectiveness<br/>and features
                                  On Automatic creation
                                Tools
                                  Pen and paper
                                  Mermaid`;

    // Function to set default properties for nodes
    function getNodeDefaults(node) {
        node.width = 140;
        node.height = 50;
        node.annotations[0].style.bold = true;
        return node;
    };

    // Function to set default properties for connectors
    function getConnectorDefaults(connector) {
        connector.type = 'Orthogonal';
        return connector;
    };

    // Function to load the Mermaid mindmap data into the diagram
    function loadMermaidMindmap() {
        diagramInstance.loadDiagramFromMermaid(mermaidMindmapData);
    };

    return (
        // Initialize Diagram component
        <div>
            <button onClick={loadMermaidMindmap} id="loadMermaidMindmap">Load Mermaid Mindmap</button>
            <DiagramComponent
                id="container"
                ref={(diagram) => (diagramInstance = diagram)}
                width={"100%"}
                height={"350px"}
                layout={{
                    type: 'MindMap',
                    verticalSpacing: 50,
                    horizontalSpacing: 50,
                    orientation: 'Horizontal',
                }}
                getNodeDefaults={getNodeDefaults}
                getConnectorDefaults={getConnectorDefaults}
            >
                <Inject services={[MindMap]} />
            </DiagramComponent>
        </div>
    );
}

// Render the App component into the 'diagram' element in the DOM
const root = ReactDOM.createRoot(document.getElementById("diagram"));
root.render(<App />);
import * as React from "react";
import * as ReactDOM from "react-dom";
import { DiagramComponent, Inject, MindMap, NodeModel, ConnectorModel } from "@syncfusion/ej2-react-diagrams";

export default function App() {
  let diagramInstance: DiagramComponent;

  // Mermaid syntax for the mindmap data
  let mermaidMindmapData: string = `mindmap
                              root((mindmap))
                                Origins
                                  Popularisation
                                Research
                                  On effectiveness<br/>and features
                                  On Automatic creation
                                Tools
                                  Pen and paper
                                  Mermaid`;

  // Function to set default properties for nodes
  function getNodeDefaults(node: NodeModel | any): NodeModel {
    node.width = 140;
    node.height = 50;
    node.annotations[0].style.bold = true;
    return node;
  };

  // Function to set default properties for connectors
  function getConnectorDefaults(connector: ConnectorModel): ConnectorModel {
    connector.type = 'Orthogonal';
    return connector;
  };

  // Function to load the Mermaid mindmap data into the diagram
  function loadMermaidMindmap() {
    diagramInstance.loadDiagramFromMermaid(mermaidMindmapData);
  };

  return (
    // Initialize Diagram component
    <div>
      <button onClick={loadMermaidMindmap} id="loadMermaidMindmap">Load Mermaid Mindmap</button>
      <DiagramComponent
        id="container"
        ref={(diagram: any) => (diagramInstance = diagram)}
        width={"100%"}
        height={"350px"}
        layout={{ type: 'MindMap', orientation: 'Horizontal' }}
        getNodeDefaults={getNodeDefaults}
        getConnectorDefaults={getConnectorDefaults}
      >
        <Inject services={[MindMap]} />
      </DiagramComponent>
    </div>
  );
}

// Render the App component into the 'diagram' element in the DOM
const root = ReactDOM.createRoot(document.getElementById("diagram") as HTMLElement);
root.render(<App />);

Loading UML Sequence Diagram

The following example demonstrates loading a UML Sequence diagram from Mermaid syntax:

import * as React from "react";
import * as ReactDOM from "react-dom";
import { DiagramComponent } from "@syncfusion/ej2-react-diagrams";

export default function App() {
let diagramInstance;
const mermaidData = `
 sequenceDiagram
    participant User
    participant Controller
    participant Service
    participant Database

    User->>Controller: sendRequest()
    activate Controller

    Controller->>Service: processRequest()
    activate Service

    Service->>Database: queryData()
    activate Database
    Database-->>Service: returnData()
    deactivate Database

    Service-->>Controller: returnResponse()
    deactivate Service

    Controller-->>User: sendResponse()
    deactivate Controller`;

    // Function to load the Mermaid sequence diagram data into the diagram
    function loadMermaidSeqDiagram() {
        diagramInstance.loadDiagramFromMermaid(mermaidData);
    };

    return (
        // Initialize Diagram component
        <div>
            <button onClick={loadMermaidSeqDiagram} id="loadMermaid">Load Mermaid</button>
            <DiagramComponent
                id="container" ref={(diagram) => (diagramInstance = diagram)}
                width={"100%"} height={"600px"}>
            </DiagramComponent>
        </div>
    );
}

// Render the App component into the 'diagram' element in the DOM
const root = ReactDOM.createRoot(document.getElementById("diagram"));
root.render(<App />);
import * as React from "react";
import * as ReactDOM from "react-dom";
import { DiagramComponent } from "@syncfusion/ej2-react-diagrams";

export default function App() {
  let diagramInstance: DiagramComponent;
  const mermaidData: string = `
 sequenceDiagram
    participant User
    participant Controller
    participant Service
    participant Database

    User->>Controller: sendRequest()
    activate Controller

    Controller->>Service: processRequest()
    activate Service

    Service->>Database: queryData()
    activate Database
    Database-->>Service: returnData()
    deactivate Database

    Service-->>Controller: returnResponse()
    deactivate Service

    Controller-->>User: sendResponse()
    deactivate Controller`;

  // Function to load the Mermaid sequence diagram data into the diagram
  function loadMermaidSeqDiagram() {
    diagramInstance.loadDiagramFromMermaid(mermaidData);
  };

  return (
    // Initialize Diagram component
    <div>
      <button onClick={loadMermaidSeqDiagram} id="loadMermaid">Load Mermaid</button>
      <DiagramComponent
        id="container" ref={(diagram: any) => (diagramInstance = diagram)}
        width={"100%"} height={"600px"}>
      </DiagramComponent>
    </div>
  );
}

// Render the App component into the 'diagram' element in the DOM
const root = ReactDOM.createRoot(document.getElementById("diagram") as HTMLElement);
root.render(<App />);

NOTE

Mermaid syntax-based serialization and deserialization supports only Flowchart layout, Mind map layout, and UML Sequence Diagram. Ensure that your Mermaid data aligns with one of these supported layouts for successful diagram loading.