Skip to content

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.

blade
{{-- 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:

php
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:

blade
@fragment('user-profile')
<div id="user-profile">
    <h2>{{ $user->name }}</h2>
    <p>{{ $user->email }}</p>
</div>
@endfragment

The 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:

blade
@fragment('contact-list')
<div id="contact-list">
    @foreach($contacts as $contact)
        <x-contact-card :contact="$contact" />
    @endforeach
</div>
@endfragment

When 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:

blade
{{-- 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:

php
public function updateTodoList()
{
    $todos = Todo::latest()->get();

    return hyper()->fragment('todos.index', 'todo-list', compact('todos'));
}

Method Signature:

php
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:

blade
@fragment('todo-list')
<div id="todo-list">
    <!-- Content -->
</div>
@endfragment
php
// 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:

php
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:

php
return hyper()->fragment('todos.index', 'todo-list', $data, [
    'mode' => 'outer'
]);

Before:

html
<div id="todo-list">
    <p>Old content</p>
</div>

After:

html
<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:

php
return hyper()->fragment('todos.index', 'todo-list', $data, [
    'mode' => 'inner'
]);

Before:

html
<div id="todo-list">
    <p>Old content</p>
</div>

After:

html
<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:

php
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:

php
return hyper()->fragment('todos.index', 'new-todo', $data, [
    'selector' => '#todo-list',
    'mode' => 'append'
]);

Before:

html
<div id="todo-list">
    <div>Existing todo</div>
</div>

After:

html
<div id="todo-list">
    <div>Existing todo</div>
    <div>New todo</div>  <!-- Appended -->
</div>

prepend

Add the fragment as the first child:

php
return hyper()->fragment('todos.index', 'new-todo', $data, [
    'selector' => '#todo-list',
    'mode' => 'prepend'
]);

Before:

html
<div id="todo-list">
    <div>Existing todo</div>
</div>

After:

html
<div id="todo-list">
    <div>New todo</div>  <!-- Prepended -->
    <div>Existing todo</div>
</div>

before

Insert the fragment before the target element:

php
return hyper()->fragment('notifications', 'new-notification', $data, [
    'selector' => '#notification-list',
    'mode' => 'before'
]);

after

Insert the fragment after the target element:

php
return hyper()->fragment('help', 'tip', $data, [
    'selector' => '#form',
    'mode' => 'after'
]);

Multiple Fragments

fragments()

Render multiple fragments in a single response:

php
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:

php
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:

php
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:

blade
@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>
@endfragment

You can render any fragment independently:

php
// 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:

blade
{{-- 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:

php
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:

  1. Skip unnecessary template compilation - Only the fragment content is processed
  2. Send less data - Only the updated portion is transmitted
  3. 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:

blade
{{-- 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>
@endfragment

Each section can be updated independently, making your application feel more responsive.

Common Patterns

Pagination with Fragments

php
public function index()
{
    $products = Product::paginate(20);

    return hyper()
        ->fragment('products.index', 'product-grid', compact('products'))
        ->web(view('products.index', compact('products')));
}
blade
@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>
@endfragment

When users click pagination links, only the product grid updates.

Form Success Response

php
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

php
public function search()
{
    $query = signals('searchQuery');
    $results = Product::search($query)->get();

    return hyper()->fragment('search.results', 'results-list', compact('results', 'query'));
}
blade
@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>
@endfragment
php
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

php
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:

bash
php artisan view:clear

This 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:

  1. Automatically clear the view cache
  2. Retry the fragment rendering
  3. 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:clear when deploying to new environments
  • Include php artisan view:clear in your deployment scripts

Development Tip

During development, if you're experiencing persistent fragment issues, try:

bash
php artisan view:clear && php artisan config:clear

This ensures both Blade views and configuration are freshly compiled.

Learn More