Datastar Expressions

Datastar expressions are strings that are evaluated by data-* attributes. While they are similar to JavaScript, there are some important differences that make them more powerful for declarative hypermedia applications.

The following example outputs 1 because we’ve defined foo as a signal with the initial value 1, and are using $foo in a data-* attribute.

1<div data-signals:foo="1">
2    <div data-text="$foo"></div>
3</div>

A variable el is available in every Datastar expression, representing the element that the attribute is attached to.

1<div data-text="el.offsetHeight"></div>

When Datastar evaluates the expression $foo, it first converts it to the signal value, and then evaluates that expression in a generated function. This means that JavaScript can be used in Datastar expressions.

1<div data-text="$foo.length"></div>

JavaScript operators are also available in Datastar expressions. These include (but are not limited to) the ternary operator ?:, the logical OR operator ||, and the logical AND operator &&. These operators are helpful in keeping Datastar expressions terse.

 1// Output one of two values, depending on the truthiness of a signal
 2<div data-text="$landingGearRetracted ? 'Ready' : 'Waiting'"></div>
 3
 4// Show a countdown if the signal is truthy or the time remaining is less than 10 seconds
 5<div data-show="$landingGearRetracted || $timeRemaining < 10">
 6    Countdown
 7</div>
 8
 9// Only send a request if the signal is truthy
10<button data-on:click="$landingGearRetracted && @post('/launch')">
11    Launch
12</button>

Multiple statements can be used in a single expression by separating them with a semicolon.

1<button data-on:click="$landingGearRetracted = true; @post('/launch')">
2    Force launch
3</button>

Expressions may span multiple lines, but a semicolon must be used to separate statements. Unlike JavaScript, line breaks alone are not sufficient to separate statements.

1<button data-on:click="
2    $landingGearRetracted = true; 
3    @post('/launch')
4">
5    Force launch
6</button>

Using JavaScript #

Most of your JavaScript logic should go in data-* attributes, since reactive signals and actions only work in Datastar expressions.

JavaScript functionality that is not appropriate for data-* attributes should be moved to external scripts or encapsulated in web components, preferably using Rocket.

Caution: if you find yourself trying to do too much in Datastar expressions, you are probably overcomplicating it™.

Always encapsulate state and send props down, events up.

Executing Scripts #

Just like elements and signals, the backend can also send JavaScript to be executed on the frontend using backend actions.

1<button data-on:click="@get('/endpoint')">
2    What are you talking about, HAL?
3</button>

If a response has a content-type of text/javascript, the value will be executed as JavaScript in the browser.

1alert('This mission is too important for me to allow you to jeopardize it.')
Demo

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 by including a script tag inside of a datastar-patch-elements SSE event.

1event: datastar-patch-elements
2data: elements <div id="hal">
3data: elements     <script>alert('This mission is too important for me to allow you to jeopardize it.')</script>
4data: elements </div>
5

If you only want to execute a script, you can append the script tag to the body.

1event: datastar-patch-elements
2data: mode append
3data: selector body
4data: elements <script>alert('This mission is too important for me to allow you to jeopardize it.')</script>
5

Most SDKs have an ExecuteScript helper function for executing a script. Here’s the code to generate the SSE event above using the Go SDK.

1sse := datastar.NewSSE(writer, request)
2sse.ExecuteScript(`alert('This mission is too important for me to allow you to jeopardize it.')`)
Demo

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.