A BIG thanks to the LiveView team for introducing managed lists (streams)

As per the headline, I hereby use the opportunity to thank the LiveView team for finally introducing (as of v0.18.12) the long awaited mechanism for managing the lists - streams.

So far, the only way to do it was with client-side hooks or (far better) Alpine, but still, component-based nested lists kept on having bugs I hope the stream mechanism overcomes completely (as in by design).

Crossing fingers it all works out of the box.


For those of us not in the know is this the


Functionality or something else ?


Yes it is.

The :at option may be provided to insert or update an item to a particular index in the collection on the client.

From the example I get this is the 0-based index, simply counting rows.

As far as you know is there the option to insert ‘after id 1234’?

Judging by the documentation, it’s gonna be your code that provides the index and if the relative reference is an id, then it means you’ll probably end up maintain an id => index map yourself, but that’s not a big deal in comparison to what was needed before this (the temporary assigns, JS sorting code, etc.)

Does anyone know if streams only work in live views and not in live components?

I’m getting the error: lifecycle hooks are not supported on stateful components. when trying to use stream in the update function of a live component.

1 Like

Not yet, but if true it also means there’s no support for nested lists (streams) which in turns means the deficiency resulting from the notorious bug I reported last April may have not been addressed as of yet: When in list, all but first two components get unnecessarily re-rendered on their children update · Issue #2000 · phoenixframework/phoenix_live_view · GitHub

What do the authors say @josevalim?



0.18.16 (2023-02-23)


  • Support streams in Live Components
  • Optimize plug error translation when a Plug.Exception is raised over connected LiveView
1 Like

That’s awesome! Glad to see support for streams in live components. I’ll test this out soon.

But for now onto my next issue with streams :sweat_smile: It looks like it’s not possible to reset an entire stream.

My use case is a chat app which has the typical layout of a list of conversations on the left and when you click conversation it loads all the messages on the right. Because I couldn’t use streams in a live component I was just using a live view. On mount I would create a stream for the messages and then in handle params I tried to reset the stream with stream(socket, :messages, get_messages[new_conversation_id]) but I get the error existing hook :messages already attached on :after_render.

True, there’s no delete_all and the docs states “Assigns a NEW stream to the socket”.

Now, this is just me thinking aloud, but maybe try removing the assign manually (as in: assign( socket, :name, nil)) and then setting the stream anew OR now that live components support streams, simply reinitialize the entire live component (replace it in its parent template with a new instance of the same component).

A post was split to a new topic: How to handle stream with pagination implementation like scrivener, flop?

Is there any way to keep the stream at a fixed size? I’m thinking about the use case of streaming some logs, where the server doesn’t want to keep the logs in the live view gen server, but you don’t want the client to have the logs keep growing, forever.

Why don’t you simply delete the first while inserting at the end or vice versa?

Now that I’ve migrated completely, I found myself with the same problem. The short answer is you can’t, at least not in the v0.18.18.

However, judging by the most recent changes of the LiveView code, they’re adding this feature as we speak. There is going to be a reset: true option to reset the stream to a new list of items.

They also realized it was clumsy to have just a single stream/4 function for in most cases you don’t want the stream/4 in mount but in update, so they added a stream_configure/3 function as well.

1 Like

Look at the :limit opt in the docs:


If I understand you, then this will do what you need.