
In the React world, most developers don’t think twice before usingclosures in their component render methods, especially when mapping over lists.
But what if I told you there's an underutilized, yet powerful alternative that can help with performance, readability, and even better integration with tools?
Let’s talk aboutHTML data-* attributes: a feature that’s rarely used by frontend developers, but deserves a second look.
❓ What are data-* Attributes?
HTML’sdata-*
attributes allow you toembed custom data inside DOM elements. In plain HTML:
<divdata-user-id="123"data-role="admin">John</div>
InReact, you can use them the same way:
<divdata-user-id={user.id}data-role={user.role}>{user.name}</div>
They are accessible in event handlers via the dataset object:
e.currentTarget.dataset.userId;
🧠 The Common Pattern: Closures in .map()
Let’s say you’re rendering a list of items:
{items.map((item)=>(<buttonkey={item.id}onClick={()=>handleClick(item.id)}>{item.name}</button>))}
This works fine. It’s readable and easy to write.
But behind the scenes,you're creating a new function for each item on every render: a closure that capturesitem.id
.
In many apps, this has no practical impact. However...
⚠️ The Downsides of Closures
While closures are a fundamental part of JavaScript and React, using them inside.map()
can have drawbacks:
1. Unnecessary Re-Renders
If you're usingReact.memo
,React.useCallback
, or virtualized lists (likereact-window),new function references can cause unwanted re-renders. Since the function is recreated every time, memoization doesn't help.
2. Harder to Optimize
Imagine you're building a highly interactive list that renders hundreds of items. Minimizing re-renders becomes important, andinline closures can work against that.
✅ The Alternative:data-*
Instead of creating a closure for each item, attach metadata directly to the DOM usingdata-*
:
functionhandleClick(e){constid=e.currentTarget.dataset.id;console.log("Clicked item:",id);}{items.map((item)=>(<buttonkey={item.id}data-id={item.id}onClick={handleClick}>{item.name}</button>))}
Single function reference → plays nicely with
React.memo
orReact.useCallback
Improved performance for large lists or re-render-sensitive components
🤔 So Why Isn’t Everyone Doing This?
Because closures are easy, intuitive, and performant enough for most apps.data-*
feels a bit "old-school," and it’s rarely mentioned in modern React/Frontend tutorials.
But in situations where performance or memoization matters,this approach can be a hidden gem.
✨ In Summary
Whiledata-*
attributes aren’t a common tool in a React developer’s toolbox, they offer real advantages in specific scenarios:
Reduce unnecessary function creations
Improve memoization and rendering performance
Enable simpler and cleaner event handling
They’re not a replacement for closures, but they’re a great alternative when performance or architecture calls for it.
Have you ever useddata-*
attributes like this? Or do you usually stick with closures?I’d love to hear your thoughts and experiences!
I hope you found this post interesting. Let me know what you think in the comments 👇
Top comments(38)

Im not a fan of this approach because only serialisable data can be inserted into the DOM and then extracted back out into JS, and while you can simply use the ID tofind
the item in the array but doing so will cause so many loops that you lose any performance gained by removing the closures.
While always, it depends on the situation, so it's nice to have it in your pocket should the situation arise.

- LocationRussia, Nizhniy Novgorod
- WorkFrontend Developer VK
- Joined
new function references can cause unwanted re-renders
That's why functional components in React are sucks.
Also most React developers do not even know thatuseMemo
useEffect
useState
and all other React hooks it is not only "magic hook" - this is functions which do some calculations inside.
Take a look at this great article
mbrizic.com/blog/react-is-insane/
I personally try to get rid of using React hooks, I'm using MVVM and MobX which works with classes

- LocationRussia, Nizhniy Novgorod
- WorkFrontend Developer VK
- Joined
Also if we will usedata-*
attributes then this is killing any types in TypeScript, so this is not good.

- LocationRussia, Nizhniy Novgorod
- WorkFrontend Developer VK
- Joined
Also modifyingdata-*
attributes is not performant (calls reflow after change)

- LocationSan Diego County
- EducationGraphic design and multimedia
- WorkFull Stack Developer
- Joined
Hard disagree. This article is extremely accurate representation of what 90% of enterprise and startup codebases contain right now. Anyone who denies it either has no experience or is too far the react cult to realize what’s happening.

So much this

- LocationRussia, Nizhniy Novgorod
- WorkFrontend Developer VK
- Joined
Why ?
- LocationSpain
- WorkSoftware Engineer
- Joined
I’m not criticizing React. Actually I love it. I’m just presenting another way of doing things 😙

You should. at this moment, frameworks like Vue or Angular are now better and more modern / performant than React.

youtu.be/DLa2FQQzCr4?si=rSh0tlV-R5...
tldr: this is not a great article

- LocationSan Diego County
- EducationGraphic design and multimedia
- WorkFull Stack Developer
- Joined
That video demonstrates and perfectly proves the points in the article by showcasing snippets from his own code that contain useEffect hooks and also sharing examples from large projects that contain similar problems…
TLDR: that video is an amazing demonstration why the article is accurate and how blind React worshippers are for their own misery.

It's also not typesafe, and for large data or long lists you will be serialising the data into strings ahead of time, before you even need it. It's cheaper to make a new anonymous function than serialising large data sets to string.
Keeping the data in memory and only accessing when needed is sometimes more preferable.

I like this, I can see the uses for it.
But like everything in writing code, there are many ways to skin a cat.
And as engineers, we need to be aware of the security risks and use this with caution.
OK for UI-related non-sensitive data
Not OK for anything security-sensitive (roles, tokens, permissions)
Safe Use Example:<div data-product-id={product.id} onClick={handleClick}>{product.name}</div>
function handleClick(e) {
const id = e.currentTarget.dataset.productId;
// Use it to fetch more info securely from backend
}
Unsafe Example:<div data-auth-token={user.token}>...</div> // Don't leak sensitive data
<div data-role="admin" onClick={showAdminPanel}> // Role-based access is not secure on frontend

With the rise of SSR this may become more important, however I remember the time when getting data out of the DOM was a big push. The thing is, you always want a single source of truth for your data. If you put things in the DOM and have things in memory, you will have to figure which one to trust when they fall out of sync. Moving everything to JavaScript is easy, because AJAX goes straight to JS. The DOM cannot query the server and only understands strings. So if the DOM is your source of truth, how are you going to get data into and out of it? Will it be able to keep up?

- LocationKolkata, West Bengal, India
- WorkCEO & Founder at Think to Share
- Joined
Such a simple trick but so underrated..
Some comments may only be visible to logged-in visitors.Sign in to view all comments.
For further actions, you may consider blocking this person and/orreporting abuse