How to Store State in SVG Using Data Attributes

Storing application state directly within SVG elements is a practical way to manage interactive vector graphics without maintaining complex external mappings. By utilizing standard HTML5 data-* custom attributes, developers can bind metadata, status flags, and configuration values directly to SVG shapes like <path>, <circle>, and <g>. This article covers how to embed data-* attributes into SVG markup, read and modify them using JavaScript, and leverage CSS attribute selectors for state-driven rendering.

Adding data-* Attributes to SVG Markup

SVG 2 fully supports data-* attributes on all graphic and structural elements. You can declare them directly in the SVG source code just as you would on any standard HTML element.

<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
  <circle 
    id="node-1" 
    cx="50" 
    cy="50" 
    r="30" 
    data-node-id="101"
    data-status="idle"
    data-capacity="75"
  />
  <circle 
    id="node-2" 
    cx="150" 
    cy="50" 
    r="30" 
    data-node-id="102"
    data-status="active"
    data-capacity="100"
  />
</svg>

Reading and Updating State with JavaScript

You can access and update these values dynamically using the DOM dataset API or the standard getAttribute and setAttribute methods.

Using the dataset API

The dataset property converts hyphenated attribute names into camelCase properties.

const node = document.querySelector('#node-1');

// Reading state
console.log(node.dataset.nodeId);   // "101"
console.log(node.dataset.status);   // "idle"

// Updating state
node.dataset.status = "active";
node.dataset.capacity = "80";

Using getAttribute and setAttribute

If you need to maintain explicit attribute strings or support older environments, standard attribute methods work identically:

const node = document.querySelector('#node-1');

// Reading state
const currentStatus = node.getAttribute('data-status');

// Writing state
node.setAttribute('data-status', 'error');

Styling SVG Elements Based on State

Binding state directly to SVG nodes enables state-driven styling through CSS attribute selectors, removing the need to manually toggle class names or manipulate inline styles.

/* Default state */
circle[data-status="idle"] {
  fill: #94a3b8;
  stroke: #64748b;
}

/* Active state */
circle[data-status="active"] {
  fill: #22c55e;
  stroke: #16a34a;
}

/* Error state */
circle[data-status="error"] {
  fill: #ef4444;
  stroke: #dc2626;
}

Example: Interactive Toggle Node

Below is a complete implementation showing how an SVG element toggles its own state and responds to user interaction:

<svg width="100" height="100" viewBox="0 0 100 100">
  <rect 
    id="toggle-btn"
    x="10" 
    y="10" 
    width="80" 
    height="80" 
    rx="8"
    data-active="false"
  />
</svg>

<style>
  #toggle-btn {
    cursor: pointer;
    transition: fill 0.2s ease;
  }
  #toggle-btn[data-active="false"] {
    fill: #cbd5e1;
  }
  #toggle-btn[data-active="true"] {
    fill: #3b82f6;
  }
</style>

<script>
  const button = document.getElementById('toggle-btn');

  button.addEventListener('click', () => {
    const isCurrentlyActive = button.dataset.active === 'true';
    button.dataset.active = (!isCurrentlyActive).toString();
  });
</script>

Important Considerations