Use Turbo Frames when a section of the page should update independently, such as filtering a list, loading a modal, or lazy loading hidden content. Use Turbo Streams when one request must update multiple parts of the page, such as appending a record, changing a count, and toggling a button state. In practice, prefer Frames first and use Streams when Frames are not enough.
How to choose the right Turbo primitive for the job
Turbo Frames and Turbo Streams solve different interaction problems, so the decision should start with the shape of the update, not the technology preference. A Frame is best when one part of the page can be re-rendered independently. A Stream is better when a single event needs to change multiple places in the interface or update content that is not inside the initiating frame.
The practical test is whether the user action has a single local destination or a broader page effect. If the change belongs to one isolated region, use a Frame and keep the interaction self-contained. If the change must fan out across counters, lists, status labels, or other UI regions, Streams give you the coordination layer without forcing a full-page refresh.
That distinction matters because Turbo Frames keep the browser navigation model simple. The URL, history, and server response are all focused on one region, which makes them easy to reason about for form submissions, filters, expandable panels, and lazy-loaded sections. Streams are more orchestration-oriented, which is useful when the response is really an event message that should be applied to several DOM targets.
Where Turbo Frames fit best
Use a Frame when the UI element has a clear boundary and the server can return a complete fragment for that boundary. This works well for partial page replacement, inline editing, modal content, drill-down views, and on-demand loading of content that starts hidden or expensive to render.
Frames also tend to be the cleaner choice when the interaction should preserve local state within a page area. For example, a search panel, a paginated table, or a sidebar can be refreshed independently without requiring the rest of the page to know that the update happened. That keeps the response focused and reduces the chance that unrelated DOM changes become part of the feature.
In practice, Frames are strongest when the backend can answer the request with one coherent fragment and the user only needs to see that fragment change. If you can describe the desired result as “replace this section with a new version of itself,” a Frame is usually the simplest implementation.
Where Turbo Streams are the better fit
Use Streams when the same event has to update several parts of the page at once, or when the change is not confined to a single predeclared region. A create action may need to append a row, update a badge count, clear a form, and change an empty-state message. That is a Stream-shaped problem, because the update is naturally multi-target.
Streams are also a strong choice for server-driven events that arrive outside the immediate user interaction, such as background job progress, broadcasts, collaborative updates, or changes that must appear in more than one place for consistency. The advantage is that the server can emit the intended DOM operations directly instead of making the client stitch together several separate requests.
As a rule, Streams are the coordination tool, while Frames are the containment tool. If the user action should have visible consequences across the page, or if the server needs to express a richer update than a simple fragment replacement, Streams are the more flexible abstraction.
Standards & Framework Alignment
This section maps relevant standards and security frameworks to the operational risks and controls described in this guidance.
CIS Controls v8 provides the primary governance reference for this topic.
| Framework | Control / Reference | Relevance |
|---|---|---|
| CIS Controls v8 | CIS 16 — Application Software Security | Hotwire UI choices affect how safely server responses are rendered into the browser. |
| Recommendation — Use secure rendering patterns and validate dynamic content paths before returning HTML fragments or stream updates. | ||
Practitioner Guidance
Decision rule: start with a Frame when the feature has one clear UI boundary and one server-rendered fragment. Move to Streams when the same business event must synchronise multiple DOM regions, or when the response is more like a page-wide update than a local replacement.
What to verify: check whether the interaction can still be understood if only one region is updated. If the answer is no because the feature needs a counter, list item, form state, and banner to change together, the implementation probably belongs in Streams even if a Frame could handle one of the pieces.
Common mistake: teams often choose Streams too early because they seem more powerful. That usually makes the feature harder to reason about, because the UI contract becomes “anything can change anywhere” instead of “this request updates this region.”
Practitioner takeaway: Frames should be your default for local, self-contained updates, and Streams should be reserved for coordinated UI effects that truly need multiple targets to stay consistent.
Related resources from NHI Mgmt Group
- How should development teams decide between Next.js 13 and 14 for a production web app?
- When should teams prefer Turbo Frames over Turbo Streams for modal workflows?
- What is the difference between Turbo Frames and Turbo Streams for modal and list updates?
- What is the difference between Turbo Streams and Turbo Frames for infinite scrolling?