Lissome is a library to integrate the Gleam frontend framework Lustre with Phoenix Live View.

[!WARNING] This project is on early stage of development and breaking changes are expected.

Setup

First, make sure you have the Gleam compiler installed. Instructions can be found here

  1. We will use a tool called mix_gleam to manage a Gleam project with Mix. Follow the instructions to setup it.

  2. Add lissome to your mix.exs file:

def deps do
  [
    ...,
    {:lissome, "~> 0.2.0"},
  ]
end
  1. Create a gleam.toml file with your Gleam dependencies:
name = "your_app_name"

[dependencies]
gleam_stdlib = ">= 0.44.0 and < 2.0.0"
lustre = ">= 4.6.3 and < 5.0.0"

[dev-dependencies]
gleeunit = ">= 1.0.0 and < 2.0.0"
  1. Run:
mix deps.get
gleam deps download

[!NOTE] Although mix_gleam is able to install Gleam dependencies, it doesn't manages dependencies well outside Erlang target. For that reason, we usea gleam.toml file and the Gleam tooling to manage Gleam dependencies.

Usage

To render a Lustre app, we need to define a Gleam module that contains a public main function. This function must start a Lustre app created with the lustre.simple function.

//// src/hello.gleam

pub fn init() {
  ...
}

pub fn update(msg, model) {
  ...
}

pub fn view(model) {
  ...
}

pub fn main() {
  let app = lustre.simple(init, update, view)
  let assert Ok(_) = lustre.start(app, "#app", Nil)

  Nil
}

Now, inside HEEX we can render it using the lustre component:

defmodule MyApp.MyLiveView do
  use MyApp, :live_view

  import Lissome.Component

  def mount(_params, _session, socket) do
    {:ok, socket}
  end

  def render(assigns) do
    ~H"""
    <div>
      <div>Content rendered with Phoenix Live View</div>
      <div>
        <.lustre id="app" name="hello" />
      </div>
    </div>
    """
  end
end

Check out the project in the example directory for a complete code example.

SSR

Thanks to the ability of Gleam to compile to both Erlang and JavaScript, we can do server-side rendering of Lustre without having to install Node.js. This is why Lissome has SSR enabled by default, but you can disable it by passing ssr={false} to the lustre component.

Keep in mind that Lissome will call the init and the view functions of your Gleam module in order to render the initial HTML. By default Lissome will look for functions with that name in your module. If you happen to named them differently, you can pass to the lustre component init_fn with the name of your function responsible for initializing the model and view_fn with the name of your function responsible for rendering the view. Also, both functions must be public.

<.lustre
  id="app"
  name="hello"
  init_fn="my_init_function"
  view_fn="my_view_function"
/>

Use cases

In my experience, many part of the UI in Phoenix apps could be done with Phoenix LiveView; even for parts that are heavily interactive we have ways to build components like modals using JS commands and perform optimistic updates by combining JS commands and Tailwind CSS classes.

However, there are still parts of the UI that are very complex to implement in Phoenix LiveView, often because they have a non-trivial client-side state.

In that situations, we could use Gleam and its fronted framework, Lustre, as they are well suited to handle that kind of state in a simple way. Also, Gleam has interopability with Elixir and both shares many concepts, such as immutability, functional paradigms and pattern matching.

Lissome is designed to address only the gaps of Phoenix LiveView when it comes to client-side state.

Roadmap

  • [ ] Improvements to the SSR.
  • [ ] Gleam's helpers for communicating with Phoenix LiveView and supporting Lustre's effects.
  • [ ] Hot module replacement for Gleam.
  • [ ] Support for LiveJson.
  • [ ] Support for Lustre's server components.