Add Reactivity to HTML with Custom Attributes – Helium
It allows you to create interactive UI by sprinkling special @ directives into your regular HTML elements without build steps or complex tooling.
@ attributes directly in your HTML.@bind attribute creates automatic synchronization between form inputs and your data model.prevent, once, outside, and debounce.: prefix.@helium, you can embed multiple independent Helium instances on the same page without conflicts.1. For the quickest setup, just add the following script tag to your HTML file. This is our preferred method for small projects.
<script type="module"> import helium from 'https://cdn.jsdelivr.net/gh/daz-codes/helium/helium.js'; helium(); </script>
2. If you’re working within a project that has a build process, you can install it from NPM.
npm install @daz4126/helium
import helium from "@daz4126/helium"; helium();
3. The helium() function accepts an optional configuration object where you can define initial variable values and helper functions that will be available throughout your application.
helium({
count: 0,
userName: "",
formatDate(timestamp) {
return new Date(timestamp).toLocaleDateString();
}
}); 4. Once initialized, you can start using Helium attributes in your HTML. The library automatically processes any element with a Helium attribute (those starting with @ or data-he-) and its children. If you want to limit the scope, add an @helium attribute to a container element, and only that element and its descendants will be reactive.
| Attribute | Description | Example |
|---|---|---|
@data | Initializes variables in a component’s scope. | <div @data="{ count: 0 }"></div> |
@text | Updates the textContent of an element with a JS expression. | <span @text="count"></span> |
@bind | Creates a two-way binding with an input element’s value. | <input @bind="name"> |
@visible | Makes an element visible if the expression is truthy. | <div @visible="count > 3">...</div> |
@hidden | Hides an element if the expression is truthy. | <div @hidden="count <= 3">...</div> |
@ref | Creates a named reference to an element. | <ul @ref="list"></ul> |
@init | Runs a JS expression once when the component initializes. | <div @init="timestamp = Date.now()"></div> |
5. You can listen to any DOM event by prefixing the event name with @.
<button @click="count++">Click Me</button>
Event handlers support several modifiers that change their behavior:
.prevent: Calls event.preventDefault()..once: The handler will only run one time..outside: The handler runs only when the event occurs outside the element..debounce: Delays execution until the specified time (in milliseconds) has passed without additional events.<button @click="count++">Click Me</button> <form @submit.prevent="saveData()"> <button @click.once="showWelcome()">Show Welcome</button> <div @click.outside="closeMenu()"> <input @input.debounce:500="performSearch()"> <!-- Keyboard Events --> <input @keydown.enter="submitForm()"> <div @keydown.escape="closeModal()"> <div @keydown.ctrl.s="saveDocument()">
6. To dynamically update an element’s attribute, prefix the attribute name with a colon (:). The value should be a JavaScript expression.
<div :class="isActive ? 'bg-blue' : 'bg-gray'"> <input :disabled="isProcessing"> <a :href="'/users/' + userId">Profile</a>
Dynamic classes support object syntax where keys are class names and values are booleans indicating whether the class should be applied:
<div :class="{ active: isActive, disabled: isDisabled, 'text-large': isLarge }"> Dynamic styles also support object syntax:
<div :style="{ color: textColor, fontSize: fontSize + 'px', display: isVisible ? 'block' : 'none' }"> 7. Helium provides a few handy “magic” attributes for use inside your expressions:
$el: A reference to the current element.$event: The DOM event object within an event handler.$data: The main data object containing all your reactive variables.$: A shortcut for document.querySelector.<button @click="$('#sidebar').classList.add('visible')">Show Sidebar</button>
<button @click="$el.remove()">Remove Me</button>
<button @click="console.log($event.timeStamp)">Log Time</button>
<button @click="updateUser($data)">Update</button> 8. When you pass functions to the helium() initialization, they become available in all expressions. However, there’s an important caveat about reactivity: magic variables and Helium data are not directly accessible inside these functions. You need to pass them as arguments.
If you pass a Helium variable directly, it will be passed by value, and updates inside the function will not trigger reactive updates. To maintain reactivity, pass the $data object instead and modify its properties:
// This won't work for reactivity
helium({
increment(count) {
count++; // This only updates the local parameter
}
}); <button @click="increment(count)">Increment</button>
// This maintains reactivity
helium({
increment(data) {
data.count++; // This updates the reactive property
}
}); <button @click="increment($data)">Increment</button>
9. Helium includes built-in AJAX helpers ($get, $post, $put, $patch, $delete) that you can use via event attributes. When using these methods, you can specify additional attributes to control the request and response handling:
append, prepend, or replace).<button @get="/api/users" @target="#user-list" @action="append" @loading="<p>Loading...</p>"> Load Users </button>
The AJAX helpers automatically handle CSRF tokens if a meta tag with name="csrf-token" exists, and they support Turbo Stream responses for seamless integration with Hotwire applications.
Q: Can I use Helium with existing JavaScript frameworks or libraries?
A: Yes, Helium is designed to coexist with other tools. Since it’s scoped to elements with Helium attributes, it won’t interfere with code outside that scope.
Q: How do I handle complex state management with nested objects and arrays?
A: Helium’s Proxy-based reactivity works with nested structures automatically. When you update a nested property like data.user.profile.name = 'Jane', it will trigger updates for any bindings that reference it. For arrays, use standard methods like push, splice, or direct index assignment, and Helium will detect the changes. If you’re working with deeply nested structures and performance becomes an issue, consider flattening your state or breaking it into smaller reactive scopes with multiple @helium root elements.
Q: Why aren’t my custom functions triggering reactive updates?
A: This is the most common pitfall. Functions defined in the initialization config don’t have direct access to the reactive state. If you pass a Helium variable as a parameter, it’s passed by value, so modifications inside the function won’t trigger updates. The solution is to pass $data as a parameter and modify its properties: increment($data) with a function like increment(data) { data.count++; }. This ensures you’re modifying the actual reactive Proxy, not a local copy.
Q: How can I integrate Helium with server-side rendered content that changes dynamically?
A: Helium automatically processes new DOM nodes through its MutationObserver, so dynamically loaded content will be picked up immediately. If you’re using HTMX or Turbo, the newly inserted HTML will be processed and made reactive. For the smoothest experience, consider loading Idiomorph alongside Helium. This will enable intelligent DOM morphing that preserves focus states and minimizes flicker when content updates. You can also use Helium’s built-in AJAX helpers with the @target and @action attributes to declaratively specify where server responses should be inserted.
Q: How does reactivity work with nested objects or arrays?
A: Helium’s reactivity is powered by a Proxy, which deeply wraps objects. This means changes to nested properties like user.name = 'new name' or array manipulations like items.push('new item') will trigger UI updates automatically, provided you’re modifying the array in a way that the proxy can detect.
The post Add Reactivity to HTML with Custom Attributes – Helium appeared first on CSS Script.
Prime Video has published a The Boys Season 5, Episode 7 teaser trailer, revealing that…
A new video has revealed a ton of exciting new information on the original, scrapped…
Universal Pictures is inviting audiences to embark on a “digital odyssey” for director Christopher Nolan’s…
The Dungeon Crawler Carl series has been experiencing a whirlwind of a year so far…
Heads up: The massive 77" LG Evo C5 4K OLED Smart TV just dropped to…
After months of concerns regarding 007 First Light's performance, IO Interactive has confirmed the upcoming…
This website uses cookies.