| name | developing-with-turbo-frames |
| description | Develops with Turbo Frames for scoped navigation and lazy loading. Activates when using the x-turbo::frame Blade component or turbo-frame HTML element; working with data-turbo-frame targeting, frame lazy loading via src attribute, or data-turbo-action for URL updates; detecting frame requests with wasFromTurboFrame(); using frame morphing with refresh="morph"; or when the user mentions Turbo Frame, turbo frame, scoped navigation, inline editing, lazy loading frames, or breaking out of a frame with _top. |
Turbo Frames
Turbo Frames decompose pages into independent segments that scope navigation. Clicking links or submitting forms inside a <turbo-frame> only updates that frame, keeping the rest of the page intact.
The Frame Component
Use the <x-turbo::frame> Blade component to render a <turbo-frame> element:
@verbatim
{{ $post->title }}
Edit
@endverbatim
The :id Prop
The :id prop accepts multiple formats and auto-generates DOM IDs:
@verbatim
{{-- String: uses as-is --}}
...
{{-- Model instance: generates dom_id($post) e.g. "post_1" --}}
<x-turbo::frame :id="$post">...</x-turbo::frame>
{{-- Array [model, prefix]: generates dom_id($post, 'edit') e.g. "edit_post_1" --}}
<x-turbo::frame :id="[$post, 'edit']">...</x-turbo::frame>
@endverbatim
Scoped Navigation
By default, links and forms inside a frame target that same frame. When the server responds, Turbo extracts the matching <turbo-frame> from the response and swaps its content:
@verbatim
{{-- Clicking this link fetches the edit page and extracts the matching frame --}}
Edit
{{-- Submitting this form updates only this frame with the response --}}
<form action="{{ route('posts.update', $post) }}" method="POST">
@csrf
@method('PUT')
<input name="title" value="{{ $post->title }}">
<button type="submit">Save</button>
</form>
</x-turbo::frame>
@endverbatim
Targeting Other Frames
Override the default frame target using data-turbo-frame:
@verbatim
{{-- Target a specific frame by its DOM ID --}}
View
{{-- Break out of the frame and navigate the entire page --}}
View full page
@endverbatim
You can also set a default target on the frame itself:
@verbatim
{{-- All navigation within this frame targets "_top" by default --}}
View
@endverbatim
Lazy Loading
Frames can defer loading their content using the :src attribute. The frame fetches its content automatically:
@verbatim
{{-- Eager lazy load: fetches immediately when the page loads --}}
Loading comments...
{{-- Viewport lazy load: fetches when the frame enters the viewport --}}
<x-turbo::frame :id="$post" :src="route('posts.comments.index', $post)" loading="lazy">
Loading comments...
</x-turbo::frame>
@endverbatim
Promoting Frame Navigations to Page Visits
Use data-turbo-action to make a frame navigation also update the browser URL and history:
<a href="/posts/1" data-turbo-frame="post_detail" data-turbo-action="advance">View</a>
This updates the frame content AND pushes the URL to the browser history, allowing Back button navigation.
Detecting Frame Requests on the Server
Use request macros to detect if a request came from a Turbo Frame:
@verbatim
// Check if the request came from any Turbo Frame
if ($request->wasFromTurboFrame()) {
// Return frame-specific response
}
// Check if it came from a specific frame
if ($request->wasFromTurboFrame(dom_id($post, 'create_comment'))) {
// Return response for that specific frame
}
@endverbatim
Morphing Within Frames
Add refresh="morph" to morph frame content instead of replacing it, preserving DOM state:
<turbo-frame id="post_1" refresh="morph">
</turbo-frame>
Frame Rendering Customization
Customize how frame content is rendered using the turbo:before-frame-render event in JavaScript:
document.addEventListener("turbo:before-frame-render", (event) => {
});
Benefits of Frames
- Efficient caching: Each frame is cached independently, giving longer-lived caches.
- Parallelized execution: Lazy-loaded frames are fetched concurrently, reducing total page load time.
- Mobile-ready: Frames with independent URLs can be rendered as native sheets/screens in Hotwire Native apps.