diff --git a/src/common/graph/graph.ts b/src/common/graph/graph.ts
index 13d135dddf473cfc921ce5c53790a03c7f95eedb..f0c61c6007d2fee2a738b68398fd9ff56ea178cf 100644
--- a/src/common/graph/graph.ts
+++ b/src/common/graph/graph.ts
@@ -47,9 +47,9 @@ export class Graph
     private idToNode: Map<number, Node>;
     private idToLink: Map<number, Link>;
 
-    private nextNodeId = 0;
-    private nextLinkId = 0;
-    private nextObjectGroupId = 0;
+    protected nextNodeId = 0;
+    protected nextLinkId = 0;
+    protected nextObjectGroupId = 0;
 
     /**
      * Creates a new Graph object.
@@ -184,6 +184,14 @@ export class Graph
         return this.idToNode.get(id);
     }
 
+    public nodeType(id: number): NodeType {
+        return this.idToObjectGroup.get(id);
+    }
+
+    public link(id: number): Link {
+        return this.idToLink.get(id);
+    }
+
     private addNode(node: Node) {
         if (this.idToNode.has(node.id)) {
             node.id = ++this.nextNodeId;
@@ -283,12 +291,16 @@ export class Graph
         }
 
         const nodeType = this.idToObjectGroup.get(id);
+        this.idToObjectGroup.delete(id);
 
         for (const node of this.nodes) {
             if (node.type.id === nodeType.id) {
                 node.type = this.objectGroups[0];
             }
         }
+        this.objectGroups = this.objectGroups.filter(
+            (group) => group.id !== id
+        );
         return true;
     }
 
diff --git a/src/editor/components/nodedetails.tsx b/src/editor/components/nodedetails.tsx
index 70083d95ba7b05f683f5f82a1540fc367153861d..6e29bd45fab8228b6356e63a6f2afa522e42c148 100644
--- a/src/editor/components/nodedetails.tsx
+++ b/src/editor/components/nodedetails.tsx
@@ -59,6 +59,7 @@ function NodeDetails({
         Object.assign(referenceData, { [key]: value });
 
         onNodeDataChange(
+            // Generate changes for individual nodes if multiple nodes are selected
             selectedNodes.map((node) => {
                 const update = Object.fromEntries(
                     Object.entries(referenceData).filter(
diff --git a/src/editor/components/nodetypeentry.tsx b/src/editor/components/nodetypeentry.tsx
index ca8b6f7ff1083cde70ea507ef54eb3574f7d7115..93fb425e487613f6ced40cd7005fee1457e638cb 100644
--- a/src/editor/components/nodetypeentry.tsx
+++ b/src/editor/components/nodetypeentry.tsx
@@ -1,155 +1,89 @@
-import React from "react";
-import { ReactNode } from "react";
-import { NodeType } from "../../common/graph/nodetype";
+import React, { useState } from "react";
+import { NodeType, NodeTypeData } from "../../common/graph/nodetype";
 import "./nodetypeentry.css";
-import { DynamicGraph } from "../graph";
 
-type propTypes = {
-    graph: DynamicGraph;
-    type: NodeType;
-    onChange: { (): void };
-    onSelectAll: (type: NodeType) => void;
+type NodeTypeEntryProps = {
+    nodeType: NodeType;
+    onNodeTypeDelete: (id: number[]) => void;
+    onNodeTypeDataChange: (
+        requests: NodeTypeData[],
+        createCheckpoint?: boolean
+    ) => void;
+    onNodeTypeSelect: (type: NodeType) => void;
+    enableDelete: boolean;
 };
-type stateTypes = {
-    temporaryColor: string;
-};
-
-export class NodeTypeEntry extends React.Component<propTypes, stateTypes> {
-    debounceTimeout: NodeJS.Timeout;
-    debounceArgs: any[];
-    debounceFunc: any;
-
-    constructor(props: propTypes) {
-        super(props);
-        this.deleteType = this.deleteType.bind(this);
-        this.handleTextChange = this.handleTextChange.bind(this);
-        this.handleColorChange = this.handleColorChange.bind(this);
-
-        this.state = {
-            temporaryColor: undefined,
-        };
-    }
 
-    private deleteType() {
-        //this.props.type.delete();
-    }
+function NodeTypeEntry({
+    nodeType,
+    onNodeTypeDataChange,
+    onNodeTypeDelete,
+    onNodeTypeSelect,
+    enableDelete,
+}: NodeTypeEntryProps) {
+    const [tmpColor, setTmpColor] = useState<string>(undefined);
 
-    private isValidColor(color: string): boolean {
+    const isValidColor = (color: string) => {
         if (color.length <= 0) {
             return false;
         }
 
         // Source: https://stackoverflow.com/a/8027444
         return /^#([0-9A-F]{3}){1,2}$/i.test(color);
-    }
-
-    private handleColorChange(event: any) {
-        const newColor = event.target.value;
-
-        if (this.isValidColor(newColor)) {
-            // Is actual change?
-            if (this.props.type.color !== newColor) {
-                // Update proper color
-                this.props.type.color = newColor;
-                // this.props.type.graph.storeCurrentData( // TODO: Reimplement
-                //     "Changed color of type [" + this.props.type + "]"
-                // );
-            }
-
-            // Reset temporary value
-            this.setState({
-                temporaryColor: undefined,
-            });
+    };
+
+    const referenceData: NodeTypeData = {
+        id: nodeType.id,
+        color: nodeType.color,
+        name: nodeType.name,
+    };
+
+    const handleDataChange = function <ValueType>(
+        key: keyof NodeTypeData,
+        value: ValueType
+    ) {
+        // if (!changed) { TODO: Move to nodetypeseditor
+        //     setChanged(true);
+        // }
+
+        Object.assign(referenceData, { [key]: value });
+        onNodeTypeDataChange([referenceData], false);
+    };
+
+    const handleColorChange = (color: string) => {
+        if (isValidColor(color)) {
+            handleDataChange("color", color);
+            setTmpColor(undefined);
         } else {
-            // Only set as temporary value
-            this.setState({
-                temporaryColor: newColor,
-            });
-        }
-
-        this.props.onChange();
-    }
-
-    /**
-     * Generic function for handeling a changing text input and applying the new value to the node type.
-     * @param event Change event of text input.
-     * @param property Property to give new value.
-     */
-    private handleTextChange(event: any, property: string) {
-        const newValue = event.target.value;
-
-        // Actual change?
-        if ((this.props.type as any)[property] == newValue) {
-            return;
+            setTmpColor(color);
         }
-
-        (this.props.type as any)[property] = newValue;
-        this.props.onChange();
-
-        // Save change, but debounce, so it doesn't trigger too quickly
-        this.props.onChange();
-        // this.debounce(
-        //     (property: string) => {
-        //         // this.props.type.graph.storeCurrentData( // TODO: Reimplement
-        //         //     "Changed " + property + " of type [" + this.props.type + "]"
-        //         // );
-        //         this.props.onChange();
-        //     },
-        //     500,
-        //     property
-        // );
-    }
-
-    // debounce(func: any, wait: number, ...args: any[]) {
-    //     // It works, don't question it
-    //     const later = () => {
-    //         this.debounceTimeout = null;
-    //         this.debounceFunc(...this.debounceArgs);
-    //     };
-    //
-    //     clearTimeout(this.debounceTimeout);
-    //     if (
-    //         this.debounceArgs !== undefined &&
-    //         args[0] !== this.debounceArgs[0] &&
-    //         this.debounceFunc !== undefined
-    //     ) {
-    //         this.debounceFunc(...this.debounceArgs);
-    //     }
-    //
-    //     this.debounceArgs = args;
-    //     this.debounceFunc = func;
-    //     this.debounceTimeout = setTimeout(later, wait);
-    // }
-
-    render(): ReactNode {
-        return (
-            <li className="node-type">
-                <input
-                    className="node-type-name"
-                    type={"text"}
-                    value={this.props.type.name}
-                    onChange={(event) => this.handleTextChange(event, "name")}
-                />
-                <input
-                    className="node-type-color"
-                    type={"text"}
-                    value={
-                        this.state.temporaryColor !== undefined
-                            ? this.state.temporaryColor
-                            : this.props.type.color
-                    }
-                    onChange={(event) => this.handleColorChange(event)}
-                />
-                <button onClick={() => this.props.onSelectAll(this.props.type)}>
-                    Select nodes
+    };
+
+    return (
+        <li className="node-type">
+            <input
+                className="node-type-name"
+                type={"text"}
+                value={nodeType.name}
+                onChange={(event) =>
+                    handleDataChange("name", event.target.value)
+                }
+            />
+            <input
+                className="node-type-color"
+                type={"text"}
+                value={tmpColor !== undefined ? tmpColor : nodeType.color}
+                onChange={(event) => handleColorChange(event.target.value)}
+            />
+            <button onClick={() => onNodeTypeSelect(nodeType)}>
+                Select nodes
+            </button>
+            {enableDelete && (
+                <button onClick={() => onNodeTypeDelete([nodeType.id])}>
+                    Delete
                 </button>
-                {this.props.graph &&
-                this.props.graph.objectGroups.length > 1 ? (
-                    <button onClick={this.deleteType}>Delete</button>
-                ) : (
-                    ""
-                )}
-            </li>
-        );
-    }
+            )}
+        </li>
+    );
 }
+
+export default NodeTypeEntry;
diff --git a/src/editor/components/nodetypeseditor.tsx b/src/editor/components/nodetypeseditor.tsx
index 65321bf91042380a35875a37aa60b08155ba9e62..5eca976e51f2557e54f76fd551458b58f9706458 100644
--- a/src/editor/components/nodetypeseditor.tsx
+++ b/src/editor/components/nodetypeseditor.tsx
@@ -1,47 +1,44 @@
 import React from "react";
-import { ReactNode } from "react";
 import "./nodetypeseditor.css";
-import { NodeTypeEntry } from "./nodetypeentry";
-import { NodeType } from "../../common/graph/nodetype";
-import { DynamicGraph } from "../graph";
+import NodeTypeEntry from "./nodetypeentry";
+import { NodeType, NodeTypeData } from "../../common/graph/nodetype";
 
-type propTypes = {
-    graph: DynamicGraph;
-    onChange: { (): void };
-    onSelectAll: (type: NodeType) => void;
+type NodeTypesEditorProps = {
+    nodeTypes: NodeType[];
+    onNodeTypeSelect: (type: NodeType) => void;
+    onNodeTypeCreation: () => NodeType;
+    onNodeTypeDelete: (id: number[]) => void;
+    onNodeTypeDataChange: (
+        requests: NodeTypeData[],
+        createCheckpoint?: boolean
+    ) => void;
 };
 
-export class NodeTypesEditor extends React.Component<propTypes> {
-    constructor(props: propTypes) {
-        super(props);
-        this.addType = this.addType.bind(this);
-    }
-
-    private addType() {
-        this.props.graph.createObjectGroup();
-    }
-
-    render(): ReactNode {
-        if (this.props.graph === undefined) {
-            return "No graph selected.";
-        }
-
-        return (
-            <div id="node-types-editor">
-                <h3>Node types</h3>
-                <ul>
-                    {this.props.graph.objectGroups.map((type) => (
-                        <NodeTypeEntry
-                            onChange={this.props.onChange}
-                            key={type.id}
-                            type={type}
-                            graph={this.props.graph}
-                            onSelectAll={this.props.onSelectAll}
-                        />
-                    ))}
-                </ul>
-                <button onClick={this.addType}>Add type</button>
-            </div>
-        );
-    }
+function NodeTypesEditor({
+    nodeTypes,
+    onNodeTypeSelect,
+    onNodeTypeCreation,
+    onNodeTypeDelete,
+    onNodeTypeDataChange,
+}: NodeTypesEditorProps) {
+    return (
+        <div id="node-types-editor">
+            <h3>Node types</h3>
+            <ul>
+                {nodeTypes.map((nodeType) => (
+                    <NodeTypeEntry
+                        key={nodeType.id}
+                        nodeType={nodeType}
+                        onNodeTypeSelect={onNodeTypeSelect}
+                        onNodeTypeDelete={onNodeTypeDelete}
+                        onNodeTypeDataChange={onNodeTypeDataChange}
+                        enableDelete={nodeTypes.length > 1}
+                    />
+                ))}
+            </ul>
+            <button onClick={onNodeTypeCreation}>Add type</button>
+        </div>
+    );
 }
+
+export default NodeTypesEditor;
diff --git a/src/editor/components/sidepanel.tsx b/src/editor/components/sidepanel.tsx
index 4022b64294af79628b2cfecbe9f38ed100d99af2..16e878650a69dd6663743c050b96f2512b63aee1 100644
--- a/src/editor/components/sidepanel.tsx
+++ b/src/editor/components/sidepanel.tsx
@@ -4,11 +4,11 @@ import "./sidepanel.css";
 import HistoryNavigator from "./historynavigator";
 import { DynamicGraph } from "../graph";
 import NodeDetails from "./nodedetails";
-import { NodeTypesEditor } from "./nodetypeseditor";
+import NodeTypesEditor from "./nodetypeseditor";
 import Settings from "./settings";
 import Instructions from "./instructions";
 import { Node } from "../../common/graph/node";
-import { NodeType } from "../../common/graph/nodetype";
+import { NodeType, NodeTypeData } from "../../common/graph/nodetype";
 import { EditorSettings, NodeDataChangeRequest } from "../editor";
 
 interface SidepanelProps {
@@ -17,6 +17,12 @@ interface SidepanelProps {
     onRedo: () => void;
     onUndo: () => void;
     onNodeTypeSelect: (type: NodeType) => void;
+    onNodeTypeCreation: () => NodeType;
+    onNodeTypeDelete: (id: number[]) => void;
+    onNodeTypeDataChange: (
+        requests: NodeTypeData[],
+        createCheckpoint?: boolean
+    ) => void;
     onSettingsChange: (settings: EditorSettings) => void;
     onNodeDataChange: (
         requests: NodeDataChangeRequest[],
@@ -34,6 +40,9 @@ function Sidepanel({
     onUndo,
     onRedo,
     onNodeTypeSelect,
+    onNodeTypeCreation,
+    onNodeTypeDelete,
+    onNodeTypeDataChange,
     onSettingsChange,
     onNodeDataChange,
     onSave,
@@ -61,11 +70,11 @@ function Sidepanel({
             />
             <hr />
             <NodeTypesEditor
-                onChange={() =>
-                    console.log("Refactor onChange for nodetypes editor!")
-                } // TODO: Refactor
-                graph={graph}
-                onSelectAll={onNodeTypeSelect}
+                nodeTypes={graph.objectGroups}
+                onNodeTypeSelect={onNodeTypeSelect}
+                onNodeTypeCreation={onNodeTypeCreation}
+                onNodeTypeDelete={onNodeTypeDelete}
+                onNodeTypeDataChange={onNodeTypeDataChange}
             />
             <hr />
             <Settings settings={settings} onSettingsChange={onSettingsChange} />
diff --git a/src/editor/editor.tsx b/src/editor/editor.tsx
index 556bfe9de11b014636e6c9a68f883eba8177c846..2e3a9eec40753f8e545b63c6043fec11a68b7131 100644
--- a/src/editor/editor.tsx
+++ b/src/editor/editor.tsx
@@ -13,7 +13,7 @@ import { Node, NodeProperties } from "../common/graph/node";
 import { SpaceManager } from "./components/spacemanager";
 import SelectLayer from "./components/selectlayer";
 import { Coordinate2D, GraphData, SimGraphData } from "../common/graph/graph";
-import { NodeType } from "../common/graph/nodetype";
+import { NodeType, NodeTypeData } from "../common/graph/nodetype";
 import { GraphRenderer2D } from "./renderer";
 import * as Config from "../config";
 import Sidepanel from "./components/sidepanel";
@@ -80,6 +80,10 @@ export class Editor extends React.PureComponent<any, stateTypes> {
         this.saveSpace = this.saveSpace.bind(this);
         this.forceUpdate = this.forceUpdate.bind(this);
         this.handleNodeTypeSelect = this.handleNodeTypeSelect.bind(this);
+        this.handleNodeTypeDataChange =
+            this.handleNodeTypeDataChange.bind(this);
+        this.handleNodeTypeDeletion = this.handleNodeTypeDeletion.bind(this);
+        this.handleNodeTypeCreation = this.handleNodeTypeCreation.bind(this);
         this.handleBoxSelect = this.handleBoxSelect.bind(this);
         this.selectNodes = this.selectNodes.bind(this);
         this.handleNodeDataChange = this.handleNodeDataChange.bind(this);
@@ -259,6 +263,34 @@ export class Editor extends React.PureComponent<any, stateTypes> {
         this.setState({ graph: graph });
     }
 
+    private handleNodeTypeDataChange(
+        nodeTypeData: NodeTypeData[],
+        createCheckpoint = true
+    ) {
+        if (nodeTypeData.length == 0) {
+            return;
+        }
+
+        // Create a shallow copy of the graph object to trigger an update over setState
+        const graph = Object.assign(new DynamicGraph(), this.state.graph);
+
+        // Modify node type
+        for (const request of nodeTypeData) {
+            const node = graph.nodeType(request.id);
+            Object.assign(node, request);
+        }
+
+        // Create checkpoint
+        if (createCheckpoint) {
+            graph.createCheckpoint(
+                `Modified ${nodeTypeData.length} node(s) data.`
+            );
+        }
+
+        // Push shallow copy to state
+        this.setState({ graph: graph });
+    }
+
     private handleNodeCreation(
         position?: Coordinate2D,
         createCheckpoint = true
@@ -326,6 +358,29 @@ export class Editor extends React.PureComponent<any, stateTypes> {
         this.setState({ graph: graph });
     }
 
+    private handleNodeTypeCreation(createCheckpoint = true): NodeType {
+        const graph = Object.assign(new DynamicGraph(), this.state.graph);
+        const nodeType = graph.createObjectGroup();
+
+        if (createCheckpoint) {
+            graph.createCheckpoint("Created new node type.");
+        }
+
+        this.setState({ graph: graph });
+        return nodeType;
+    }
+
+    private handleNodeTypeDeletion(ids: number[], createCheckpoint = true) {
+        const graph = Object.assign(new DynamicGraph(), this.state.graph);
+        ids.forEach((id) => graph.deleteNodeType(id));
+
+        if (createCheckpoint) {
+            graph.createCheckpoint(`Deleted ${ids.length} node type(s).`);
+        }
+
+        this.setState({ graph: graph });
+    }
+
     private loadGraphFromCheckpoint(checkpoint: Checkpoint<SimGraphData>) {
         const graph = new DynamicGraph();
         graph.fromSerializedObject(checkpoint.data);
@@ -425,6 +480,9 @@ export class Editor extends React.PureComponent<any, stateTypes> {
                             onUndo={this.handleUndo}
                             onRedo={this.handleRedo}
                             onNodeTypeSelect={this.handleNodeTypeSelect}
+                            onNodeTypeCreation={this.handleNodeTypeCreation}
+                            onNodeTypeDelete={this.handleNodeTypeDeletion}
+                            onNodeTypeDataChange={this.handleNodeTypeDataChange}
                             onSettingsChange={(settings) =>
                                 this.setState({ settings: settings })
                             }
diff --git a/src/editor/graph.ts b/src/editor/graph.ts
index 5d71e9f217b1ab394fd09de3894a578eef0e96f9..0c87a85b49f99bc8780473a85315fa22c3c195f1 100644
--- a/src/editor/graph.ts
+++ b/src/editor/graph.ts
@@ -42,7 +42,7 @@ export class DynamicGraph extends Common.Graph {
     public createObjectGroup(data?: NodeTypeData): NodeType {
         if (data == undefined) {
             data = {
-                id: 0,
+                id: ++this.nextObjectGroupId,
                 name: "Unnamed",
                 color: "#000000",
             };