---
title: "Firelight.Elmish"
description: "Run an Elmish Model-View-Update loop inside a Lit web component: one immutable model, one pure update function."
---

# Firelight.Elmish

Run the Model-View-Update loop inside a component: one immutable model, one pure `update` function.

- [Elmish docs](https://elmish.github.io/elmish/)
- [Lit docs: Reactive controllers](https://lit.dev/docs/composition/controllers/)
- [NuGet 0.3.0](https://www.nuget.org/packages/Firelight.Elmish/0.3.0)
- [Source](https://github.com/roboz0r/Firelight/tree/main/src/Firelight.Elmish)

## Why use it

As a component's state grows, mutable fields changed from different event handlers get hard to
follow. Elmish keeps all of the state in one immutable model, and every change goes through a single
`update` function: given a message and the current model, it returns the next model.
That function is plain F#, with no DOM or Lit in it, so it's easy to read and to test.

`ElmishController` connects the loop to a Lit component. It's a
[reactive controller](https://lit.dev/docs/composition/controllers/): `init` runs when the
component is created, and each new model renders the component. Use
`ElmishController.withCmds` for side effects such as HTTP requests, or pass a full
Elmish `Program` for subscriptions and tracing.

## Example

Because every model is an immutable value, undo is just a list of the earlier ones. Nothing in the
component has to know how to reverse a change.

```fsharp
module Snippets.UndoCounter

open Fable.Core
open Firelight
open Firelight.Elmish
open type Firelight.Lit

// The whole state is one immutable value, so undo is just keeping the earlier values.
type Model = { Count: int; History: int list }

type Msg =
    | Increment
    | Decrement
    | Undo

let init () = { Count = 0; History = [] }

let update msg model =
    match msg with
    | Increment ->
        {
            Count = model.Count + 1
            History = model.Count :: model.History
        }
    | Decrement ->
        {
            Count = model.Count - 1
            History = model.Count :: model.History
        }
    | Undo ->
        match model.History with
        | previous :: older -> { Count = previous; History = older }
        | [] -> model

[<AttachMembers>]
type UndoCounter() as this =
    inherit LitElement()

    let elmish = ElmishController.simple this init update

    static member styles =
        css
            $$"""
        :host { display: inline-flex; align-items: center; gap: 0.75rem; }
        button { font: inherit; padding: 0.4rem 0.9rem; cursor: pointer; }
        output { min-width: 2ch; text-align: center; font-weight: 600; }
        """

    override _.render() =
        let model = elmish.model

        html
            $"""
        <button @click={fun _ -> elmish.dispatch Decrement}>−</button>
        <output>{model.Count}</output>
        <button @click={fun _ -> elmish.dispatch Increment}>+</button>
        <button ?disabled={model.History.IsEmpty} @click={fun _ -> elmish.dispatch Undo}>Undo</button>"""

defineElement<UndoCounter> "my-undo-counter"
```

```html
<my-undo-counter></my-undo-counter>
```

For a larger example, the [Todo demo](https://roboz0r.github.io/Firelight/demos/todo/) runs one Elmish loop and shares its state with each item through context.

## Install

```sh
dotnet add package Firelight.Elmish
```
