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 model. 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 |
                                      |        |
                                      +--------+
                                        ^    |
                                        |    | 
                                    Msg |    | #(Model, Cmd(Msg))
                                        |    |
                                        |    v
+------+                      +------------------------+
|      |  #(Model, Cmd(Msg))  |                        |
| init |--------------------->|     Lustre Runtime     |
|      |                      |                        |
+------+                      +------------------------+
                                        ^    |
                                        |    |
                                    Msg |    | Model
                                        |    |
                                        |    v
                                      +--------+
                                      |        |
                                      | render |
                                      |        |
                                      +--------+

Someone please PR the Gleam docs generator to fix the monospace font, thanks! 💖

pub opaque type App(model, msg)
pub type Error {
  ElementNotFound
}

Constructors

  • ElementNotFound

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 updates. 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 (model, msg) {
     case msg {
       Tick -> #(model + 1, tick())
     }
   }

   let render = fn (model) {
     element.div([], [
       element.text("Time elapsed: ")
       element.text(int.to_string(model))
     ])
   }
   
   let app = lustre.simple(init, update, render)
   assert Ok(_) = lustre.start(app, "#root")   
 }
 
 fn tick () -> Cmd(Msg) {
   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!") 
    ])
  )

  assert Ok(_) = 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 model used throughout your app. If that’s the case or if you know you need some global model 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 model.

import gleam/int
import lustre
import lustre/element
import lustre/event

type Msg {
  Decr
  Incr
}

pub fn main () {
  let init = 0

  let update = fn (model, msg) {
    case msg {
      Decr -> model - 1
      Incr -> model + 1
    }
  }

  let render = fn (model) {
    element.div([], [
      element.button([ event.on_click(Decr) ], [
        element.text("-")
      ]),

      element.text(int.to_string(model)),

      element.button([ event.on_click(Incr) ], [
        element.text("+")
      ])
    ])
  }

  let app = lustre.simple(init, update, render)
  assert Ok(_) = 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)
 }

This may not seem super useful at first, but by returning this dispatch function from your main (or elsewhere) you can get events into your Lustre app from the outside world.

Search Document