HTMLData Attributes (data-*)

Data Attributes (data-*)

Custom data attributes let you attach your own private data to any HTML element without inventing non-standard attributes or hijacking existing ones. Any attribute named data-something is guaranteed valid HTML and is exposed to JavaScript through the element's dataset property.

Basic Syntax

data-attributes.html

HTML
<article
  class="product-card"
  data-id="4821"
  data-category="electronics"
  data-in-stock="true"
>
  <h3>Wireless Mouse</h3>
</article>
  • The prefix must be exactly data- (lowercase).

  • Everything after the prefix can be any lowercase name, using hyphens to separate words.

  • Values are always strings — there is no built-in type conversion for numbers or booleans.

Reading Data Attributes with dataset

In JavaScript, every data-* attribute on an element is readable and writable through element.dataset, using a camelCase version of the attribute name (hyphens are removed and the next letter is capitalized).

reading-dataset.js

JS
const card = document.querySelector('.product-card')

console.log(card.dataset.id)        // "4821"
console.log(card.dataset.category)  // "electronics"
console.log(card.dataset.inStock)   // "true" (still a string!)

// Writing works the same way, and updates the attribute in the DOM
card.dataset.inStock = 'false'
// <article ... data-in-stock="false"> in the live DOM
"4821"
"electronics"
"true"
Naming maps in both directions
data-in-stock in HTML becomes dataset.inStock in JavaScript, and assigning dataset.inStock = "false" updates the data-in-stock attribute back in the DOM. The mapping is always hyphen-case in HTML, camelCase in JavaScript.
Common Use Cases
  • Storing a database or API identifier on a rendered element (data-id="4821") so click handlers know which record to act on.

  • Storing lightweight UI state flags (data-open="true", data-selected="false") that CSS can also target via attribute selectors.

  • Passing simple configuration values from server-rendered HTML to a small client-side script (data-refresh-interval="30").

  • Marking elements as JS "hooks" (data-js="toggle-menu") separate from styling classes, so a redesign that changes CSS classes does not break behavior.

click-handler-example.html

HTML
<ul id="cart">
  <li data-id="101" data-price="19.99">T-Shirt <button data-action="remove">Remove</button></li>
  <li data-id="102" data-price="34.50">Mug <button data-action="remove">Remove</button></li>
</ul>

<script>
  document.getElementById('cart').addEventListener('click', (event) => {
    if (event.target.dataset.action === 'remove') {
      const item = event.target.closest('li')
      console.log('Removing item', item.dataset.id, 'priced at', item.dataset.price)
      item.remove()
    }
  })
</script>
Styling with Data Attributes

CSS can select on data attributes directly, which is a common way to drive visual state without toggling class names for every possible value.

data-attribute-css.html

HTML
<style>
  .badge[data-status="active"] { background: #2e7d32; color: white; }
  .badge[data-status="paused"] { background: #ed6c02; color: white; }
</style>

<span class="badge" data-status="active">Active</span>
<span class="badge" data-status="paused">Paused</span>
Don't Invent Non-Standard Attributes
Stick to data-* for custom data
Never add made-up attributes like userid="4821" or active="true" directly on an element — these are invalid HTML, will fail markup validation, and may collide with a future standard attribute of the same name. Always prefix custom data with data-.

Instead of

Use

userid="4821"

data-user-id="4821"

active="true"

data-active="true"

sortkey="price"

data-sort-key="price"

Values Are Always Strings

Because dataset values are strings, remember to convert them explicitly when you need a number or boolean.

type-conversion.js

JS
const el = document.querySelector('[data-quantity="3"]')

// Wrong: string concatenation, not addition
console.log(el.dataset.quantity + 1) // "31"

// Right: convert first
console.log(Number(el.dataset.quantity) + 1) // 4

// Booleans need an explicit check too
const isActive = el.dataset.active === 'true'
JSON in a data attribute
For structured data, store a JSON string and parse it: JSON.parse(el.dataset.config). Keep this to small, non-sensitive payloads — data attributes are visible in the page source.
Quick Reference

HTML attribute

JS dataset property

data-id

dataset.id

data-user-name

dataset.userName

data-in-stock

dataset.inStock

data-sort-key

dataset.sortKey