Reactive Signals
In a hypermedia approach, the backend drives state to the frontend and acts as the primary source of truth. It’s up to the backend to determine what actions the user can take next by patching appropriate elements in the DOM.
Sometimes, however, you may need access to frontend state that’s driven by user interactions. Click, input and keydown events are some of the more common user events that you’ll want your frontend to be able to react to.
Datastar uses signals to manage frontend state. You can think of signals as reactive variables that automatically track and propagate changes in and to Datastar expressions. Signals are denoted using the $
prefix.
Data Attributes #
Datastar allows you to add reactivity to your frontend and interact with your backend in a declarative way using data-* attributes.
The Datastar VSCode extension and IntelliJ plugin provide autocompletion for all data-*
attributes.
data-bind
#
The data-bind
attribute sets up two-way data binding on any HTML element that receives user input or selections. These include input
, textarea
, select
, checkbox
and radio
elements, as well as web components whose value can be made reactive.
1<input data-bind-foo />
This creates a new signal that can be called using $foo
, and binds it to the element’s value. If either is changed, the other automatically updates.
You can accomplish the same thing passing the signal name as a value, an alternate syntax that might be more useful for some templating languages:
1<input data-bind="foo" />
data-text
#
The data-text
attribute sets the text content of an element to the value of a signal. The $
prefix is required to denote a signal.
The value of the data-text
attribute is a Datastar expression that is evaluated, meaning that we can use JavaScript in it.
data-computed
#
The data-computed
attribute creates a new signal that is derived from a reactive expression. The computed signal is read-only, and its value is automatically updated when any signals in the expression are updated.
This results in the $repeated
signal’s value always being equal to the value of the $foo
signal repeated twice. Computed signals are useful for memoizing expressions containing other signals.
data-show
#
The data-show
attribute can be used to show or hide an element based on whether an expression evaluates to true
or false
.
This results in the button being visible only when the input value is not an empty string. This could also be shortened to data-show="!$foo"
.
data-class
#
The data-class
attribute allows us to add or remove an element’s class based on an expression.
If the expression evaluates to true
, the success
class is added to the element; otherwise, it is removed.
The data-class
attribute can also be used to add or remove multiple classes from an element using a set of key-value pairs, where the keys represent class names and the values represent expressions.
data-attr
#
The data-attr
attribute can be used to bind the value of any HTML attribute to an expression.
This results in a disabled
attribute being given the value true
whenever the input is an empty string.
The data-attr
attribute can also be used to set the values of multiple attributes on an element using a set of key-value pairs, where the keys represent attribute names and the values represent expressions.
1<button data-attr="{disabled: $foo == '', title: $foo}">Save</button>
data-signals
#
Signals are globally accessible from anywhere in the DOM. So far, we’ve created signals on the fly using data-bind
and data-computed
. If a signal is used without having been created, it will be created automatically and its value set to an empty string.
Another way to create signals is using the data-signals
attribute, which patches (adds, updates or removes) one or more signals into the existing signals.
1<div data-signals-foo="1"></div>
Signals can be nested using dot-notation.
1<div data-signals-form.foo="2"></div>
The data-signals
attribute can also be used to patch multiple signals using a set of key-value pairs, where the keys represent signal names and the values represent expressions.
1<div data-signals="{foo: 1, form: {foo: 2}}"></div>
data-on
#
The data-on
attribute can be used to attach an event listener to an element and run an expression whenever the event is triggered.
This results in the $foo
signal’s value being set to an empty string whenever the button element is clicked. This can be used with any valid event name such as data-on-keydown
, data-on-mouseover
, etc. Custom events may also be used.
These are just some of the attributes available in Datastar. For a complete list, see the attribute reference.
Frontend Reactivity #
Datastar’s data attributes enable declarative signals and expressions, providing a simple yet powerful way to add reactivity to the frontend.
Datastar expressions are strings that are evaluated by Datastar attributes and actions. While they are similar to JavaScript, there are some important differences that are explained in the next section of the guide.
See if you can figure out what the code below does based on what you’ve learned so far, before trying the demo below it.
1<div
2 data-signals="{response: '', answer: 'bread'}"
3 data-computed-correct="$response.toLowerCase() == $answer"
4>
5 <div id="question">What do you put in a toaster?</div>
6 <button data-on-click="$response = prompt('Answer:') ?? ''">BUZZ</button>
7 <div data-show="$response != ''">
8 You answered “<span data-text="$response"></span>”.
9 <span data-show="$correct">That is correct ✅</span>
10 <span data-show="!$correct">
11 The correct answer is “
12 <span data-text="$answer"></span>
13 ” 🤷
14 </span>
15 </div>
16</div>
The Datastar Inspector can be used to inspect and filter current signals and view signal patch events in real-time.
Patching Signals #
Remember that in a hypermedia approach, the backend drives state to the frontend. Just like with elements, frontend signals can be patched (added, updated and removed) from the backend using backend actions.
If a response has a content-type
of application/json
, the signal values are patched into the frontend signals.
We call this a “Patch Signals” event because multiple signals can be patched (using JSON Merge Patch RFC 7396) into the existing signals.
1{"hal": "Affirmative, Dave. I read you."}
If the response has a content-type
of text/event-stream
, it can contain zero or more SSE events. The example above can be replicated using a datastar-patch-signals
SSE event.
Here’s the code to generate the SSE event above using the Go SDK.
Because we can send as many events as we want in a stream, and because it can be a long-lived connection, we can extend the example above to first set the hal
signal to an “affirmative” response and then, after a few seconds, reset the signal.
Here’s the code to generate the SSE events above using the Go SDK.
In addition to your browser’s dev tools, the Datastar Inspector can be used to monitor and inspect SSE events received by Datastar.
We’ll cover event streams and SSE events in more detail later in the guide, but as you can see they are just plain text events with a special syntax, made simpler by the SDKs.