lustre
Lustre is a declarative framework for building Web apps in Gleam.
Types
An App
describes a Lustre application: what state it holds and what kind
of actions get dispatched to update that state. The only useful thing you can
do with an App
is pass it to start
.
You can construct an App
from the two constructors exposed in this module:
basic
and application
. Although you can’t do
anything but start
them, the constructors are separated in case
you want to set up an application but defer starting it until some later point
in time.
+--------+
| |
| update |
| |
+--------+
^ |
| |
Action | | #(State, Action)
| |
| v
+------+ +------------------------+
| | #(State, Action) | |
| init |------------------->| Lustre Runtime |
| | | |
+------+ +------------------------+
^ |
| |
Action | | State
| |
| v
+--------+
| |
| render |
| |
+--------+
Someone please PR the Gleam docs generator to fix the monospace font, thanks! 💖
pub opaque type App(state, action)
Functions
pub fn application(init: #(a, Cmd(b)), update: fn(a, b) ->
#(a, Cmd(b)), render: fn(a) -> Element(b)) -> App(a, b)
An evolution of a simple
app that allows you to return a
Cmd
from your init
and update
s. Commands give
us a way to perform side effects like sending an HTTP request or running a
timer and then dispatch actions back to the runtime to trigger an update
.
import lustre
import lustre/cmd
import lustre/element
pub fn main () {
let init = #(0, tick())
let update = fn (state, action) {
case action {
Tick -> #(state + 1, tick())
}
}
let render = fn (state) {
element.div([], [
element.text("Count is: ")
element.text(state |> int.to_string |> element.text)
])
}
let app = lustre.simple(init, update, render)
lustre.start(app, "#root")
}
fn tick () -> Cmd(Action) {
cmd.from(fn (dispatch) {
setInterval(fn () {
dispatch(Tick)
}, 1000)
})
}
external fn set_timeout (f: fn () -> a, delay: Int) -> Nil
= "" "window.setTimeout"
pub fn element(element: Element(a)) -> App(Nil, a)
Create a basic lustre app that just renders some element on the page.
Note that this doesn’t mean the content is static! With element.stateful
you can still create components with local state.
Basic lustre apps don’t have any global application state and so the
plumbing is a lot simpler. If you find yourself passing lots of state around,
you might want to consider using simple
or application
instead.
import lustre
import lustre/element
pub fn main () {
let app = lustre.element(
element.h1([], [
element.text("Hello, world!")
])
)
lustre.start(app, "#root")
}
pub fn simple(init: a, update: fn(a, b) -> a, render: fn(a) ->
Element(b)) -> App(a, b)
If you start off with a simple [element
](#element) app, you may find
yourself leaning on stateful
elements
to manage state used throughout your app. If that’s the case or if you know
you need some global state from the get-go, you might want to construct a
simple
app instead.
This is one app constructor that allows your HTML elements to dispatch actions to update your program state.
import gleam/int
import lustre
import lustre/element
import lustre/event.{ dispatch }
type Action {
Incr
Decr
}
pub fn main () {
let init = 0
let update = fn (state, action) {
case action {
Incr -> state + 1
Decr -> state - 1
}
}
let render = fn (state) {
element.div([], [
element.button([ event.on_click(dispatch(Decr)) ], [
element.text("-")
]),
element.text(state |> int.to_string |> element.text),
element.button([ event.on_click(dispatch(Incr)) ], [
element.text("+")
])
])
}
let app = lustre.simple(init, update, render)
lustre.start(app, "#root")
}
pub fn start(app: App(a, b), selector: String) -> Result(
fn(b) -> Nil,
Error,
)
Once you have created a app with either basic
or application
, you
need to actually start it! This function will mount your app to the DOM
node that matches the query selector you provide.
If everything mounted OK, we’ll get back a dispatch function that you can call to send actions to your app and trigger an update.
import lustre
pub fn main () {
let app = lustre.appliation(init, update, render)
assert Ok(dispatch) = lustre.start(app, "#root")
dispatch(Incr)
dispatch(Incr)
dispatch(Incr)
}