### Lustre Hello World Project Setup
Source: https://github.com/lustre-labs/lustre/blob/main/pages/reference/for-elm-devs.md
This is a basic 'Hello, world' example for setting up a new project in Lustre. It shows how to start a simple Lustre application.
```gleam
import lustre
import lustre/element/html
pub fn main() {
let app = lustre.element(html.h1([], [html.text("Hello, world")]))
let assert Ok(_) = lustre.start(app, "#app", Nil)
Nil
}
```
--------------------------------
### Elm Hello World Project Setup
Source: https://github.com/lustre-labs/lustre/blob/main/pages/reference/for-elm-devs.md
This is a basic 'Hello, world' example for setting up a new project in Elm. It demonstrates the minimal code required to get started.
```elm
module Main exposing (main)
import Html
main =
Html.text "Hello, world"
```
--------------------------------
### Setup New Project: Lustre Example
Source: https://github.com/lustre-labs/lustre/blob/main/pages/reference/for-liveview-devs.md
Example of setting up a new Lustre project with a basic 'Hello, Joe' element.
```gleam
```gleam
// main.gleam
import lustre
import lustre/element/html
pub fn main() {
let app = lustre.element(html.h1([], [html.text("Hello, Joe")]))
let assert Ok(_) = lustre.start(app, "#app", Nil)
Nil
}
```
```
--------------------------------
### Setup New Project: LiveView Example
Source: https://github.com/lustre-labs/lustre/blob/main/pages/reference/for-liveview-devs.md
Example of setting up a new Phoenix LiveView project with a basic 'Hello, Joe' LiveView.
```elixir
```elixir
# lib/my_app_web/live/hello_live.ex
defmodule MyAppWeb.HelloLive do
use MyAppWeb, :live_view
def render(assigns) do
~H"""
Hello, Joe
"""
end
def mount(_params, _session, socket) do
{:ok, socket}
end
end
```
```
--------------------------------
### Lustre Server Component Setup
Source: https://github.com/lustre-labs/lustre/blob/main/pages/reference/for-liveview-devs.md
Configure and start a Lustre server component. This involves defining the component's init, update, and view functions and then using `lustre.start_server_component` to initiate it.
```gleam
// Server component setup
let component = ServerComponent(init, update, view)
lustre.start_server_component(component, req, Nil)
```
--------------------------------
### Initiating an HTTP GET Request on Application Start
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/03-side-effects.md
Demonstrates how to initiate an HTTP GET request in the `init` function using `rsvp.get` to fetch an IP address. The response is handled by `ApiReturnedIpAddress` message.
```gleam
fn init(_flags) {
let model = Model(loading: True, ip: None)
let get_ip = rsvp.get(
"https://api.ipify.org",
rsvp.expect_text(ApiReturnedIpAddress)
)
#(model, get_ip)
}
```
--------------------------------
### Manage State: LiveView Example
Source: https://github.com/lustre-labs/lustre/blob/main/pages/reference/for-liveview-devs.md
Example of managing state in LiveView using assigns and handle_event.
```elixir
```elixir
def mount(_params, _session, socket) do
socket = assign(socket, :value, 1)
{:ok, socket}
end
def handle_event("increment", _params, socket) do
{:noreply, update(socket, :value, &(&1 + 1))}
end
def handle_event("decrement", _params, socket) do
{:noreply, update(socket, :value, &(&1 - 1))}
end
```
```
--------------------------------
### Setup React Hello World Project
Source: https://github.com/lustre-labs/lustre/blob/main/pages/reference/for-react-devs.md
Illustrates the basic setup for a barebones React project using Vite and JSX.
```jsx
// src/index.js
import { createRoot } from "react-dom/client";
const root = createRoot(document.getElementById("app"));
root.render(
Hello, world
);
```
--------------------------------
### LiveView Data Fetching in Mount and handle_info
Source: https://github.com/lustre-labs/lustre/blob/main/pages/reference/for-liveview-devs.md
Fetch initial data in LiveView's mount callback and handle asynchronous operations or subsequent data fetches using handle_info. This example shows starting an async operation and updating the socket state.
```elixir
def mount(_params, _session, socket) do
if connected?(socket) do
# Start async operation like Phoenix PubSub subscription or HTTP request
send(self(), :fetch_data)
end
{:ok, assign(socket, :loading, true)}
end
def handle_info(:fetch_data, socket) do
# Fetch data and update socket
{:noreply, assign(socket, :loading, false, :data, fetch_data())}
end
```
--------------------------------
### Render HTML: LiveView Example
Source: https://github.com/lustre-labs/lustre/blob/main/pages/reference/for-liveview-devs.md
Example of rendering an HTML button in LiveView using HEEx templates.
```html
```html
```
```
--------------------------------
### Render HTML: Lustre Example
Source: https://github.com/lustre-labs/lustre/blob/main/pages/reference/for-liveview-devs.md
Example of rendering an HTML button in Lustre using function calls.
```gleam
```gleam
html.button([attribute.class("primary")], [html.text("Click me")])
```
```
--------------------------------
### Initialize Lustre Application
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/01-quickstart.md
Sets up the main Lustre application with initialization, update, and view functions. Starts the application and attaches it to a DOM element.
```gleam
import gleam/dynamic/decode
import gleam/int
import gleam/list
import lustre
import lustre/attribute
import lustre/effect.{type Effect}
import lustre/element.{type Element}
import lustre/element/html
import lustre/event
import rsvp
pub fn main() {
let app = lustre.application(init, update, view)
let assert Ok(_) = lustre.start(app, "#app", Nil)
Nil
}
```
--------------------------------
### Render Text: LiveView Interpolation Example
Source: https://github.com/lustre-labs/lustre/blob/main/pages/reference/for-liveview-devs.md
Example of rendering text with interpolated variables in LiveView.
```html
```html
Hello <%= @name %>
```
```
--------------------------------
### Manage State: Lustre Example
Source: https://github.com/lustre-labs/lustre/blob/main/pages/reference/for-liveview-devs.md
Example of managing state in Lustre using a Model type and an update function with a case statement.
```gleam
```gleam
type Model =
Int
fn init(_) -> Model {
0
}
type Message {
Incr
Decr
}
fn update(model: Model, message: Message) -> Model {
case message {
Incr -> model + 1
Decr -> model - 1
}
}
```
```
--------------------------------
### Render Text: LiveView Simple Example
Source: https://github.com/lustre-labs/lustre/blob/main/pages/reference/for-liveview-devs.md
Example of rendering plain text within a span in LiveView.
```html
```html
Hello
```
```
--------------------------------
### Example fly.toml Configuration
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/07-full-stack-deployments.md
A sample `fly.toml` file showing essential service and build configurations. Ensure `internal_port` matches your application's listening port.
```toml
app = "your-app-name"
[build]
dockerfile = "Dockerfile"
[http_service]
internal_port = 8080
force_https = true
auto_stop_machines = "stop"
auto_start_machines = true
min_machines_running = 0
processes = ["app"]
[[http_service.ports]]
port = 80
handlers = ["http"]
[[http_service.ports]]
port = 443
handlers = ["tls", "http"]
```
--------------------------------
### Handle Events: Lustre Input Event Example
Source: https://github.com/lustre-labs/lustre/blob/main/pages/reference/for-liveview-devs.md
Example of attaching an 'on_input' event handler to an input element in Lustre.
```gleam
```gleam
html.input([event.on_input(UpdateInput)], [])
```
```
--------------------------------
### Handle Events: LiveView Input Change Example
Source: https://github.com/lustre-labs/lustre/blob/main/pages/reference/for-liveview-devs.md
Example of binding a 'change' event to an input field in LiveView.
```html
```html
```
```
--------------------------------
### Run Lustre Dev Tools
Source: https://github.com/lustre-labs/lustre/blob/main/examples/01-basics/01-hello-world/README.md
Use Lustre's dev tools package to build and run the application. This command starts the development server.
```bash
gleam run -m lustre/dev start
```
--------------------------------
### Manage State with MVU in Lustre
Source: https://github.com/lustre-labs/lustre/blob/main/pages/reference/for-react-devs.md
Demonstrates state management in Lustre using the Model-View-Update (MVU) pattern with a counter example.
```gleam
type Model =
Int
fn init(_) -> Model {
0
}
type Message {
Incr
Decr
}
fn update(model: Model, message: Message) -> Model {
case message {
Incr -> model + 1
Decr -> model - 1
}
}
fn view(model: Model) -> Element(Message) {
html.div([], [
html.button([event.on_click(Decr)], [html.text("-")]),
html.span([], [html.text(int.to_string(model))]),
html.button([event.on_click(Incr)], [html.text("+")])
])
}
```
--------------------------------
### Render Text: Lustre Concatenation Example
Source: https://github.com/lustre-labs/lustre/blob/main/pages/reference/for-liveview-devs.md
Example of rendering concatenated text with a variable in Lustre.
```gleam
```gleam
html.span([], [
html.text("Hello " <> name),
])
```
```
--------------------------------
### Initialize Lustre Application
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/01-quickstart.md
Starts a Lustre application using the simple API with init, update, and view functions. Ensure the target element '#app' exists in your HTML.
```gleam
import gleam/int
import lustre
import lustre/element.{type Element}
import lustre/element/html
import lustre/event
pub fn main() {
let app = lustre.simple(init, update, view)
let assert Ok(_) = lustre.start(app, "#app", Nil)
Nil
}
```
--------------------------------
### Render Text: Lustre Simple Example
Source: https://github.com/lustre-labs/lustre/blob/main/pages/reference/for-liveview-devs.md
Example of rendering plain text within a span in Lustre using html.text.
```gleam
```gleam
html.span([], [
html.text("Hello"),
])
```
```
--------------------------------
### Lustre Project Configuration with TOML
Source: https://github.com/lustre-labs/lustre/blob/main/pages/announcements/2025-10-05.md
Configure Lustre project settings using the `gleam.toml` file. This example shows how to specify the Bun binary, enable minification, set the output directory, configure the development server host and API proxy, and include external stylesheets and scripts.
```toml
[tools.lustre.bin]
# Use the system-installed `bun` binary instead of downloading one automatically.
bun = "system"
[tools.lustre.build]
minify = true
# Build the application into our server's `priv/static` directory so it can be
# deployed together with the backend.
outdir = "../server/priv/static"
[tools.lustre.dev]
host = "0.0.0.0"
# Configure an API proxy to forward requests to our backend during development.
# This lets us avoid CORS issues while the frontend and backend are running on
# different ports.
proxy = { from = "/api", to = "http://localhost:3000/api" }
[tools.lustre.html]
# Include Bootstrap in our project for simple styling.
stylesheets = [{ href = "https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" }]
scripts = [{ src = "https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js" }]
```
--------------------------------
### Run the Gleam Server
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/06-full-stack-applications.md
Execute the compiled Gleam server application to start the web server and handle incoming requests.
```sh
cd ../server
gleam run
```
--------------------------------
### Handle Events: Lustre Decrement Button Example
Source: https://github.com/lustre-labs/lustre/blob/main/pages/reference/for-liveview-devs.md
Example of attaching an 'on_click' event handler to a button in Lustre.
```gleam
```gleam
html.button([event.on_click(Decr)], [html.text("-")])
```
```
--------------------------------
### Add lustre_portal to Project
Source: https://github.com/lustre-labs/lustre/blob/main/pages/announcements/2025-08-08.md
Install the `lustre_portal` package into your Lustre project using the Gleam package manager.
```sh
gleam add lustre_portal
```
--------------------------------
### Gleam Lustre Application Example
Source: https://github.com/lustre-labs/lustre/blob/main/README.md
This snippet demonstrates a basic Lustre application in Gleam, showcasing state management with increment/decrement buttons and displaying the count. It requires importing Lustre and its element modules.
```gleam
import gleam/int
import lustre
import lustre/element.{text}
import lustre/element/html.{div, button, p}
import lustre/event.{on_click}
pub fn main() {
let app = lustre.simple(init, update, view)
let assert Ok(_) = lustre.start(app, "#app", Nil)
Nil
}
fn init(_flags) {
0
}
type Message {
Incr
Decr
}
fn update(model, message) {
case message {
Incr -> model + 1
Decr -> model - 1
}
}
fn view(model) {
let count = int.to_string(model)
div([], [
button([on_click(Incr)], [text(" + ")]),
p([], [text(count)]),
button([on_click(Decr)], [text(" - ")])
])
}
```
--------------------------------
### Handle Events: Lustre Custom Event Example
Source: https://github.com/lustre-labs/lustre/blob/main/pages/reference/for-liveview-devs.md
Example of attaching a custom 'mousemove' event handler using the general 'on' function in Lustre.
```gleam
```gleam
html.div([event.on("mousemove", fn(event) {
// Parse event data and return a message
MouseMove(parse_coords(event))
})], [])
```
```
--------------------------------
### Add Server Dependencies
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/06-full-stack-applications.md
Install necessary Gleam packages for the server application, including web framework, HTTP, JSON, and database libraries.
```sh
cd ../server
gleam add gleam_erlang gleam_http gleam_json wisp mist lustre storail
```
--------------------------------
### GitHub Actions Workflow for Deploying to GitHub Pages
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/04-spa-deployments.md
Automates the deployment of a Lustre SPA to GitHub Pages. This workflow checks out the repository, sets up Gleam, installs dependencies, builds the app, and deploys it using GitHub Actions.
```yaml
name: Deploy to GitHub Pages
on:
push:
branches:
- main
jobs:
deploy:
name: Deploy to GitHub Pages
runs-on: ubuntu-latest
permissions:
contents: read
deployments: write
pages: write
id-token: write
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Set up Gleam
uses: erlef/setup-beam@v1
with:
otp-version: "28.0"
gleam-version: "1.12.0"
rebar3-version: "3"
- name: Install dependencies
run: gleam deps download
- name: Build app
run: gleam run -m lustre/dev build --minify --outdir=dist
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: "dist"
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
```
--------------------------------
### Add Dependencies for SSR Project
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/05-server-side-rendering.md
Installs necessary dependencies including Lustre, Mist, gleam_erlang, and gleam_http for a new Gleam project.
```sh
gleam new app && cd app && gleam add gleam_erlang gleam_http lustre mist
```
--------------------------------
### Add Lustre Development Tools to Gleam Project
Source: https://github.com/lustre-labs/lustre/blob/main/README.md
Install the development tools for Lustre, including a development server that automatically reloads the browser on file changes. This may require inotify-tools on Linux.
```sh
gleam add --dev lustre_dev_tools
```
--------------------------------
### Lustre Stateful Component Setup
Source: https://github.com/lustre-labs/lustre/blob/main/pages/reference/for-liveview-devs.md
Lustre supports stateful components using a standard MVU pattern. The `lustre.component` function initializes a component with its init, update, and view functions.
```gleam
pub fn counter_component() -> Component(CounterModel, CounterMessage, CounterEvent, props) {
lustre.component(counter_init, counter_update, counter_view, [])
}
```
--------------------------------
### Add Lustre to Gleam Project
Source: https://github.com/lustre-labs/lustre/blob/main/README.md
Install the Lustre library into your Gleam project using the Gleam package manager.
```sh
gleam add lustre
```
--------------------------------
### Prerendering a Counter Component with Declarative Shadow DOM
Source: https://github.com/lustre-labs/lustre/blob/main/pages/announcements/2026-02-16.md
This example shows how to use component.prerender to generate declarative shadow DOM for a component, enabling server-side rendering and hydration.
```html
The count is: 10
```
--------------------------------
### Add Client Dependencies and Shared Package
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/06-full-stack-applications.md
Installs necessary Gleam packages for the client application and adds the shared package as a local dependency.
```sh
cd ../client
gleam add lustre rsvp gleam_json gleam_http plinth
gleam add lustre_dev_tools --dev
```
--------------------------------
### Controlled Input Example
Source: https://github.com/lustre-labs/lustre/blob/main/pages/hints/controlled-vs-uncontrolled-inputs.md
Demonstrates a controlled input where the value is bound to the model state and changes are handled via an event.
```gleam
html.input([
// The value comes from your model
attribute.value(model),
// Changes update your model via a message
event.on_input(UserUpdatedName),
// Other attributes...
])
```
--------------------------------
### Define Model and Initial State
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/01-quickstart.md
Defines the application's data model and the initial state, including a list to hold cat data. Initializes effects to none for the starting state.
```gleam
type Model {
Model(total: Int, cats: List(Cat))
}
type Cat {
Cat(id: String, url: String)
}
fn init(_args) -> #(Model, Effect(Message)) {
let model = Model(total: 0, cats: [])
#(model, effect.none())
}
```
--------------------------------
### Lustre Counter View Function
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/05-server-side-rendering.md
This is the standard view function for the counter example, which takes an integer model and returns a Lustre Element. It's used to demonstrate how a pure view function can be leveraged for server-side rendering.
```gleam
pub fn view(model: Int) -> Element(Message) {
let count = int.to_string(model)
html.div([], [
html.button([event.on_click(Decr)], [html.text("-")]),
html.button([event.on_click(Incr)], [html.text("+")]),
html.p([], [html.text("Count: " <> count)])
])
}
```
--------------------------------
### Create a Fly.io Application
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/07-full-stack-deployments.md
Initializes a new Fly.io application in your project root. Use this to generate the basic `fly.toml` configuration.
```sh
fly launch --no-deploy
```
--------------------------------
### Create New Gleam App and Add Lustre
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/01-quickstart.md
Initialize a new Gleam project and add Lustre as a dependency to begin building a web application.
```bash
gleam new app && cd app && gleam add lustre
```
--------------------------------
### Build Lustre SPA with Minification
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/04-spa-deployments.md
Builds a Lustre SPA using lustre_dev_tools, minifying the output for production. Assumes the output will be in the 'priv/static' directory.
```bash
gleam run -m lustre/dev build --minify
```
--------------------------------
### Bundle Client App and Serve Static Assets
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/06-full-stack-applications.md
Build the client-side JavaScript bundle and place it in the server's static directory. This command prepares the client assets for the server to serve.
```sh
cd ../client
gleam run -m lustre/dev build --outdir=../server/priv/static
```
--------------------------------
### Handle Events: LiveView Decrement Button Example
Source: https://github.com/lustre-labs/lustre/blob/main/pages/reference/for-liveview-devs.md
Example of binding a 'decrement' click event to a button in LiveView.
```html
```html
```
```
--------------------------------
### Set up Mist Server and Request Handler
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/05-server-side-rendering.md
Initializes a Mist server to handle requests. It defines a handler for '/greet/:name' routes and a fallback for 404 Not Found responses. The server listens on port 3000.
```gleam
pub fn main() {
let empty_body = mist.Bytes(bytes_tree.new())
let not_found = response.set_body(response.new(404), empty_body)
let assert Ok(_) =
fn(req: Request(Connection)) -> Response(ResponseData) {
case request.path_segments(req) {
["greet", name] -> greet(name)
_ -> not_found
}
}
|> mist.new
|> mist.port(3000)
|> mist.start
process.sleep_forever()
}
```
--------------------------------
### Create Basic Lustre 'Hello, world!' Element
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/01-quickstart.md
Replace the default module in src/app.gleam with this code to render 'Hello, world!' using Lustre's element API.
```gleam
import lustre
import lustre/element/html
pub fn main() {
let app = lustre.element(html.text("Hello, world!"))
let assert Ok(_) = lustre.start(app, "#app", Nil)
Nil
}
```
--------------------------------
### Handle Events: LiveView Custom Hook Example
Source: https://github.com/lustre-labs/lustre/blob/main/pages/reference/for-liveview-devs.md
Example of using a JavaScript hook for custom events like 'mousemove' in LiveView.
```javascript
```js
let Hooks = {};
Hooks.MouseMove = {
mounted() {
this.el.addEventListener("mousemove", e => {
this.pushEvent("mouse-move", {x: e.clientX, y: e.clientY});
})
}
}
```
```
--------------------------------
### Manually Deploy Application to Fly.io
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/07-full-stack-deployments.md
Deploys your application to Fly.io by building the Docker image, uploading it, and then deploying. Access your app at `https://your-app-name.fly.dev` after completion.
```sh
fly deploy
```
--------------------------------
### Render HTML Button in Elm
Source: https://github.com/lustre-labs/lustre/blob/main/pages/reference/for-elm-devs.md
Example of rendering an HTML button with a class attribute and an onClick event in Elm.
```elm
Html.button
[ Html.Attributes.class "primary"
, Html.Events.onClick ButtonClicked
]
[ Html.text "Click me" ]
```
--------------------------------
### Build Minified JavaScript Bundle for Production
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/06-full-stack-applications.md
Uses the Lustre build tool with the `--minify` flag to create a production-ready, minified JavaScript bundle. The `--outdir` flag specifies the output directory for the bundled file.
```sh
```sh
cd ../client
gleam run -m lustre/dev build --minify --outdir=../server/priv/static
```
```
--------------------------------
### Manage State with useState in React
Source: https://github.com/lustre-labs/lustre/blob/main/pages/reference/for-react-devs.md
Provides an example of a React counter component using the `useState` hook for state management.
```jsx
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
{count}
);
}
```
--------------------------------
### Render Text in Lustre
Source: https://github.com/lustre-labs/lustre/blob/main/pages/reference/for-elm-devs.md
Example of rendering text content within a span element in Lustre, using string concatenation.
```gleam
html.span([], [
html.text("Hello, " <> name),
])
```
--------------------------------
### Gleam Server Implementation
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/06-full-stack-applications.md
The main server logic in `src/server.gleam`, setting up the database, configuring the web framework, and defining request handlers for API endpoints and static asset serving.
```gleam
import gleam/dynamic/decode
import gleam/erlang/process
import gleam/http.{Get, Post}
import gleam/json
import gleam/result
import lustre/attribute
import lustre/element
import lustre/element/html
import mist
import storail
import wisp.{type Request, type Response}
import wisp/wisp_mist
import shared/groceries.{type GroceryItem}
pub fn main() {
wisp.configure_logger()
let secret_key_base = wisp.random_string(64)
// Set up our database
let assert Ok(db) = setup_database()
let assert Ok(priv_directory) = wisp.priv_directory("server")
let static_directory = priv_directory <> "/static"
let assert Ok(_) =
handle_request(db, static_directory, _)
|> wisp_mist.handler(secret_key_base)
|> mist.new
|> mist.port(3000)
|> mist.start
process.sleep_forever()
}
// REQUEST HANDLERS ------------------------------------------------------------
fn app_middleware(
req: Request,
static_directory: String,
next: fn(Request) -> Response,
) -> Response {
let req = wisp.method_override(req)
use <- wisp.log_request(req)
use <- wisp.rescue_crashes
use req <- wisp.handle_head(req)
use <- wisp.serve_static(req, under: "/static", from: static_directory)
next(req)
}
fn handle_request(
db: storail.Collection(List(GroceryItem)),
static_directory: String,
req: Request,
) -> Response {
use req <- app_middleware(req, static_directory)
case req.method, wisp.path_segments(req) {
// API endpoint for saving grocery lists
Post, ["api", "groceries"] -> handle_save_groceries(db, req)
// Everything else gets our HTML with hydration data
Get, _ -> serve_index(db)
// Fallback for other methods/paths
_, _ -> wisp.not_found()
}
}
fn serve_index(db: storail.Collection(List(GroceryItem))) -> Response {
let html =
html.html([], [
html.head([], [
html.title([], "Grocery List"),
html.script(
[attribute.type_("module"), attribute.src("/static/client.js")],
"",
),
]),
html.body([], [html.div([attribute.id("app")], [])]),
])
html
|> element.to_document_string
|> wisp.html_response(200)
}
fn handle_save_groceries(
db: storail.Collection(List(GroceryItem)),
req: Request,
) -> Response {
use json <- wisp.require_json(req)
case decode.run(json, groceries.grocery_list_decoder()) {
Ok(items) ->
case save_items_to_db(db, items) {
Ok(_) -> wisp.ok()
Error(_) -> wisp.internal_server_error()
}
Error(_) -> wisp.bad_request("Request failed")
}
}
// DATABASE --------------------------------------------------------------------
fn setup_database() -> Result(storail.Collection(List(GroceryItem)), Nil) {
let config = storail.Config(storage_path: "./data")
let items =
storail.Collection(
name: "grocery_list",
to_json: groceries.grocery_list_to_json,
decoder: groceries.grocery_list_decoder(),
config:,
)
Ok(items)
}
fn grocery_list_key(
db: storail.Collection(List(GroceryItem)),
) -> storail.Key(List(GroceryItem)) {
// In a real application, you would probably store items as individual
// documents, or use a database like PostgreSQL instead.
storail.key(db, "grocery_list")
}
fn save_items_to_db(
db: storail.Collection(List(GroceryItem)),
items: List(GroceryItem),
) -> Result(Nil, storail.StorailError) {
storail.write(grocery_list_key(db), items)
}
```
--------------------------------
### Render Text in Elm
Source: https://github.com/lustre-labs/lustre/blob/main/pages/reference/for-elm-devs.md
Example of rendering text content within a span element in Elm, including string concatenation.
```elm
Html.span [] [ Html.text <| "Hello, " ++ name ]
```
--------------------------------
### Lustre Application Constructor Signatures
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/03-side-effects.md
Compares the signatures for `lustre.simple` and `lustre.application`, highlighting the return type of `#(model, Effect(message))` for the latter.
```gleam
pub fn simple(
init: fn(flags) -> model,
update: fn(model, message) -> model,
view: fn(model) -> Element(message),
) -> App(flags, model, message)
pub fn application(
init: fn(flags) -> #(model, Effect(message)),
update: fn(model, message) -> #(model, Effect(message)),
view: fn(model) -> Element(message),
) -> App(flags, model, message)
```
--------------------------------
### Render HTML Button in Lustre
Source: https://github.com/lustre-labs/lustre/blob/main/pages/reference/for-elm-devs.md
Example of rendering an HTML button with a class attribute and an onClick event in Lustre, using the lustre/element/html module.
```gleam
html.button([attribute.class("primary"), event.on_click(ButtonClicked)], [
html.text("Click me")
])
```
--------------------------------
### Simulate Lustre Application Logic
Source: https://github.com/lustre-labs/lustre/blob/main/pages/announcements/2025-05-18.md
Use this to simulate a running Lustre application by passing your init, update, and view functions. It allows simulating events, interrupting the simulation to assert view state, and simulating messages from external systems. Effects are not simulated.
```gleam
import app
import app/user.{User}
import lustre/dev/simulate
import lustre/dev/query
import lustre/element
pub fn user_login_test() {
let form = query.element(query.test_id("login-form"))
let app =
simulate.application(app.init, app.update, app.view)
|> simulate.start(Nil)
|> simulate.submit(on: query.element(form), fields: [
#("email", json.string("lucy@gleam.run")),
#("password", json.string("strawberry")),
])
let assert Ok(_) =
query.find(
in: simulate.view(app),
matching: query.element(matching: query.text("Loading...")),
)
as "Should show loading state while logging in"
let response = Ok(User(name: "Lucy", email: "lucy@gleam.run", role: "mascot"))
app
|> simulate.message(ApiReturnedSession(response))
|> simulate.view
|> element.to_readable_string
|> birdie.snap("The dashboard after logging in")
}
```
--------------------------------
### Initial Message Definition
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/02-state-management.md
An example of a muddled message definition before applying the Subject Verb Object pattern. This can become problematic as applications grow.
```gleam
type Message {
SetPassword(String)
ResetPassword
PasswordReset(Result(Nil, String))
}
```
--------------------------------
### Get PORT Environment Variable
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/07-full-stack-deployments.md
Retrieves the PORT environment variable, attempting to parse it as an integer. It defaults to 8080 if the variable is not set or cannot be parsed.
```Gleam
// In server.gleam
fn get_port() -> Int {
case envoy.get("PORT") {
Ok(port) -> {
case int.parse(port) {
Ok(port_number) -> port_number
Error(_) -> 8080 // Default if PORT cannot be parsed as an int
}
}
Error(_) -> 3000 // Default if PORT is not set (e.g. during `gleam run`)
}
}
```
--------------------------------
### Get HOST Environment Variable
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/07-full-stack-deployments.md
Retrieves the HOST environment variable, defaulting to 'localhost' if not set. This is used for server configuration in production environments.
```Gleam
// In server.gleam
fn get_host() -> String {
case envoy.get("HOST") {
Ok(host) -> host
Error(_) -> "localhost" // Default if HOST is not set
}
}
```
--------------------------------
### Create Monorepo and Gleam Projects
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/06-full-stack-applications.md
Initializes a new monorepo for a full stack application and creates separate Gleam projects for the client, server, and shared code.
```sh
mkdir lustre-fullstack-guide \
&& cd lustre-fullstack-guide \
&& gleam new client \
&& gleam new server \
&& gleam new shared
```
--------------------------------
### Optimize Render Performance with element.memo
Source: https://github.com/lustre-labs/lustre/blob/main/pages/announcements/2026-01-10.md
Use element.memo to hint to the runtime when rendering can be skipped. It requires a list of dependencies constructed with element.ref and a view function that takes no arguments. Dependencies are checked for reference equality.
```diff
fn view_entry(entry: Entry) {
+ use <- element.memo([element.ref(entry)])
let Entry(description:, id:, completed:, editing:, ..) = entry
html.li(
[attribute.classes([#("completed", completed), #("editing", editing)])],
[...],
)
}
```
--------------------------------
### Pure Function Example in Gleam
Source: https://github.com/lustre-labs/lustre/blob/main/pages/hints/pure-functions.md
A simple Gleam function demonstrating purity. It takes an input and returns an output solely based on that input, without external side effects.
```gleam
fn f(x) {
3 * x
}
```
--------------------------------
### Import Server-Side Rendering Modules
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/05-server-side-rendering.md
Imports modules required for handling HTTP requests and rendering HTML with Lustre on the server. Does not import from the main 'lustre' module as no runtime is needed.
```gleam
import gleam/bytes_tree
import gleam/erlang/process
import gleam/http/request.{type Request}
import gleam/http/response.{type Response}
import lustre/element
import lustre/element/html.{html}
import mist.{type Connection, type ResponseData}
```
--------------------------------
### Declarative Event Handling with `gleam/dynamic/decode`
Source: https://github.com/lustre-labs/lustre/blob/main/pages/announcements/2025-04-19.md
Migrate imperative event handlers to a declarative approach using `gleam/dynamic/decode`. This example shows how to extract and decode event data within the handler definition.
```gleam
// Before:
event.on("mousemove", fn(event) {
use x <- result.try(dynamic.field("offsetX", dynamic.int)(event))
use y <- result.try(dynamic.field("offsetY", dynamic.int)(event))
Ok(handle_mousemove(x, y))
})
// After:
event.on("mousemove", {
use x <- decode.field("offsetX", decode.int)
use y <- decode.field("offsetY", decode.int)
decode.success(handle_mousemove(x, y))
})
```
--------------------------------
### Dockerfile for Lustre Full-Stack Application
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/07-full-stack-deployments.md
Defines the build and runtime stages for a Lustre full-stack application using a multi-stage Docker build. It compiles both client and server code and sets up the runtime environment.
```Dockerfile
ARG GLEAM_VERSION=v1.12.0
# Build stage - compile the application
FROM ghcr.io/gleam-lang/gleam:${GLEAM_VERSION}-erlang-alpine AS builder
# Add project code
COPY ./shared /build/shared
COPY ./client /build/client
COPY ./server /build/server
# Install dependencies for all projects
RUN cd /build/shared && gleam deps download
RUN cd /build/client && gleam deps download
RUN cd /build/server && gleam deps download
# Compile the client code and output to server's static directory
RUN cd /build/client \
# Add this line if your project doesn't already have a dev dependency
# on `lustre_dev_tools` - otherwise, you can omit it
&& gleam add --dev lustre_dev_tools \
&& gleam run -m lustre/dev build --minify --outdir=../server/priv/static
# Compile the server code
RUN cd /build/server \
&& gleam export erlang-shipment
# Runtime stage - slim image with only what's needed to run
FROM ghcr.io/gleam-lang/gleam:${GLEAM_VERSION}-erlang-alpine
# Copy the compiled server code from the builder stage
COPY --from=builder /build/server/build/erlang-shipment /app
# Set up the entrypoint
WORKDIR /app
RUN echo -e '#!/bin/sh\nexec ./entrypoint.sh "$@"' > ./start.sh \
&& chmod +x ./start.sh
# Set environment variables
ENV HOST=0.0.0.0
ENV PORT=8080
# Expose the port the server will run on
EXPOSE $PORT
# Run the server
CMD ["./start.sh", "run"]
```
--------------------------------
### Define Counter Model and Init Function
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/01-quickstart.md
Defines the application's state as an integer and initializes it to 0. The init function accepts optional arguments which are ignored in this simple case.
```gleam
type Model =
Int
fn init(_args) -> Model {
0
}
```
--------------------------------
### Configure Server Dependencies in gleam.toml
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/06-full-stack-applications.md
Add the shared Gleam package as a dependency in the server's `gleam.toml` file to enable code sharing between client and server.
```toml
[dependencies]
shared = { path = "../shared" }
```
--------------------------------
### Keying list elements with `element.keyed`
Source: https://github.com/lustre-labs/lustre/blob/main/pages/hints/rendering-lists.md
Demonstrates how to use `element.keyed` to provide stable keys for list items, ensuring correct DOM updates and preventing visual issues.
```gleam
keyed.div([], list.map(model.cats, fn(cat) {
#(cat.id, html.img([attribute.src(cat.url)]))
}))
```
--------------------------------
### Greet Function for Server-Side Rendering
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/05-server-side-rendering.md
Generates an HTML response with a personalized greeting. It uses Lustre's element functions to construct the HTML and 'element.to_document_string' to render it.
```gleam
fn greet(name: String) -> Response(ResponseData) {
let res = response.new(200)
let html =
html([], [
html.head([], [html.title([], "Greetings!")]),
html.body([], [
html.h1([], [html.text("Hey there, " <> name <> "!")])
])
])
response.set_body(res,
html
|> element.to_document_string
|> bytes_tree.from_string
|> mist.Bytes
)
}
```
--------------------------------
### Configure Client as JavaScript Target
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/06-full-stack-applications.md
Sets the target for the client project to 'javascript' and adds the shared package as a local dependency in its gleam.toml.
```toml
[dependencies]
shared = { path = "../shared" }
name = "client"
version = "1.0.0"
target = "javascript"
```
--------------------------------
### Client-Side Hydration Logic
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/05-server-side-rendering.md
This client-side Gleam code shows how to read the initial model from a JSON script tag, decode it, and start a Lustre application. It includes fallback logic for cases where JSON decoding might fail.
```gleam
import gleam/dynamic
import gleam/json
import gleam/result
import lustre
import plinth/browser/document
import plinth/browser/element
pub fn main() {
let json =
document.query_selector("#model")
|> result.map(element.inner_text)
let flags =
case json.parse(json, decode.int) {
Ok(count) -> count
Error(_) -> 0
}
let app = lustre.application(init, update, view)
let assert Ok(_) = lustre.start(app, "#app", flags)
Nil
}
```
--------------------------------
### Render Text in Lustre
Source: https://github.com/lustre-labs/lustre/blob/main/pages/reference/for-react-devs.md
Shows how to render text and concatenated strings using `html.text` and the string concatenation operator in Lustre.
```gleam
html.div([], [html.text("Hello")])
html.div([], [html.text("Hello " <> name)])
```
--------------------------------
### Add Envoy Dependency to Server
Source: https://github.com/lustre-labs/lustre/blob/main/pages/guide/07-full-stack-deployments.md
Adds the 'envoy' Hex package to your server project for managing environment variables. Run this command in the 'server' directory.
```shell
cd server
gleam add envoy
```
--------------------------------
### Create Lustre View Functions
Source: https://github.com/lustre-labs/lustre/blob/main/pages/reference/for-react-devs.md
Create view functions that return elements. Use messages for event handling and pass data via function arguments.
```gleam
fn button(label: String, on_click: message) -> Element(message) {
html.button([event.on_click(on_click)], [html.text(label)])
}
fn view(_model: Model) -> Element(Message) {
html.div([], [
button("Click me", ButtonClicked)
])
}
```