# diagram-mindmap

> Mind map diagram configures custom tools, node and connector creation, selection events, and interactive diagram constraints.

**Framework:** blazor  **Component:** Diagram  **Variant:** mindmap

## Get this item

**If you are an agent, fetch the JSON.** Source is inlined, so one request is enough and no tooling is required:

```
GET https://ai.syncfusion.com/r/blazor/diagram-mindmap.json
```

**Install Package(s)**

```bash
dotnet add package Syncfusion.Blazor.Diagram
```

**Notes**

- Syncfusion release: 2026 Volume 2 (v34.1.29)
- The Syncfusion package is licensed. The composition in this file is source you own and edit. See https://ai.syncfusion.com/licensing.md

## Source files

### src/components/diagram-mindmap/Diagram.razor

```razor
@page "/diagram/mindmap"
@using System.Collections.ObjectModel
@using Syncfusion.Blazor.Diagram
@using NodeShape = Syncfusion.Blazor.Diagram.NodeShapes

<div class="content-wrapper">
    <div class="col-lg-9 control-section" style="border-right: 1px solid #D7D7D7;height: 640px">
        <SfDiagramComponent @ref="@DiagramInstance" InteractionController="@InteractionController" Height="600px" GetCustomTool="@GetCustomTool" NodeCreating="@NodeCreating" ConnectorCreating="@ConnectorCreating" SelectionSettings="@DiagramSelectionSettings" SelectionChanging="OnSelectionChanging" Created="OnCreated" Constraints="@_diagramConstraints">
            <ScrollSettings @bind-ScrollLimit="@ScrollLimit"></ScrollSettings>
            <Layout FixedNode="1" HorizontalSpacing="@HorizontalSpacing" VerticalSpacing="@VerticalSpacing" Type="LayoutType.MindMap" GetBranch="@GetBranch"></Layout>
            <SnapSettings Constraints="@SnapConstraints.None"></SnapSettings>
            <DataSourceSettings ID="Id" ParentID="ParentId" DataSource="@DataSource">
            </DataSourceSettings>
        </SfDiagramComponent>
    </div>
</div>
@code
{
    static List<MindMapDetails>? MindmapData { get; set; } = new List<MindMapDetails>();
    ScrollLimitMode ScrollLimit { get; set; } = ScrollLimitMode.Diagram;
    SfDiagramComponent? DiagramInstance { get; set; }
    DiagramInteractions InteractionController { get; set; } = DiagramInteractions.SingleSelect;
    private DiagramConstraints _diagramConstraints = DiagramConstraints.Default & ~DiagramConstraints.UndoRedo;
    int VerticalSpacing { get; set; } = 20;
    int HorizontalSpacing { get; set; } = 80;
    int? HorizontalValue { get; set; } = 80;
    int? VerticalValue { get; set; } = 20;
    DiagramSelectionSettings? DiagramSelectionSettings { get; set; } = new DiagramSelectionSettings();
    DiagramObjectCollection<UserHandle> Handles { get; set; } = new DiagramObjectCollection<UserHandle>();
    private BranchType GetBranch(IDiagramObject obj)
    {
        BranchType Branch = ((obj as Node)!.Data as MindMapDetails)!.Branch;
        return Branch;
    }
    private void OnCreated()
    {
        DiagramInstance!.Select(new ObservableCollection<IDiagramObject>() { DiagramInstance.Nodes![0] });
    }
    private void OnHorizontalSpaceChange(Syncfusion.Blazor.Inputs.ChangeEventArgs<int?> args)
    {
        HorizontalValue = (int)args.Value!;
        HorizontalSpacing = int.Parse(args.Value.ToString()!);
    }
    private void OnVerticalSpaceChange(Syncfusion.Blazor.Inputs.ChangeEventArgs<int?> args)
    {
        VerticalValue = (int)args.Value!;
        VerticalSpacing = int.Parse(args.Value.ToString()!);
    }
    // Method to customize the tool
    public InteractionControllerBase GetCustomTool(DiagramElementAction action, string id)
    {
        InteractionControllerBase? tool = null;
        if (id == "AddLeft")
        {
            tool = new AddRightTool(DiagramInstance!);
        }
        else if(id == "AddRight")
        {
            tool = new AddLeftTool(DiagramInstance!);
        }
        else
        {
            tool = new DeleteTool(DiagramInstance!);
        }
        return tool;
    }
    // Custom tool to add the node.
    public class AddLeftTool : InteractionControllerBase
    {
        SfDiagramComponent DiagramInstance;
        public AddLeftTool(SfDiagramComponent Diagram) : base(Diagram)
        {
            DiagramInstance = Diagram;
        }
        public override async void OnMouseDown(DiagramMouseEventArgs args)
        {
            int newChildID = DiagramInstance.Nodes!.Count + 1;
            string newchildColor = "";
            BranchType? type = (DiagramInstance.SelectionSettings!.Nodes![0].Data as MindMapDetails)!.Branch;
            BranchType childType = BranchType.Left;
            switch (type.ToString())
            {
                case "Root":
                    childType = BranchType.Left;
                    break;
                case "Left":
                    childType = BranchType.SubLeft;
                    break;
                case "SubLeft":
                    childType = BranchType.SubLeft;
                    break;
            }
            if (DiagramInstance.SelectionSettings.Nodes[0].Style!.Fill == "#034d6d")
            {
                newchildColor = "#1b80c6";
            }
            else
            {
                newchildColor = "#3dbfc9";
            }
            MindMapDetails childNode = new MindMapDetails()
            {
                Id = newChildID.ToString(),
                ParentId = (DiagramInstance.SelectionSettings.Nodes[0].Data as MindMapDetails)!.Id,
                Fill = newchildColor,
                Branch = childType,
                Label = "New Child"
            };
            DiagramInstance.BeginUpdate();
            await UpdatePortConnection(childNode, DiagramInstance);
            await DiagramInstance.EndUpdateAsync();
            MindmapData!.Add(childNode);
            DiagramInstance.ClearSelection();
            base.OnMouseDown(args);
            DiagramInstance.Select(new ObservableCollection<IDiagramObject>() { DiagramInstance.Nodes[DiagramInstance.Nodes.Count - 1] });
            DiagramInstance.StartTextEdit(DiagramInstance.Nodes[DiagramInstance.Nodes.Count - 1]);
            this.InAction = true;
        }
    }
    private static async Task UpdatePortConnection(MindMapDetails childNode,SfDiagramComponent DiagramInstance)
    {
        Node node = new Node()
        {
            Data = childNode,
        };
        Connector connector = new Connector()
        {
            TargetID = node.ID,
            SourceID = DiagramInstance.SelectionSettings!.Nodes![0].ID
        };
        await DiagramInstance.AddDiagramElementsAsync(new DiagramObjectCollection<NodeBase>() { node, connector });
        Node? sourceNode = DiagramInstance.GetObject((connector as Connector).SourceID!) as Node;
        Node? targetNode = DiagramInstance.GetObject((connector as Connector).TargetID!) as Node;
        if (targetNode != null && targetNode.Data != null)
        {
            MindMapDetails? nodeInfo = (targetNode.Data as MindMapDetails);
            if (nodeInfo!.Branch == BranchType.Right || nodeInfo.Branch == BranchType.SubRight)
            {
                (connector as Connector).SourcePortID = sourceNode!.Ports![0].ID;
                (connector as Connector).TargetPortID = targetNode.Ports![1].ID;
            }
            else if (nodeInfo.Branch == BranchType.Left || nodeInfo.Branch == BranchType.SubLeft)
            {
                (connector as Connector).SourcePortID = sourceNode!.Ports![1].ID;
                (connector as Connector).TargetPortID = targetNode.Ports![0].ID;
            }
        }
        await DiagramInstance.DoLayoutAsync();
    }
    // Custom tool to add the node.
    public class AddRightTool : InteractionControllerBase
    {
        SfDiagramComponent DiagramInstance;
        public AddRightTool(SfDiagramComponent Diagram) : base(Diagram)
        {
            DiagramInstance = Diagram;
        }
        public override async void OnMouseDown(DiagramMouseEventArgs args)
        {
            int newChildID = DiagramInstance.Nodes!.Count + 1;
            string newchildColor = "";
            BranchType type = (DiagramInstance.SelectionSettings!.Nodes![0].Data as MindMapDetails)!.Branch;
            BranchType childType = BranchType.Left;
            switch (type.ToString())
            {
                case "Root":
                    childType = BranchType.Right;
                    break;
                case "Right":
                    childType = BranchType.SubRight;
                    break;
                case "SubRight":
                    childType = BranchType.SubRight;
                    break;
            }
            if (DiagramInstance.SelectionSettings.Nodes[0].Style!.Fill == "#034d6d")
            {
                newchildColor = "#1b80c6";
            }
            else
            {
                newchildColor = "#3dbfc9";
            }
            MindMapDetails childNode = new MindMapDetails()
            {
                Id = newChildID.ToString(),
                ParentId = (DiagramInstance.SelectionSettings.Nodes[0].Data as MindMapDetails)!.Id,
                Fill = newchildColor,
                Branch = childType,
                Label = "New Child"
            };
            DiagramInstance.BeginUpdate();
            await UpdatePortConnection(childNode, DiagramInstance);
            await DiagramInstance.EndUpdateAsync();
            MindmapData!.Add(childNode);
            DiagramInstance.ClearSelection();
            base.OnMouseDown(args);
            DiagramInstance.Select(new ObservableCollection<IDiagramObject>() { DiagramInstance.Nodes[DiagramInstance.Nodes.Count - 1] });
            DiagramInstance.StartTextEdit(DiagramInstance.Nodes[DiagramInstance.Nodes.Count - 1]);
            this.InAction = true;
        }
    }
    public class DeleteTool : InteractionControllerBase
    {
        SfDiagramComponent sfDiagram;
        Node? deleteObject = null;
        public DeleteTool(SfDiagramComponent Diagram) : base(Diagram)
        {
            sfDiagram = Diagram;
        }
        public override void OnMouseDown(DiagramMouseEventArgs args)
        {
            deleteObject = (sfDiagram.SelectionSettings!.Nodes![0]) as Node;
        }
        public override void OnMouseUp(DiagramMouseEventArgs? args)
        {
            if (deleteObject != null)
            {
                RemoveData(deleteObject, sfDiagram);
                sfDiagram.Nodes!.Remove(deleteObject);
                _ = sfDiagram.EndUpdateAsync();
                _ = sfDiagram.DoLayoutAsync();
            }
            base.OnMouseUp(args);
            this.InAction = true;
        }
    }
    private static void RemoveData(Node node, SfDiagramComponent DiagramInstance)
    {
        if (node == null || DiagramInstance == null) return;
        if (node.OutEdges != null && node.OutEdges.Count > 0)
        {
            for (int i = node.OutEdges.Count - 1; i >= 0; i--)
            {
                var connector = DiagramInstance.GetObject(node.OutEdges[i]) as Connector;
                if (connector == null) continue;
                Node? targetNode = DiagramInstance.GetObject(connector!.TargetID!) as Node;
                if (targetNode != null)
                {
                    RemoveData(targetNode, DiagramInstance);
                    if (DiagramInstance.Nodes != null && DiagramInstance.Nodes.Contains(targetNode))
                        DiagramInstance.Nodes.Remove(targetNode);
                }
                if (DiagramInstance.Connectors != null && DiagramInstance.Connectors.Contains(connector))
                    DiagramInstance.Connectors.Remove(connector);
            }
        }
        if (node.Data is MindMapDetails details)
        {
            MindmapData?.Remove(details);
        }
    }
    private void OnSelectionChanging(SelectionChangingEventArgs args)
    {
        if (args.NewValue!.Count > 0)
        {
            if (args.NewValue[0] is Node && (args.NewValue[0] as Node)!.Data != null)
            {
                BranchType type = ((args.NewValue[0] as Node)!.Data as MindMapDetails)!.Branch;
                if (type == BranchType.Root)
                {
                    DiagramSelectionSettings!.UserHandles![0].Visible = false;
                    DiagramSelectionSettings.UserHandles[1].Visible = false;
                    DiagramSelectionSettings.UserHandles[2].Visible = true;
                    DiagramSelectionSettings.UserHandles[3].Visible = true;
                }
                else if(type==BranchType.Left||type==BranchType.SubLeft)
                {
                    DiagramSelectionSettings!.UserHandles![0].Visible = false;
                    DiagramSelectionSettings.UserHandles[1].Visible = true;
                    DiagramSelectionSettings.UserHandles[2].Visible = true;
                    DiagramSelectionSettings.UserHandles[3].Visible = false;
                }
                else if (type == BranchType.Right || type == BranchType.SubRight)
                {
                    DiagramSelectionSettings!.UserHandles![0].Visible = true;
                    DiagramSelectionSettings.UserHandles[1].Visible = false;
                    DiagramSelectionSettings.UserHandles[2].Visible = false;
                    DiagramSelectionSettings.UserHandles[3].Visible = true;
                }
            }
        }
    }
    private void NodeCreating(IDiagramObject obj)
    {
        Node? node = obj as Node;
        //node.Constraints = NodeConstraints.Default & ~NodeConstraints.Drag;
        MindMapDetails? hierarchicalData = null;
        if (node!.Data != null)
        {
            if (node.Data is System.Text.Json.JsonElement)
            {
                node.Data = System.Text.Json.JsonSerializer.Deserialize<MindMapDetails>(node.Data.ToString()!);
            }
            hierarchicalData = node.Data as MindMapDetails;
        }
        node.Height = 50;
        node.Width = 100;
        node.Shape = new BasicShape() { Type = NodeShape.Basic, Shape = NodeBasicShapes.Ellipse };
        PointPort port21 = new PointPort()
        {
            ID = "left",
            Offset = new DiagramPoint() { X = 0, Y = 0.5 },
            Height = 10,
            Width = 10,
        };
        PointPort port22 = new PointPort()
        {
            ID = "right",
            Offset = new DiagramPoint() { X = 1, Y = 0.5 },
            Height = 10,
            Width = 10,
        };
        if (node.Data != null)
        {
            node.Style!.Fill = hierarchicalData!.Fill;
            node.Style.StrokeColor = hierarchicalData.Fill;
            node.Ports = new DiagramObjectCollection<PointPort>()
            {
                port21,port22
            };
        }
        string name = "";
        if (hierarchicalData != null)
        {
            name = hierarchicalData.Label!;
        }
        else
        {
            name = "New Child";
        }
        node.Annotations = new DiagramObjectCollection<ShapeAnnotation>()
        {
            new ShapeAnnotation()
            {
                Content = name,
                Style=new TextStyle(){Color="White",FontSize = 12,FontFamily="Segoe UI"},
                Offset=new DiagramPoint(){X=0.5,Y=0.5}
            }
        };
        node.Constraints &= ~NodeConstraints.Rotate;
    }
    private void ConnectorCreating(IDiagramObject connector)
    {
        (connector as Connector)!.Type = ConnectorSegmentType.Bezier;
        (connector as Connector)!.BezierConnectorSettings = new BezierConnectorSettings() { AllowSegmentsReset = false };
        (connector as Connector)!.Constraints = ConnectorConstraints.Default & ~ConnectorConstraints.Select;
        (connector as Connector)!.Style = new ShapeStyle() { StrokeColor = "#4f4f4f", StrokeWidth = 1 };
        (connector as Connector)!.TargetDecorator = new DecoratorSettings() { Shape = DecoratorShape.None };
        (connector as Connector)!.SourceDecorator!.Shape = DecoratorShape.None;
        Node? sourceNode = DiagramInstance!.GetObject((connector as Connector)!.SourceID!) as Node;
        Node? targetNode = DiagramInstance.GetObject((connector as Connector)!.TargetID!) as Node;
        if (targetNode != null && targetNode.Data != null)
        {
            MindMapDetails nodeInfo = (targetNode.Data as MindMapDetails)!;
            if (nodeInfo.Branch == BranchType.Right || nodeInfo.Branch == BranchType.SubRight)
            {
                (connector as Connector)!.SourcePortID = sourceNode!.Ports![0].ID;
                (connector as Connector)!.TargetPortID = targetNode.Ports![1].ID;
            }
            else if (nodeInfo.Branch == BranchType.Left || nodeInfo.Branch == BranchType.SubLeft)
            {
                (connector as Connector)!.SourcePortID = sourceNode!.Ports![1].ID;
                (connector as Connector)!.TargetPortID = targetNode.Ports![0].ID;
            }
        }
    }
    private void UpdateHandle()
    {
        UserHandle deleteLeftHandle = AddHandle("DeleteRight", "delete", Direction.Right);
        UserHandle addRightHandle = AddHandle("AddLeft", "add", Direction.Left);
        UserHandle addLeftHandle = AddHandle("AddRight", "add", Direction.Right);
        UserHandle deleteRightHandle = AddHandle("DeleteLeft", "delete", Direction.Left);
        Handles.Add(deleteLeftHandle);
        Handles.Add(deleteRightHandle);
        Handles.Add(addLeftHandle);
        Handles.Add(addRightHandle);
        DiagramSelectionSettings!.UserHandles = Handles;
    }
    private UserHandle AddHandle(string name, string path, Direction direction)
    {
        UserHandle handle = new UserHandle()
        {
            Name = name,
            Visible = true,
            Offset = 0.5,
            Side = direction,
            Margin = new DiagramThickness() { Top = 0, Bottom = 0, Left = 0, Right = 0 }
        };
        if (path == "delete")
        {
            handle.PathData = "M1.0000023,3 L7.0000024,3 7.0000024,8.75 C7.0000024,9.4399996 6.4400025,10 5.7500024,10 L2.2500024,10 C1.5600024,10 1.0000023,9.4399996 1.0000023,8.75 z M2.0699998,0 L5.9300004,0 6.3420029,0.99999994 8.0000001,0.99999994 8.0000001,2 0,2 0,0.99999994 1.6580048,0.99999994 z";
        }
        else
        {
            handle.PathData = "M4.0000001,0 L6,0 6,4.0000033 10,4.0000033 10,6.0000033 6,6.0000033 6,10 4.0000001,10 4.0000001,6.0000033 0,6.0000033 0,4.0000033 4.0000001,4.0000033 z";
        }
        return handle;
    }
    public class MindMapDetails
    {
        public string? Id { get; set; }
        public string? Label { get; set; }
        public string? ParentId { get; set; }
        public BranchType Branch { get; set; }
        public string? Fill { get; set; }
    }
    public object? DataSource;
    protected override void OnInitialized()
    {
        DiagramSelectionSettings!.Constraints &= ~(SelectorConstraints.ResizeAll | SelectorConstraints.Rotate);
        MindmapData = new List<MindMapDetails>()
        {
            new MindMapDetails(){Id="1",Label="Business Planning",ParentId ="",Branch= BranchType.Root, Fill="#034d6d" },
            new MindMapDetails(){Id="3",Label= "Requirements", ParentId="1",Branch= BranchType.Right,Fill= "#1b80c6" },
            new MindMapDetails(){Id="4",Label= "Marketing", ParentId="1",Branch= BranchType.Left,Fill= "#1b80c6" },
            new MindMapDetails(){Id="5",Label= "Budgets",ParentId= "1",Branch= BranchType.Right,Fill= "#1b80c6" },
            new MindMapDetails() { Id = "10", Label="Customer Groups", ParentId= "4", Branch = BranchType.SubLeft,Fill= "#3dbfc9" },
            new MindMapDetails() { Id = "11", Label= "Branding", ParentId= "4", Branch = BranchType.SubLeft, Fill= "#3dbfc9" },
        };
        DataSource = MindmapData;
        UpdateHandle();
    }
}
```
