README.md

# off_topic

[![Package Version](https://img.shields.io/hexpm/v/off_topic)](https://hex.pm/packages/off_topic)
[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/off_topic/)

This is an experimtal (in design not implementation) [Lustre](https://lustre.build) runtime extension,
adding  so-called subscriptions. Subscriptions are at their core effects with a
cleanup function. If you know React, Vue, or Svelte, you can think of them as
`useEffect` or `watch` calls. `off_topic` lets you define your active subscriptions
in a declarative way, using a familiar diffing mechanism to figure out which effects
to start, and which cleanup functions to run.

It also comes with a comprehensive library of built-in effects and subscriptions and
a vast collection of little demo apps showing how to work with them. Built-in
subscriptions range from simple timers to browser events to SSE and WebSockets.

Almost all subscriptions and effects in `off_topic` work both with client _and_ server-
components using a small (~3kb min+gzip), extensible runtime custom-element
in the browser.

```sh
gleam add off_topic
```

## Usage

`off_topic.application` is a drop-in for `lustre.application` that adds a
`subscriptions` callback. off_topic calls it after every update, starting new
subscriptions and stopping removed ones automatically.

```gleam
import lustre
import off_topic.{type Subscription}

pub fn main() {
  let app = off_topic.application(init:, update:, subscriptions:, view:)
  let assert Ok(_) = lustre.start(app, "#app", Nil)
  Nil
}

fn subscriptions(model: Model) -> Subscription(Msg) {
  off_topic.batch([
    // always listen for the page state / visibility
    off_topic.page_state(PageStateChanged),
    case model.page_state {
      // while the page is focused, run a 1 second interval
      off_topic.Active ->
        off_topic.every(every: duration.seconds(1), immediate: False, on_elapsed: Tick)
      // when the page is no longer active, returning off_topic.none()
      // causes the runtime to clean up the interval automatically.
      _ -> off_topic.none()
    },
  ])
}
```

The module documentation contains many more examples and their source code for you
to play with!

There's also some guides on the left that go into more detail.

## Documentation

Full API reference and guides at <https://hexdocs.pm/off_topic>.