Skip to main content

Graph stylesheet reference

GraphLayer accepts a single stylesheet prop that describes how every node, edge, and decorator should render. A stylesheet is a declarative bundle of style layers that the GraphStyleEngine normalizes into Deck.gl accessors. Each entry focuses on what to draw, while the engine coerces values, wires update triggers, and fans the configuration out across the underlying Deck.gl primitives.

Top-level structure

import type {GraphLayerProps} from '@deck.gl-community/graph-layers';

const stylesheet: NonNullable<GraphLayerProps['stylesheet']> = {
nodes: [
{type: 'circle', radius: 10, fill: '#2563EB'},
{type: 'label', text: '@id', color: '#0F172A'}
],
edges: {
stroke: '#CBD5F5',
strokeWidth: 1.5,
decorators: [{type: 'arrow', size: 8}]
}
};

nodes is always an array—the order controls drawing order. edges can be a single style object or an array when you want to stack multiple edge passes. The legacy nodeStyle/edgeStyle props continue to work but forward to the new structure and will be removed in a future release.

Every style entry is a GraphStylesheet<TType> whose type narrows the set of supported properties:

  • Node primitives'circle', 'rectangle', 'rounded-rectangle', 'path-rounded-rectangle', 'label', 'marker', 'icon' (alias of 'marker').
  • Edge primitives'edge'.
  • Edge decorators'edge-label', 'flow', and 'arrow'.

Declarative values

Every property accepts one of the following shapes:

ShapeExampleNotes
ConstantstrokeWidth: 2Applied verbatim to every rendered object.
Attribute bindingfill: '@statusColor'Reads the named graph attribute from nodes/edges. Shortcuts described below.
Accessorstroke: edge => edge.isCritical ? '#F97316' : '#94A3B8'Receives the node or edge datum. The return value is automatically coerced into Deck.gl-friendly formats (CSS colors → [r, g, b, a]).
StatefulstrokeWidth: {default: 1, hover: 3}Maps interaction states to values. Equivalent selector blocks can be provided via ':hover' style objects.

Attribute bindings

Attribute bindings are the quickest way to connect graph data to visuals. They come in two forms:

// String shorthand: pulls `node.getPropertyValue('group')`
fill: '@group',

// Object form: add fallbacks or remap data with a scale/formatter
strokeWidth: {
attribute: 'weight',
fallback: 1,
scale: value => Math.sqrt(value)
},

// Declarative scale mapping (uses d3-scale under the hood)
color: {
attribute: 'region',
scale: {
type: 'ordinal',
domain: ['Americas', 'EMEA', 'APAC'],
range: ['#2563EB', '#10B981', '#F97316']
}
}

The attribute reads from GraphNode.getPropertyValue(attribute)/ GraphEdge.getPropertyValue(attribute) if available, falling back to the raw datum. Use fallback to supply defaults when the attribute is missing. When scale is an object the engine instantiates the specified D3 scale; pass a function if you want full control.

Selectors and interaction states

Selectors override properties for specific interaction states. Prefix a state with a colon and supply a nested style object:

const edgeStyle = {
stroke: '#CBD5F5',
strokeWidth: 1,
':hover': {
stroke: '#2563EB',
strokeWidth: 2
},
':selected': {
strokeWidth: 4
}
};

The built-in states are default, hover, dragging, and selected. When you use the map form ({default: ..., hover: ...}) the same state names apply.

Node styles

Node visuals are composed by stacking one or more style layers under the stylesheet.nodes array:

const stylesheet = {
nodes: [
{type: 'circle', radius: {attribute: 'degree', fallback: 6, scale: value => 4 + value}},
{type: 'label', text: '@id', color: '#172B4D', offset: [0, 16]}
]
};

Entries are drawn in array order (earlier entries render beneath later entries).

Shared node properties

Every node style understands these keys in addition to its type-specific properties:

PropertyTypeDefaultDescription
typestring literalSelects the primitive ('circle', 'rectangle', 'label', etc.).
data(nodes: any[]) => anynodes => nodesReplace the data object passed to Deck.gl when a sublayer needs derived data.
visiblebooleantrueToggle the sublayer without removing it from the stylesheet.
opacitynumber | accessor | attribute binding1Multiplies the alpha channel produced by the primitive.
offset[number, number] | accessor | attribute bindingnullPixel offset from the layout position. Positive Y moves up.

All other properties accept the same declarative value shapes listed above: constants, attribute bindings such as fill: '@statusColor', functions, or state maps.

Node primitives

Mix primitives to create layered nodes. For example:

const stylesheet = {
nodes: [
{
type: 'rounded-rectangle',
width: 120,
height: 48,
cornerRadius: 0.4,
fill: {
default: '#0F172A',
hover: '#1E293B'
},
stroke: '#38BDF8',
strokeWidth: {attribute: 'isSelected', scale: value => (value ? 4 : 1)}
},
{type: 'label', text: '@label', color: '#F8FAFC', fontSize: 18}
]
};

Circle node style

The circle primitive renders filled disks using Deck.gl’s ScatterplotLayer. It is ideal for compact node markers or when you want the radius to encode a numerical value. Pair it with attribute bindings to map graph properties directly onto color and size.

In addition to the shared node style options, circles understand the following keys:

PropertyTypeDefaultDescription
fillconstant | accessor | attribute bindingblack ([0, 0, 0])Fill color. Accepts CSS strings, [r, g, b]/[r, g, b, a] arrays, or bindings such as fill: '@groupColor'.
radiusconstant | accessor | attribute binding1Radius in pixels. Accessors can read node data to size circles proportionally.
strokeconstant | accessor | attribute bindingblack ([0, 0, 0])Outline color.
strokeWidthconstant | accessor | attribute binding0Outline width in pixels.

All color accessors can return either a color string or an array. Alpha values are optional—when omitted the color is treated as fully opaque.

{
type: 'circle',
radius: {attribute: 'degree', fallback: 4, scale: (value) => Math.max(4, value)},
fill: {
default: '#CBD5F5',
hover: '#3B82F6'
},
stroke: '#1E3A8A',
strokeWidth: 1.5
}

To add a subtle highlight when selecting a node you can combine selectors with accessors:

{
type: 'circle',
radius: 10,
fill: '#22C55E',
strokeWidth: {
default: 0,
selected: {attribute: 'isSelected', fallback: false, scale: (value) => (value ? 4 : 0)}
},
stroke: '#052E16'
}

Rectangle node style

Rectangles are rendered with Deck.gl’s PolygonLayer and are useful for card-like nodes or to provide a background behind other primitives. Attribute bindings let you size and color cards directly from node metadata.

Besides the shared node options, rectangles support:

PropertyTypeDefaultDescription
widthconstant | accessor | attribute binding– (required)Rectangle width in pixels.
heightconstant | accessor | attribute binding– (required)Rectangle height in pixels.
fillconstant | accessor | attribute bindingblack ([0, 0, 0])Interior color.
strokeconstant | accessor | attribute bindingblack ([0, 0, 0])Border color.
strokeWidthconstant | accessor | attribute binding0Border width in pixels.
{
type: 'rectangle',
width: {attribute: 'width', fallback: 120},
height: {attribute: 'height', fallback: 60},
fill: '#1F2937',
stroke: '#93C5FD',
strokeWidth: 1
}

You can combine selectors to animate the border as the user interacts with the node:

{
type: 'rectangle',
width: {attribute: 'padding', fallback: 0, scale: (value) => 100 + value * 2},
height: 48,
fill: {
default: '#0F172A',
hover: '#1E293B'
},
strokeWidth: {
default: 1,
selected: 4
},
stroke: '#38BDF8'
}

Rounded rectangle node style

This primitive uses a custom shader to draw rectangles with smoothly rounded corners while keeping the geometry lightweight. It is great for pill-shaped or card-style nodes that remain crisp at any zoom level, and it supports the same attribute-binding shortcuts as other node styles.

Rounded rectangles share all options from basic rectangles and add the following controls:

PropertyTypeDefaultDescription
cornerRadiusconstant | accessor | attribute binding0.1Amount of corner rounding. 0 keeps sharp corners, 1 approaches a circle. Values between 0 and 1 are typical.
radiusconstant | accessor | attribute binding1Optional radius multiplier for the underlying geometry. It can expand or shrink the shader’s falloff and exists primarily for compatibility.

Because the shape is generated in the shader it remains smooth regardless of the polygon resolution.

{
type: 'rounded-rectangle',
width: 140,
height: 56,
cornerRadius: {attribute: 'cornerRadius', fallback: 0.5},
fill: '#111827',
stroke: '#4ADE80',
strokeWidth: {
default: 1,
selected: 3
}
}

You can also adjust the radius dynamically to highlight specific groups:

{
type: 'rounded-rectangle',
width: 110,
height: 44,
cornerRadius: {attribute: 'cluster', fallback: 'default', scale: value => (value === 'core' ? 0.35 : 0.15)},
fill: '@clusterColor'
}

Path rounded rectangle node style

This variant generates the rounded rectangle geometry on the CPU and feeds it to Deck.gl’s PolygonLayer. It trades slightly higher CPU cost for compatibility with features that rely on actual polygon outlines (e.g. GPU picking or custom polygon processing) while still honoring attribute bindings for every property.

Path rounded rectangles accept the rectangle properties plus:

PropertyTypeDefaultDescription
cornerRadiusconstant | accessor | attribute binding0.1Corner rounding factor. As with the shader version, 0 is sharp and 1 is fully rounded.

The width, height, fill, stroke, and strokeWidth options behave identically to the 'rectangle' node style.

{
type: 'path-rounded-rectangle',
width: {attribute: 'children', fallback: [], scale: value => 120 + (value?.length ?? 0) * 8},
height: 48,
cornerRadius: 0.35,
fill: '#0B1120',
stroke: '#38BDF8',
strokeWidth: 1.5
}

Because the geometry is computed per node you can animate the radius as part of an interaction:

{
type: 'path-rounded-rectangle',
width: 96,
height: 40,
cornerRadius: {
default: 0.2,
hover: 0.5
},
fill: '#1E3A8A'
}

Marker node style

Markers render vector icons from the bundled marker set. Under the hood the style uses a Deck.gl IconLayer with zoom-aware sizing logic.

Marker styles extend the shared node options with the following keys:

PropertyTypeDefaultDescription
markerconstant | accessor | attribute binding'circle'Name of the marker glyph. See the list below for supported values.
sizeconstant | accessor | attribute binding12Marker size in pixels before zoom scaling.
fillconstant | accessor | attribute bindingblack ([0, 0, 0])Fill color for the glyph.
scaleWithZoomconstant | accessor | attribute bindingtrueWhen true, markers grow/shrink with the viewport zoom level. Set to false to keep a constant pixel size.

Supported marker names include:

"location-marker-filled", "bell-filled", "bookmark-filled", "bookmark", "cd-filled",
"cd", "checkmark", "circle-check-filled", "circle-check", "circle-filled", "circle-i-filled",
"circle-i", "circle-minus-filled", "circle-minus", "circle-plus-filled", "circle-plus",
"circle-questionmark-filled", "circle-questionmark", "circle-slash-filled", "circle-slash",
"circle-x-filled", "circle-x", "circle", "diamond-filled", "diamond", "flag-filled",
"flag", "gear", "heart-filled", "heart", "bell", "location-marker", "octagonal-star-filled",
"octagonal-star", "person-filled", "person", "pin-filled", "pin", "plus-small", "plus",
"rectangle-filled", "rectangle", "star-filled", "star", "tag-filled", "tag", "thumb-down-filled",
"thumb-down", "thumb-up", "thumb_up-filled", "triangle-down-filled", "triangle-down",
"triangle-left-filled", "triangle-left", "triangle-right-filled", "triangle-right",
"triangle-up-filled", "triangle-up", "x-small", "x"
{
type: 'marker',
marker: {attribute: 'status', fallback: 'online', scale: value => (value === 'offline' ? 'triangle-down-filled' : 'circle-filled')},
size: 18,
fill: {
default: '#6B7280',
hover: '#F59E0B'
},
scaleWithZoom: false
}

Use selectors to show different icons for interaction states:

{
type: 'marker',
marker: {
default: '@defaultIcon',
selected: 'star-filled'
},
size: 16,
fill: '#FBBF24'
}

Label node style

Labels are rendered with Deck.gl’s TextLayer and are typically combined with a background primitive (circle, rectangle, etc.).

Alongside the shared node options, labels support the following keys:

PropertyTypeDefaultDescription
textconstant | accessor | attribute binding– (required)Text to display. Attribute strings such as text: '@label' read from node properties.
colorconstant | accessor | attribute bindingblack ([0, 0, 0])Font color.
fontSizeconstant | accessor | attribute binding12Font size in pixels.
textAnchorconstant | accessor | attribute binding'middle'Horizontal alignment: 'start', 'middle', or 'end'.
alignmentBaselineconstant | accessor | attribute binding'center'Vertical alignment: 'top', 'center', or 'bottom'.
angleconstant | accessor | attribute binding0Clockwise rotation in degrees.
textMaxWidthconstant | accessor | attribute binding-1Maximum width in pixels before wrapping. -1 disables wrapping.
textWordBreakconstant | accessor | attribute binding'break-all'Word-breaking mode passed to TextLayer ('break-all', 'break-word', etc.).
textSizeMinPixelsconstant | accessor | attribute binding9Minimum size the text is allowed to shrink to.
scaleWithZoomconstant | accessor | attribute bindingtrueWhether the font scales with zoom. Set to false to keep screen-space size.
{
type: 'label',
text: '@label',
color: '#E2E8F0',
fontSize: 16,
offset: [0, 18],
alignmentBaseline: 'top'
}

Using selectors you can provide contextual hints:

{
type: 'label',
text: '@label',
color: {
default: '#64748B',
hover: '#F8FAFC'
},
textMaxWidth: 160,
textWordBreak: 'break-word',
':selected': {
fontSize: 20,
scaleWithZoom: false
}
}

Edge styles

Edges are styled via the stylesheet.edges prop on GraphLayer. The legacy edgeStyle prop forwards to this shape but will be removed in a future release. Similar to nodes, a GraphStylesheet definition is normalized by the GraphStyleEngine, which resolves colors, attribute bindings, and interaction states before feeding them into Deck.gl’s LineLayer.

const stylesheet = {
edges: {
stroke: {attribute: 'isCritical', fallback: false, scale: value => (value ? '#F97316' : '#94A3B8')},
strokeWidth: {
default: 1,
hover: 3,
selected: {attribute: 'weight', fallback: 2, scale: value => Math.min(6, 2 + value)}
},
decorators: [
{type: 'edge-label', text: '@id', color: '#000', fontSize: 18},
{type: 'arrow', color: '#222', size: 8, offset: [4, 0]}
]
}
};

Shared edge properties

PropertyTypeDefaultDescription
strokeconstant | accessor | attribute bindingblack ([0, 0, 0])Line color. Accepts CSS strings, [r, g, b, a] arrays, or bindings such as stroke: '@color'.
strokeWidthconstant | accessor | attribute binding0Line width in pixels.
data(edges: any[]) => anyedges => edgesOverride the data object passed to the Deck.gl layer.
visiblebooleantrueToggle the entire edge layer on/off.
decoratorsArray[]Additional visual adornments such as labels or animated flow indicators.

Edge styles honor the same selectors as node styles (:hover, :dragging, and :selected). Selector blocks can override any property, including decorators.

Edge decorators

Decorators add auxiliary visuals that travel along the edge path. Each decorator is an object with a type field and accepts the same declarative value shapes as nodes and edges.

Edge label decorator

Adds text anchored near the edge’s midpoint. Internally this uses the same ZoomableTextLayer as node labels, so the available options are similar.

PropertyTypeDefaultDescription
textconstant | accessor | attribute binding– (required)Label content. Attribute strings such as text: '@weight' pull from edge properties.
colorconstant | accessor | attribute bindingblack ([0, 0, 0])Font color.
fontSizeconstant | accessor | attribute binding12Font size in pixels.
textAnchorconstant | accessor | attribute binding'middle'Horizontal alignment relative to the computed position.
alignmentBaselineconstant | accessor | attribute binding'center'Vertical alignment.
angleconstant | accessor | attribute bindingAutomaticRotation in degrees. Defaults to the edge direction; override to lock the angle.
textMaxWidthconstant | accessor | attribute binding-1Maximum width before wrapping. -1 disables wrapping.
textWordBreakconstant | accessor | attribute binding'break-all'Word-breaking mode ('break-word', 'break-all', etc.).
textSizeMinPixelsconstant | accessor | attribute binding9Minimum rendered size for zooming out.
scaleWithZoomconstant | accessor | attribute bindingtrueWhether the label scales with the viewport zoom level.
offsetconstant | accessor | attribute bindingnullAdditional pixel offset from the centroid-derived anchor position.

All properties support selectors (:hover, :selected, …) and accessors, just like the base edge style.

{
type: 'edge-label',
text: {attribute: 'weight', scale: value => `${value} ms`},
color: {
default: '#1F2937',
hover: '#111827'
},
textAnchor: 'start',
offset: [8, 0]
}

To keep labels readable while zooming, disable scaling at small sizes:

{
type: 'edge-label',
text: '@label',
scaleWithZoom: {
default: true,
dragging: false
},
textSizeMinPixels: 12
}

Flow decorator

The flow decorator draws animated segments moving along the edge direction. It is useful to express throughput or directional emphasis.

PropertyTypeDefaultDescription
colorconstant | accessor | attribute bindingblack ([0, 0, 0])Color of the animated segment.
speedconstant | accessor | attribute binding0Segments per second that travel along the edge. Positive values flow from source to target.
widthconstant | accessor | attribute binding1Visual width of the segment in pixels.
tailLengthconstant | accessor | attribute binding1Length of the fading trail behind each segment.

All fields support accessors and selectors. A speed of 0 disables the motion while still rendering a static highlight.

{
type: 'flow',
color: '#22D3EE',
width: 2,
speed: {attribute: 'loadFactor', fallback: 0},
tailLength: 4
}

To create directional emphasis only while hovering:

{
type: 'flow',
color: '#FACC15',
width: 3,
speed: {
default: 0,
hover: 2
}
}

Arrow decorator

Renders arrowheads for directed edges. Supports color, size, and offset. The offset accessor accepts [along, perpendicular] distances in layer units, where along shifts the arrow away from the target node and perpendicular offsets it orthogonally from the edge.

Decorators are processed by the stylesheet engine, so they can use selectors and accessors just like the main edge style.

Composing stylesheets

Supply the stylesheet via the stylesheet prop when constructing GraphLayer (or React’s <GraphLayer />). The layer merges the declaration with sensible defaults and feeds the result into the renderer:

new GraphLayer({
stylesheet: {
nodes: [
{type: 'circle', radius: 12, fill: '@groupColor'},
{type: 'label', text: '@id', color: '#0F172A'}
],
edges: {
stroke: {
attribute: 'weight',
scale: {type: 'linear', domain: [0, 10], range: ['#CBD5F5', '#1E3A8A']}
},
strokeWidth: {attribute: 'weight', fallback: 1, scale: value => 0.5 + value * 0.2},
decorators: [{type: 'edge-label', text: '@weight'}]
}
}
});

Edge styles may omit the type field—GraphLayer defaults it to 'edge'—but supplying it enables TypeScript to infer the correct decorator and property options. Decorators are simply additional stylesheet entries inside the decorators array and support the same value shapes and selectors as nodes and edges.