Getting Started with Datastar – Build a Rust and Axum Todo App

alex_hirner1 pts0 comments

Getting Started with Datastar - Build a Rust + Axum Todo App - HAMY

Blog<br>Projects<br>Notes<br>About<br>49

Getting Started with Datastar - Build a Rust + Axum Todo App<br>Essay - Published: 2026.04.08 | 8 min read (2,048 words)<br>build | create | datastar | rust<br>DISCLOSURE: If you buy through affiliate links, I may earn a small commission. (disclosures)

For the past several years I've been building server-side rendered apps using hypermedia libraries like HTMX and Datastar to sprinkle in interactivity where it's useful. I like this approach because it's simple, efficient, and allows me to use whatever language / stack I want - everything can write strings and serve web requests and therefore can serve hypermedia.

I've also been learning and using High-Level Rust so it was only natural we combine the two at some point.

This post continues my fullstack Rust web series:

Build a Simple Single-File Rust Web API

Build a Single-File Rust Web API with SQLite

Build a Fullstack SSR Web App with Rust + Maud

Here we'll sprinkle in interactivity with Datastar.

What we're building

We're building a simple CRUD todo list app:

Create todos

Read a list of todos

Update the completed status of todos

Delete todos

Tech stack:

Backend : Rust + Axum

Data : Sqlite and sqlx

Frontend : SSR HTML with Maud and Datastar for interactivity (using the datastar crate)

What is Datastar

Datastar is a newer entry to the Hypermedia space and is the most unusual choice in this stack so wanted to take a minute to explain a bit about what it is and how it works.

The core idea behind hypermedia frameworks is:

The backend controls the frontend - little to no client-side logic

You can partially rerender pages with HTML fragments - improving reactivity and composability of SSR HTML when compared to MPAs

This reduces code complexity and can improve performance by reducing layers of transformation logic and duplication across client/server

Datastar takes this a little further than others like HTMX by:

SSE first - HTML fragments and signals all pass over same SSE connection

Built in signals - so can do both client and server interactivity, similar to an Alpine + HTMX setup

In this example we're using 3 key server to client primitives:

PatchElements - Server sends HTML fragments, Datastar places them in the DOM via CSS selectors

PatchSignals - Server sends JSON, Datastar updates the reactive client-side signals (this is where Alpine is often used)

ExecuteScript - Server tells the browser to run some javascript - for eg alerts or navigation

How it's built

We'll be focused mainly on the view layer and Datastar in this post to keep this guide streamlined and understandable. To see more details on the other layers, checkout the other posts in this series.

If you want the full source code of this project so you can clone it and run it yourself, checkout the HAMY LABS Code Example repo on GitHub. This is available to HAMINIONS Members.

Models & Errors

The Models and Errors code is largely the same as it was in the previous installment of the series:

Todo - Domain representation of a Todo

CreateTodo - Dto for deserializing Create request payloads

TodoRow - Data representation of a Todo

#[derive(Clone, LightClone)]<br>struct Todo {<br>id: Uuid,<br>title: Arc,<br>completed: bool,

#[derive(Deserialize)]<br>struct CreateTodo {<br>title: String,

#[derive(FromRow)]<br>struct TodoRow {<br>id: Uuid,<br>title: String,<br>completed: bool,

The errors are also the same:

enum TodoError {<br>NotFound(Uuid),<br>Internal(sqlx::Error),

Service Layer

The Service Layer is similarly untouched:

TodoService trait - interface with async list, get, create, complete, and delete methods

SqliteTodoService - implementation that calls into sqlite

#[async_trait]<br>trait TodoService: Send + Sync {<br>async fn list(&self) -> Result, TodoError>;<br>async fn get(&self, id: Uuid) -> Result;<br>async fn create(&self, input: CreateTodo) -> Result;<br>async fn complete(&self, id: Uuid) -> Result;<br>async fn delete(&self, id: Uuid) -> Result;

Views - SSR HTML with Datastar Attributes via Maud

This is where we start to see Datastar. HTML templates include data-* attributes that Datastar interprets. If you're coming from HTMX this is similar to the hx-* attributes.

The views are split into page-based components. These are composed together to build the full page but each can be rendered independently which powers the individual component endpoints which is how we get partial rerenders.

Typically how this works is:

Use coarse-grained components - given a domain object, how does it render on this page?

Main endpoint for the page - renders the full DOM tree

View endpoints under that page - render just the requested item

This is similar to the Backend for Frontend API design approach, but we return HTML fragments and signals instead of just JSON.

Layout - The base layout of our page, note that it pulls in Datastar. Gotcha: Datastar's public API has been changing a lot as it goes through 1.0 RC candidates so you...

datastar rust build todo html server

Related Articles