Announcing LiveState 0.7
21 September 2023

Announcing LiveState 0.7

After teaching a training class and giving a well attended conference talk at ElixirConf, I’m more excited than ever about LiveState. Briefly, LiveState is a project to give developers a “LiveView like” developer experience when their site is not served by Phoenix or Elixir. In this release, I’ve focused on quality of life features. LiveState is in good shape feature wise, but we’d like to make the process of building an app with LiveState easier to do. To this end, we’ve added a couple new goodies.

Hold my hand

I’ve had a blog post and conference talks going into some detail on how to build things with LiveState, and that’s been enough for some users to get started building real apps, which is great. At the conference, I got to pair with a user just getting started with LiveState and the process of adding it to their app wasn’t as obvious as I thought it would be. To fix that, I spent a good amount of time writing an honest-to-goodness, step-by-step tutorial. While I’d certainly like to take it further and cover more scenarios of what you can build with LiveState, it should be enough to get going. As always, feedback welcomed.

Please write my code for me

In the process of writing the tutorials, I realized there was an opportunity to eliminate a few steps by writing some generators. There will probably be more to come, but for now I have two.

LiveState channel generator

To write a new channel (and optionally generate a socket for it to live in), you can do:

mix Todo

This will generate a channel that uses the LiveState.Channel behaviour and implements the init/3 and handle_event/3 callbacks. In the future, we’ll add an example test case as well.

LiveState element generator

This generator expects a channel topic and a custom element name like so:

mix live_state.gen.element ContactForm contact-form

It will create a custom element in app/js/contact-form.ts. The element created will use the Lit library and the decorators from phx-live-state. Using Lit was something I considered carefully because it gives the impression that LiveState is tied to Lit. It is not. You are absolutely able to use any library (or none at all) with LiveState. Custom elements are also completely optional, although I personally think they are a great choice for embedded app development. All that said, I wanted the generator to build something that was as easy to understand as possible, and so I chose Lit as a nice, ergonomic choice that still avoids framework lock-in.

Test helpers

Lastly, but definitely not leastly, I started on some test helpers for writing LiveState channel tests. Before, in order to write channel tests for LiveState channels, you essentially had to know the specific messages LiveState is sending, leading to code that looked like this:

  test "handle_event", %{socket: socket} do
    push(socket, "lvs_evt:add_todo", %{"description" => "Do the thing"})

    assert_push("state:change", %{
      state: %{todos: [%{"description" => "Do the thing"}]},
      version: 1

Now, by importing LiveState.TestHelpers, you can write tests that look like this:

  test "handle_event", %{socket: socket} do
    send_event(socket, "add_todo", %{"description" => "Do the thing"})
    assert_state_change %{todos: [%{"description" => "Do the thing"}]}

Less code, and less implementation details of LiveState in your channel tests. There is certainly more to do along these lines, but hopefully this is a good start.

What’s next?

At this point, I’m fairly happy with where the core of LiveState is at. We’re using it on real projects, and seeing some other folks do so too. I do have same crazyish ideas I’d like to try out, but I’ll probably wait to share those for a future post. Mostly though, features will come out of real users using LiveState in anger (but hopefully not being angry about it). If that’s you, we’d love to hear how it’s going!

Related Posts

Want to learn more about the work we do?

Explore our work

Ready to start your software journey with us?

Contact Us