How to make an LWC pass value to your JavaScript controller
Making an LWC pass value to a controller when someone clicks a link feels like it should be a one-liner. Then you start second-guessing event bubbling, security, and whether you're doing it the "Salesforce way." I've been there, and I've watched plenty of experienced devs overcomplicate it from that point on.
Brand new Lightning Web Component or an old Visualforce page you're patching, the goal is the same. Click a link, grab a record ID or a status flag, do something with it in your JS.
The modern standard: data attributes in LWC
When you need an LWC pass value from the UI into your logic, use data attributes. They're standard HTML5 and they keep your JavaScript out of your markup. Instead of parsing a URL or reaching for some global variable, you tag the element with what you need.
<! - template.html - >
<template>
<a href="#" data-id={acc.Id} onclick={handleAccountClick}>
{acc.Name}
</a>
</template>
In your JavaScript you have to prevent the default link behavior, so the page doesn't refresh, and then read that data attribute. This LWC pass value pattern comes up constantly once you get into LWC component communication and data handling.
// component.js
import { LightningElement } from 'lwc';
export default class AccountList extends LightningElement {
handleAccountClick(event) {
event.preventDefault();
// This is the magic line
const accountId = event.currentTarget.dataset.id;
console.log('You clicked account:', accountId);
}
}
Why an LWC pass value strategy needs the right event target
The components I've seen break most often break on the same confusion: event.target versus event.currentTarget. If your hyperlink wraps an icon or a span and the user clicks that icon, event.target is the icon. event.currentTarget is always the link where you attached the listener.
Pro tip: use
event.currentTarget.dataset. It's the more reliable read, because it comes from the element that actually carries the onclick handler, even when the user clicks a nested child.
Still stuck in Aura or Visualforce? The logic is much the same. In Aura you'd use data- attributes on your anchor tags. In Visualforce you might reach for apex:outputLink, but if you're calling JS, a plain <a> tag with a data attribute is cleaner, and it beats building long JavaScript strings in your page markup.
Best practices for an LWC pass value workflow
Getting the data out is the easy part. Keeping the code from turning into a bowl of spaghetti is where I've learned a few things the hard way.
- Don't use javascript:void(0). It's an old habit. In LWC, use a hash or a real URL and call
preventDefault(). It's better for accessibility and feels more natural to the browser. - Keep it simple. Don't try to pass entire JSON objects through a data attribute. If you need more than a simple ID or a string, pass the ID and find the rest of the data in your JS array.
- Think about navigation. If you're only trying to open a record, skip the JS controller and use the
lightning/navigationservice. It's built for this. - Watch your types. Values coming out of
datasetare always strings, so cast a number or a boolean yourself.
On more complex interfaces, LWC element scrolling is worth a look for what happens after that link is clicked. Small polish like that is what makes a component feel finished.
What about security?
Any time you take a value out of the DOM and hand it to a controller, think about XSS. Locker Service, or Lightning Web Security now, covers a lot of ground, but you still shouldn't blindly trust everything coming from the UI. If that LWC pass value goes straight into an Apex call, make sure the Apex is properly secured and using bind variables.
Key takeaways
| Feature | Recommended Approach |
|---|---|
| Standard LWC pass value | Use data-attributes and event.currentTarget.dataset. |
| Navigation | Use NavigationMixin instead of raw links where possible. |
| Legacy Visualforce | Stick to data- attributes or Remote Actions. |
| Event Handling | Always use event.preventDefault() to stop page reloads. |
The short answer is data attributes. They're the most "future-proof" way to handle this. Whether you're passing a record ID to open a modal or a row index to delete an item from a list, the data-* pattern keeps your code readable and easy to debug. Next time you're tempted to write a complex string-building function in your HTML, stop and reach for a data attribute instead.
Leave a Comment