Fragments
Fragments allow you to render specific portions of your Blade templates without compiling the entire view. This makes your application more efficient and your code more maintainable by promoting reusability and reducing server-side rendering overhead.
What Are Fragments?
A fragment is a named section within a Blade view that you can render independently. Instead of rendering an entire view file, you render just the part you need.
{{-- resources/views/todos/index.blade.php --}}
<div class="container">
<h1>My Todos</h1>
@fragment('todo-list')
<div id="todo-list">
@forelse($todos as $todo)
<x-todo-item :todo="$todo" />
@empty
<p>No todos yet</p>
@endforelse
</div>
@endfragment
</div>When you render this fragment from your controller, only the content between @fragment and @endfragment is compiled and sent to the browser:
public function index()
{
$todos = Todo::latest()->get();
return hyper()->fragment('todos.index', 'todo-list', compact('todos'));
}Defining Fragments
Basic Fragment
Define a fragment with @fragment and @endfragment:
@fragment('user-profile')
<div id="user-profile">
<h2>{{ $user->name }}</h2>
<p>{{ $user->email }}</p>
</div>
@endfragmentThe fragment name must be unique within the view file. Use descriptive names that indicate the fragment's purpose.
Fragments with Root IDs
For automatic targeting, give your fragment's root element an ID that matches or relates to the fragment name:
@fragment('contact-list')
<div id="contact-list">
@foreach($contacts as $contact)
<x-contact-card :contact="$contact" />
@endforeach
</div>
@endfragmentWhen you render this fragment, Datastar automatically finds and updates the #contact-list element in the DOM.
Multiple Fragments in One View
A single view can contain multiple fragments:
{{-- resources/views/dashboard.blade.php --}}
<!DOCTYPE html>
<html>
<body>
<div class="dashboard">
@fragment('stats')
<div id="dashboard-stats">
<x-stat-card :value="$stats->users" label="Users" />
<x-stat-card :value="$stats->revenue" label="Revenue" />
</div>
@endfragment
@fragment('activity')
<div id="recent-activity">
@foreach($activities as $activity)
<x-activity-item :activity="$activity" />
@endforeach
</div>
@endfragment
</div>
</body>
</html>You can render each fragment independently or multiple at once.
Rendering Fragments
fragment()
Render a single fragment from your controller:
public function updateTodoList()
{
$todos = Todo::latest()->get();
return hyper()->fragment('todos.index', 'todo-list', compact('todos'));
}Method Signature:
fragment(string $view, string $fragment, array $data = [], array $options = [])$view- The Blade view containing the fragment$fragment- The fragment name$data- Data to pass to the fragment$options- Targeting and mode options (see below)
Automatic Targeting
By default, Hyper uses the fragment's root element ID for targeting:
@fragment('todo-list')
<div id="todo-list">
<!-- Content -->
</div>
@endfragment// Automatically targets #todo-list
return hyper()->fragment('todos.index', 'todo-list', $data);If the fragment's root element has an ID, that element is automatically selected and updated using Datastar's morphing algorithm.
Explicit Targeting
Override automatic targeting with the selector option:
return hyper()->fragment('todos.index', 'todo-list', $data, [
'selector' => '#main-content'
]);This renders the fragment but patches it into #main-content instead of using automatic targeting.
Patch Modes
Control how the fragment HTML is applied to the target element with the mode option.
outer (Default)
Morph the entire element including its outer tag:
return hyper()->fragment('todos.index', 'todo-list', $data, [
'mode' => 'outer'
]);Before:
<div id="todo-list">
<p>Old content</p>
</div>After:
<div id="todo-list">
<p>New content</p>
</div>The outer mode uses Datastar's intelligent morphing algorithm which preserves event listeners, focus, and other JavaScript state when possible.
inner
Replace only the inner HTML:
return hyper()->fragment('todos.index', 'todo-list', $data, [
'mode' => 'inner'
]);Before:
<div id="todo-list">
<p>Old content</p>
</div>After:
<div id="todo-list">
<!-- New fragment content replaces inner HTML -->
<p>New content</p>
</div>The outer <div id="todo-list"> remains untouched.
replace
Completely replace the element without morphing:
return hyper()->fragment('todos.index', 'todo-list', $data, [
'mode' => 'replace'
]);This removes the old element and inserts the new one. Unlike outer, it doesn't preserve JavaScript state.
append
Add the fragment as the last child:
return hyper()->fragment('todos.index', 'new-todo', $data, [
'selector' => '#todo-list',
'mode' => 'append'
]);Before:
<div id="todo-list">
<div>Existing todo</div>
</div>After:
<div id="todo-list">
<div>Existing todo</div>
<div>New todo</div> <!-- Appended -->
</div>prepend
Add the fragment as the first child:
return hyper()->fragment('todos.index', 'new-todo', $data, [
'selector' => '#todo-list',
'mode' => 'prepend'
]);Before:
<div id="todo-list">
<div>Existing todo</div>
</div>After:
<div id="todo-list">
<div>New todo</div> <!-- Prepended -->
<div>Existing todo</div>
</div>before
Insert the fragment before the target element:
return hyper()->fragment('notifications', 'new-notification', $data, [
'selector' => '#notification-list',
'mode' => 'before'
]);after
Insert the fragment after the target element:
return hyper()->fragment('help', 'tip', $data, [
'selector' => '#form',
'mode' => 'after'
]);Multiple Fragments
fragments()
Render multiple fragments in a single response:
public function refreshDashboard()
{
return hyper()->fragments([
[
'view' => 'dashboard',
'fragment' => 'stats',
'data' => ['stats' => $this->getStats()]
],
[
'view' => 'dashboard',
'fragment' => 'activity',
'data' => ['activities' => $this->getActivities()]
],
[
'view' => 'dashboard',
'fragment' => 'notifications',
'data' => ['notifications' => $this->getNotifications()]
]
]);
}Each fragment renders and patches independently. You can mix different views, targets, and modes:
return hyper()->fragments([
[
'view' => 'products.index',
'fragment' => 'product-grid',
'data' => ['products' => $products]
],
[
'view' => 'products.index',
'fragment' => 'filters',
'data' => ['categories' => Category::all()],
'options' => ['mode' => 'inner']
]
]);Chaining fragment() Calls
You can also chain multiple fragment() calls:
return hyper()
->fragment('dashboard', 'stats', ['stats' => $stats])
->fragment('dashboard', 'activity', ['activities' => $activities])
->signals(['lastRefresh' => now()->toIso8601String()]);Both approaches work identically. Choose the one that fits your code style.
Nested Fragments
Fragments can be nested within other fragments:
@fragment('dashboard')
<div id="dashboard">
<header>Dashboard</header>
@fragment('stats')
<div id="stats">
<x-stat-card :value="$userCount" />
</div>
@endfragment
@fragment('chart')
<div id="chart">
<x-revenue-chart :data="$chartData" />
</div>
@endfragment
</div>
@endfragmentYou can render any fragment independently:
// Render entire dashboard
hyper()->fragment('dashboard', 'dashboard', $data);
// Render only stats
hyper()->fragment('dashboard', 'stats', $data);
// Render only chart
hyper()->fragment('dashboard', 'chart', $data);Nested fragments allow you to build complex, composable interfaces where you can update individual sections without touching the parent.
Fragment Philosophy
Code Reuse
Fragments promote code reuse. Instead of duplicating view logic for Hyper requests and initial loads, you write it once:
{{-- resources/views/contacts/index.blade.php --}}
<!DOCTYPE html>
<html>
<head>
<title>Contacts</title>
@hyper
</head>
<body>
<div class="container">
<h1>Contacts</h1>
@fragment('contact-list')
<div id="contact-list">
@foreach($contacts as $contact)
<x-contact-card :contact="$contact" />
@endforeach
<div class="pagination">
{{ $contacts->links() }}
</div>
</div>
@endfragment
</div>
</body>
</html>Your controller can render the full view for initial loads and just the fragment for Hyper interactions:
public function index()
{
$contacts = Contact::latest()->paginate(10);
return hyper()
->fragment('contacts.index', 'contact-list', compact('contacts'))
->web(view('contacts.index', compact('contacts')));
}Performance Benefits
Fragments render faster because they:
- Skip unnecessary template compilation - Only the fragment content is processed
- Send less data - Only the updated portion is transmitted
- Update precisely - The DOM changes are minimal and targeted
For a large dashboard with multiple sections, rendering a single fragment is significantly faster than rendering the entire page.
Better Organization
Fragments help organize complex views into logical sections:
{{-- resources/views/admin/dashboard.blade.php --}}
@fragment('user-stats')
<div id="user-stats">
<!-- User statistics -->
</div>
@endfragment
@fragment('revenue-chart')
<div id="revenue-chart">
<!-- Revenue visualization -->
</div>
@endfragment
@fragment('recent-orders')
<div id="recent-orders">
<!-- Order list -->
</div>
@endfragment
@fragment('activity-feed')
<div id="activity-feed">
<!-- Activity stream -->
</div>
@endfragmentEach section can be updated independently, making your application feel more responsive.
Common Patterns
Pagination with Fragments
public function index()
{
$products = Product::paginate(20);
return hyper()
->fragment('products.index', 'product-grid', compact('products'))
->web(view('products.index', compact('products')));
}@fragment('product-grid')
<div id="product-grid">
<div class="grid">
@foreach($products as $product)
<x-product-card :product="$product" />
@endforeach
</div>
<div class="pagination">
@foreach($products->links()->elements[0] as $page => $url)
<a href="{{ $url }}" data-navigate="true">{{ $page }}</a>
@endforeach
</div>
</div>
@endfragmentWhen users click pagination links, only the product grid updates.
Form Success Response
public function store()
{
$validated = signals()->validate([
'name' => 'required',
'email' => 'required|email'
]);
$contact = Contact::create($validated);
return hyper()
->fragment('contacts.index', 'contact-list', [
'contacts' => Contact::latest()->paginate(10)
])
->signals([
'contactForm' => [], // Clear form
'errors' => [],
'message' => 'Contact created!'
]);
}Search Results
public function search()
{
$query = signals('searchQuery');
$results = Product::search($query)->get();
return hyper()->fragment('search.results', 'results-list', compact('results', 'query'));
}@fragment('results-list')
<div id="search-results">
<p>Found {{ $results->count() }} results for "{{ $query }}"</p>
@forelse($results as $result)
<x-search-result :item="$result" />
@empty
<p>No results found</p>
@endforelse
</div>
@endfragmentSidebar Updates
public function updateCart($productId)
{
Cart::add($productId);
return hyper()
->fragment('shop.layout', 'cart-sidebar', [
'cartItems' => Cart::items(),
'cartTotal' => Cart::total()
])
->signals(['message' => 'Added to cart!']);
}Live Dashboard Sections
public function refreshStats()
{
return hyper()->fragments([
[
'view' => 'dashboard',
'fragment' => 'user-stats',
'data' => ['stats' => $this->getUserStats()]
],
[
'view' => 'dashboard',
'fragment' => 'revenue',
'data' => ['revenue' => $this->getRevenue()]
]
]);
}Troubleshooting
Fragment Compilation Errors
If you encounter errors like "unexpected end of file" or fragments not rendering correctly, this is typically due to Laravel's Blade cache containing stale compiled views.
Quick Fix:
php artisan view:clearThis clears Laravel's compiled Blade views and allows them to be re-compiled with Hyper's fragment directives properly registered.
Why This Happens:
Hyper registers custom Blade directives (@fragment, @endfragment) when the package boots. If views containing fragments are compiled before these directives are registered, the Blade cache can become corrupted.
Automatic Recovery:
Hyper includes automatic error recovery. If a fragment rendering fails due to cache issues, Hyper will:
- Automatically clear the view cache
- Retry the fragment rendering
- Log a warning for debugging
In most cases, this recovery happens transparently, but you may see a brief delay on the first request after installation or updates.
Prevention:
- Hyper includes a Composer post-install hook that automatically clears the view cache
- Always run
php artisan view:clearwhen deploying to new environments - Include
php artisan view:clearin your deployment scripts
Development Tip
During development, if you're experiencing persistent fragment issues, try:
php artisan view:clear && php artisan config:clearThis ensures both Blade views and configuration are freshly compiled.
Learn More
- Response Builder - Using fragment methods
- Blade Integration - Fragment directives
- DOM Morphing - How Datastar preserves state
- Dual Responses - Progressive enhancement with fragments

