Stop Overcomplicating It
Most of the time, if you run into issues when using Datastar, you are probably overcomplicating it™.
As explained in going deeper, Datastar is a hypermedia framework. If you approach it like a JavaScript framework, you are likely to run into complications.
So how does one use a hypermedia framework?
The Datastar Way#
Between attribute plugins and action plugins, Datastar provides you with everything you need to build hypermedia-driven applications. Using this approach, the backend drives state to the frontend and acts as the single source of truth, determining what actions the user can take next.
Any additional JavaScript functionality you require that does not work via data-*
attributes and datastar-execute-script
SSE events should ideally be extracted out into external scripts or, better yet, web components.
External Scripts#
When using external scripts, pass data into functions via arguments and return a result or listen for custom events dispatched from them (props down, events up).
In this way, the function is encapsulated – all it knows is that it receives input via an argument, acts on it, and optionally returns a result or dispatches a custom event – and data-*
attributes can be used to drive reactivity.
<div data-signals-result="''">
<input data-bind-foo
data-on-input="result.value = myfunction(foo.value)"
>
<span data-text="result.value"></span>
</div>
function myfunction(data) {
return `You entered ${data}`;
}
If your function call is asynchronous then it will need to dispatch a custom event containing the result. While asynchronous code can be placed within Datastar expressions, Datastar will not await it.
<div data-signals-result="''"
data-on-mycustomevent__window="result.value = evt.detail.value"
>
<input data-bind-foo
data-on-input="myfunction(foo.value)"
>
<span data-text="result.value"></span>
</div>
async function myfunction(data) {
const value = await new Promise((resolve) => {
setTimeout(() => resolve(`You entered ${data}`), 1000);
});
window.dispatchEvent(
new CustomEvent('mycustomevent', {detail: {value}})
);
}
Web Components#
Web components allow you create reusable, encapsulated, custom elements. They are native to the web and require no external libraries or frameworks. Web components unlock custom elements – HTML tags with custom behavior and styling.
When using web components, pass data into them via attributes and listen for custom events dispatched from them (props down, events up).
In this way, the web component is encapsulated – all it knows is that it receives input via an attribute, acts on it, and optionally dispatches a custom event containing the result – and data-*
attributes can be used to drive reactivity.
<div data-signals-result="''">
<input data-bind-foo>
<my-component
data-attributes-src="foo.value"
data-on-mycustomevent="result.value = evt.detail.value"
></my-component>
<span data-text="result.value"></span>
</div>
class MyComponent extends HTMLElement {
static get observedAttributes() {
return ['src'];
}
attributeChangedCallback(name, oldValue, newValue) {
const value = `You entered ${newValue}`;
this.dispatchEvent(
new CustomEvent('mycustomevent', {detail: {value}})
);
}
}
customElements.define('my-component', MyComponent);
Since the value
attribute is allowed on web components, it is also possible to use data-bind
to bind a signal to the web component’s value. Note that a change
event must be dispatched so that the event listener used by data-bind
is triggered by the value change.
<input data-bind-foo>
<my-component
data-attributes-src="foo.value"
data-bind-result
></my-component>
<span data-text="result.value"></span>
class MyComponent extends HTMLElement {
static get observedAttributes() {
return ['src'];
}
attributeChangedCallback(name, oldValue, newValue) {
this.value = `You entered ${newValue}`;
this.dispatchEvent(new Event('change'));
}
}
customElements.define('my-component', MyComponent);
Third-Party Libraries#
Datastar is a tiny self-contained framework that can help liberate you from “dependency hell”. If, for some reason, you absolutely must use a third-party library, you should continue using the props down, events up pattern whenever possible.
For edge-cases in which you find yourself having to change the DOM without involving Datastar, you can import Datastar and apply it to any part of the DOM. for more details, see the JavaScript API.