diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkLayer.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkLayer.tsx
index 81f943b883c908aadc27263e22273821529e041d..64a608dc6a1c468ad7aca0e7aad20c0ef77c7b9b 100644
--- a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkLayer.tsx
+++ b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkLayer.tsx
@@ -32,7 +32,7 @@ export class NodeLinkLayer extends CompositeLayer<CompositeLayerType> {
       this._layers[layerId] = new LineLayer({
         id: layerId,
         data: edgeData,
-        visible: !layerSettings.edges[label].hidden,
+        visible: !layerSettings?.edges[label]?.hidden,
         pickable: true,
         getWidth: layerSettings.edges[label].width,
         getSourcePosition: (d) => getNodeLocation(d.from),
@@ -76,26 +76,34 @@ export class NodeLinkLayer extends CompositeLayer<CompositeLayerType> {
       });
     });
 
-    const textLayerId = 'label-target';
+    if (layerSettings?.showLabels) {
+      const textLayerId = 'label-target';
 
-    this._layers[textLayerId] = new TextLayer({
-      id: textLayerId,
-      data: data.nodes,
-      getPosition: (d: any) => getNodeLocation(d._id),
-      getText: (d: any) => d.id,
-      getSize: 15,
-      visible: true,
-      getAlignmentBaseline: 'top',
-      background: true,
-      getPixelOffset: [10, 10],
-      extensions: [collisionFilter],
-      collisionEnabled: true,
-      getCollisionPriority: (d: any) => d.id,
-      collisionTestProps: { sizeScale: 10 },
-      getRadius: 10,
-      radiusUnits: 'pixels',
-      collisionGroup: 'text',
-    });
+      this._layers[textLayerId] = new TextLayer({
+        id: textLayerId,
+        data: data.nodes,
+        getPosition: (d) => getNodeLocation(d._id),
+        getText: (d) => `${d.attributes[layerSettings?.labelAttribute] ?? d._id}`,
+        getSize: 20,
+        visible: true,
+        getAlignmentBaseline: 'top',
+        background: true,
+        getRadius: 10,
+        radiusUnits: 'pixels',
+        characterSet: 'auto',
+        fontFamily: 'monospace',
+        billboard: false,
+        getAngle: () => 0,
+        collisionGroup: 'textLabels',
+        extensions: [collisionFilter],
+        collisionEnabled: layerSettings?.collisionEnabled ?? true,
+        getCollisionPriority: () => 100,
+        collisionTestProps: { sizeScale: layerSettings?.labelSpace ?? 5 },
+        updateTriggers: {
+          getText: [layerSettings.labelAttribute],
+        },
+      });
+    }
 
     return Object.values(this._layers);
   }
diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx
index 306774b916bb3d26875f68e5f42164826024ee72..cf1f304dea7a7eab1f3609621d4fafb1e8473dfc 100644
--- a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx
+++ b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect } from 'react';
+import React, { useEffect, useMemo } from 'react';
 import ColorPicker from '@graphpolaris/shared/lib/components/colorComponents/colorPicker';
 import { Button, DropdownColorLegend, EntityPill, Icon, Input, RelationPill } from '@graphpolaris/shared/lib/components';
 import { MapProps } from '../../mapvis';
@@ -16,7 +16,15 @@ export function NodeLinkOptions({
 
   useEffect(() => {
     if (!layerSettings) {
-      const initialSettingsObject = { enableBrushing: false, nodes: {}, edges: {} };
+      const initialSettingsObject = {
+        showLabels: true,
+        collisionEnabled: true,
+        labelSpace: 5,
+        layerSettings: '_id',
+        enableBrushing: false,
+        nodes: {},
+        edges: {},
+      };
 
       graphMetadata.nodes.labels.forEach((node) => {
         initialSettingsObject.nodes = {
@@ -50,6 +58,19 @@ export function NodeLinkOptions({
     }
   }, [graphMetadata, settings, updateLayerSettings]);
 
+  const commonNodeAttributes = useMemo(() => {
+    const nodeTypes = Object.keys(graphMetadata.nodes.types);
+    if (nodeTypes.length === 0) return [];
+
+    return nodeTypes.reduce(
+      (commonAttrs, nodeType) => {
+        const attributes = Object.keys(graphMetadata.nodes.types[nodeType]?.attributes || {});
+        return commonAttrs.filter((attr) => attributes.includes(attr));
+      },
+      Object.keys(graphMetadata.nodes.types[nodeTypes[0]].attributes || {}),
+    );
+  }, [graphMetadata.nodes.types]);
+
   const handleCollapseToggle = (type: string, itemType: 'nodes' | 'edges') => {
     if (layerSettings) {
       updateLayerSettings({
@@ -67,6 +88,41 @@ export function NodeLinkOptions({
   return (
     layerSettings && (
       <div>
+        <Input
+          label="Show labels"
+          type="boolean"
+          value={layerSettings?.showLabels}
+          onChange={(val) => updateLayerSettings({ showLabels: val as boolean })}
+        />
+
+        <Input
+          inline
+          label="Label text"
+          type="dropdown"
+          value={layerSettings?.labelAttribute}
+          options={[...commonNodeAttributes]}
+          disabled={!layerSettings?.showLabels}
+          onChange={(val) => updateLayerSettings({ labelAttribute: val as string })}
+        />
+
+        <Input
+          label="Prevent collision"
+          type="boolean"
+          value={layerSettings?.collisionEnabled}
+          onChange={(val) => updateLayerSettings({ collisionEnabled: val as boolean })}
+        />
+        {layerSettings?.collisionEnabled && (
+          <Input
+            label="Label space"
+            type="slider"
+            min={0}
+            max={10}
+            step={0.5}
+            value={layerSettings?.labelSpace}
+            onChange={(val) => updateLayerSettings({ labelSpace: val })}
+          />
+        )}
+
         {graphMetadata.nodes.labels.map((nodeType) => {
           const nodeSettings = layerSettings?.nodes?.[nodeType] || {};