See Datastar in action with this reactive todo app. Add tasks, toggle completion, and filter - all with real-time UI updates.
A lightweight (14.3 KiB) JavaScript library that adds reactivity to HTML using data-* attributes. Use with any backend language and Server-Sent Events for real-time UI updates.
Add reactivity directly to HTML with data-* attributes.
State is managed through reactive signals that automatically track and propagate changes throughout your UI.
Embrace the web's natural architecture. Keep logic on the server and your frontend lightweight and focused.
ds_bind(signal_name) Creates a two-way connection between form elements and signals. When users interact with the element, the signal updates automatically.
Input(ds_bind('input1'), ...) ds_text(expr) Sets an element's text content based on signal values using JavaScript expressions. Great for displaying dynamic output that updates automatically when signals change.
$input $input.toUpperCase() $input ? $input : 'Nothing entered' 'Length: ' + $input.length Span(ds_text('$input.toUpperCase()')) ds_computed(name=expr, ...) Creates new read-only signals derived from reactive expressions. These computed values update automatically when their dependencies change.
$input.length $input.repeat(2) $input ? $input.toUpperCase() : 'Nothing entered' Div(ds_computed(doubled='$input.repeat(2)')) ds_show(when=expr) Conditionally shows or hides elements based on reactive expressions. Elements are only visible when the expression evaluates to true.
$input != '' !$input $count > 10 && $isAdmin ds_show(when="$input != ''") ds_show("$input != ''") Button(ds_show(when='$input != ""'), 'Save') ds_classes(**expressions) Conditionally applies CSS classes based on reactive expressions. Classes are added when the expression evaluates to true and removed when false.
ds_classes(hidden="$input == ''") ds_classes(text_primary="$input.length > 0", font_bold="$input.length > 3") ds_classes(bg_error="$isInvalid", text_success="$isValid") Div(ds_classes(text_primary="$input.length > 0")) ds_attrs(**expressions) Reactively sets HTML attributes based on signal values. Attributes are updated automatically when the expressions evaluate to new values.
ds_attrs(disabled="$input == ''") ds_attrs(disabled="!['red', 'blue'].includes($input)", title="$input ? 'Submit ' + $input : 'Enter a valid color'") ds_attrs(style="'color: ' + ($isError ? 'red' : 'green')") ds_attrs(aria_expanded="$isOpen", aria_label="$buttonLabel") Button(ds_attrs(disabled="$isEmpty"), "Save") ds_signals(**signal_values) Initializes reactive signals that can be accessed throughout your application. Signals are the foundation of Datastar's reactivity system.
ds_signals(count="0") - Numbers don't need quotes in JavaScript ds_signals(name="'Anonymous'") - Strings require quotes in JavaScript (inner quotes) ds_signals(isAdmin="false") - JavaScript booleans are lowercase ds_signals(count="0", name="'User'", isAdmin="false") - Define several at once ds_signals(user__name="'Anonymous'") - Double underscore becomes dot notation ds_signals(user=json_dumps({"name": "", "email": ""})) - For complex nested data Div(ds_signals(count="0", name="'User'")) ds_on(**event_handlers) Attaches event listeners to elements that execute JavaScript expressions when triggered. This enables interactive UI without writing custom JavaScript functions.
ds_on(click="$count++") ds_on(click="$count = 0; $message = 'Reset'") ds_on(input="$value = evt.target.value") ds_on(keydown="if(evt.key === 'Enter') $submit = true") ds_on(click="@post('/api/data')")
The special evt variable gives access to the browser's native event object, allowing you to access properties like evt.target, evt.key, etc.
Button(ds_on(click="$count++"), "Increment") ds_signals, ds_computed, ds_on, ds_show, ds_text This example demonstrates a simple interactive quiz using Datastar's client-side reactivity features.
ds_signals for state management ds_computed for answer validation ds_on for user interaction ds_show for dynamic feedback ds_text for displaying values While the initial HTML is served from a route, all quiz interactions happen entirely in the browser without additional server requests. This makes it simple to implement but limits it to using hardcoded questions.
Button(ds_on(click="$response = prompt('Answer:')"), "Answer") @get, signal_update, fragment_update Building on the client-side quiz, this example demonstrates how to enhance the experience with server-side functionality using Server-Sent Events (SSE).
@get directive for dynamic content signal_update fragment_update Unlike the previous example where all interaction happens in the browser, this quiz makes additional server requests during use to fetch new questions and answers, demonstrating how Datastar bridges client and server functionality.
Button(ds_on(click="@get('/actions/quiz')"), "Fetch Question") @sse, signal_update, fragment_update Datastar provides a powerful way to create server routes that can send real-time updates to the client using Server-Sent Events (SSE).
@sse decorator marks a route as an SSE endpoint fragment_update replaces or modifies HTML elements signal_update changes signal values without reloading the page @rt("/your/path") @sse decorator to enable streaming yield to send updates to the client @get, @post, etc. directives @rt("/api/data") @sse async def handler(): yield signal_update(...) ds_indicator(signal_name) ds_indicator("signal_name") to create a signal that tracks the loading state true during the request and false when complete ds_show or ds_classes to create responsive loading states Button("Load Data", ds_on(click="@get('/api/data')"), ds_indicator("loading")) @setAll(prefix, value) / @toggleAll(prefix) @setAll(prefix, value) sets all signals that match the prefix to the specified value @toggleAll(prefix) toggles the boolean value of all signals that match the prefix Button("Check All", ds_on(click="@setAll('checkboxes.', true)")) signal_update(), fragment_update() yield fragment_update(status_component, "#container", "inner") Get building! Or explore the standalone Todo app some more.