Lustre applications are built using a message-based state management system (similar to the Elm Architecture). Every interactive application is composed of three core building blocks:
- Model: A type representing the entire state of your application. An
init function is used to construct the initial model. - Message: A type representing all possible ways the outside world (e.g., user interactions, API responses) can communicate with your application. An
update function receives these messages and returns a new Model. - View: A function that takes the current
Model and returns an Element(Message). This function is called whenever the model changes to re-render the UI.
The Lifecycle Loop:
Model $\rightarrow$ view $\rightarrow$ Element(Message) $\rightarrow$ (User Interaction) $\rightarrow$ Message $\rightarrow$ update $\rightarrow$ New Model $\rightarrow$ (Repeat)
import gleam/int
import lustre
import lustre/element.{type Element}
import lustre/element/html
import lustre/event
// 1. The Model
type Model = Int
// 2. The Messages
type Message {
UserClickedIncrement
UserClickedDecrement
}
// The init function
fn init(_args) -> Model {
0
}
// The update function
fn update(model: Model, message: Message) -> Model {
case message {
UserClickedIncrement -> model + 1
UserClickedDecrement -> model - 1
}
}
// 3. The View
fn view(model: Model) -> Element(Message) {
let count = int.to_string(model)
html.div([], [
html.button([event.on_click(UserClickedIncrement)], [html.text("+")]),
html.p([], [html.text(count)]),
html.button([event.on_click(UserClickedDecrement)], [html.text("-")])
])
}
pub fn main() {
// Use lustre.simple to tie the blocks together
let app = lustre.simple(init, update, view)
let assert Ok(_) = lustre.start(app, "#app", Nil)
Nil
}