Can Hotwire stream two turbo_frame_tags from the same object on the same page?

I’m trying to stream an object “question” twice on a single page. One to be displayed on a desktop view and another on a mobile view.

Whenever I copy the partial (below) on a single page, only the first partial in the line of code gets streamed live.

I would need to refresh the page in order for the second partial to be rendered.

<div id="questions">
	<%= render @room.questions %>

Can Hotwire stream two turbo_frame_tags from the same object on the same page?

1 Like

HTML does not allow two items on the same page to have the same ID. You will either need to use CSS to make the one thing look different in two different contexts, or you will have to give each one a unique ID.


1 Like

Thanks Walterdavis!

Is there a way to change the dom_id with Hotwire?

I assumed I needed to change the the dom_id tag and render it in a different partial view. But when I did so, the dom_id remained the same as question_1, question_3 and question_3.

Example code

<div id="<%= dom_id question_mobile %>">
<%= render partial: "questions/question_mobile", collection: @room.questions, as: :question_mobile %>

Kind Regards,

If question_mobile in your example is a unique persisted object, then it will have an id (whatever you get when you call to_param on it) and you should get something like question_mobile_42, and question_mobille_43 for unique instances when you call dom_id question_mobile. If these are new objects, made via .new or .build, then they won’t yet have ids, and you’ll have to do something to give each one a predictable and unique id in order to follow this pattern. You can give them a unique id through JavaScript – it doesn’t have to be in the DOM as written by Rails – but dom_id is only going to do anything for you within Rails.


The dom_id helper is great for the problem you’re trying to solve. If I’m understanding the architecture correctly, it sounds like you’re rendering a collection from an index action, than making changes to members of that collection in an update action? If so, it seems like what you’ll want is some view files that look something like…

<!-- app/views/questions/index.html.erb -->
<% @questions.each do |question| %>
  <div id="<%= dom_id(question, :mobile)"><!-- Question on mobile --></div>
  <div id="<%= dom_id(question, :desktop)"><!-- Question on desktop --></div>
<% end %>

<!-- app/views/questions/update.turbo_stream.erb -->
<%= turbo_stream.replace(dom_id(@question, :mobile)) do %>
  <div id="<%= dom_id(@question, :mobile)"><!-- Question on mobile --></div>
<% end %>

<%= turbo_stream.replace(dom_id(@question, :desktop)) do %>
  <div id="<%= dom_id(@question, :desktop)"><!-- Question on desktop --></div>
<% end %>

Am I understanding the problem you’re trying to solve?

Hey danott

Looks like I’m half way there :slightly_smiling_face: .Your suggestions to change the dom_id worked. However, the object turbo_stream still doesn’t update for the new mobile dom_id loop.

I tried implementing your code with both :mobile and :desktop id but the original question stream stopped working with :desktop in the id (so I kept the original “dom_id question” for the desktop loop).

Is there a way to isolate and turbo_stream the new question mobile dom_id?

I’m not sure I understand what you’re trying to accomplish.

Calling turbo_stream.replace(@question) is roughly equivalent to calling turbo_stream.replace(dom_id(@question)). It sounds like you’re still broadcasting to dom_id(@question), which is why reverting dom_id(@question, :desktop) to dom_id(@question) restored the behavior.

If you want to isolate the “mobile dom id”, you’ll have to explicitly broadcast to it, with dom_id(@question, :mobile).

If you have two different elements, you’ll have to broadcast to them independently, since everything in turbo streams and turbo frames is identified by id, which HTML requires to be unique.