Pages
Page data
A page loads its data from its route, and Nitro gives you tools to shape that. You can load slow data after the page shows, reload some properties, merge and scroll lists, share data with every component, and flash data once.
On this page
A page loads its data in mount(), on the server, from its route. The tools on this page
control what to load later, what to load again, and how new data combines with what the page already has.
Shared data and flash data work in components too.
Deferring slow properties
A page can show first and load its slow data right after:
public ?array $stats = null;
public function mount(): void
{
$this->defer('stats', fn () => Order::stats());
}
@deferred('stats')
<x-stats :stats="$stats" />
@placeholder
<p>Loading…</p>
@enddeferred
Nitro renders the page without the deferred properties, and the page then asks its own URL for them. The
page stays usable while they load, and anything that changes in the meantime is kept. Use
@deferred without names to wait for every deferred property.
Reloading properties
A page can reload some of its properties from its route. Call $this->reload('orders') in a
method or a #[Server] method, or $reload('orders') in a handler. The page asks its
URL again and takes the named properties from the answer, while the other properties keep their values in
the browser. Without names, every property is reloaded, including what the visitor typed.
A reload runs mount() again. Use $this->wants() to skip the work a reload doesn't need:
#[Server]
public function add(string $item): void
{
Order::create(['item' => $item]);
$this->reload('orders', 'total');
}
public function mount(): void
{
if ($this->wants('orders')) {
$this->orders = Order::latest()->get()->toArray();
}
if ($this->wants('total')) {
$this->total = Order::sum('amount');
}
}
The answer signs the reloaded properties, so #[Locked] properties reach the next
#[Server] call exactly as the server gave them.
Merging and scrolling lists
When a property is reloaded, you can merge the new value into the one the page already has, instead of replacing it. The first render and a visit still set it as usual.
public array $posts = [];
public array $tags = [];
public array $filters = [];
public function mount(): void
{
$this->scroll('posts', fn () => Post::latest()->paginate(20))->matchOn('id');
$this->merge('tags', fn () => Tag::popular()->get()->toArray())->matchOn('id');
$this->deepMerge('filters', fn () => $this->defaultFilters());
}
| Does | |
|---|---|
$this->merge()
|
Appends the new items.
->prepend()
puts them first, and
->append('data')
merges the list at a key
|
$this->deepMerge()
|
Merges arrays key by key, all the way down. Lists are appended |
$this->scroll()
|
Takes a paginator (
paginate()
,
simplePaginate()
or
cursorPaginate()
), and the property holds its items.
$loadMore('posts')
asks for the next page and appends it, and
$this->hasMore('posts')
tells you whether there is one
|
->matchOn('id')
|
Replaces an item that has the same id, instead of adding it again |
@foreach ($posts as $post)
<article nitro:key="{{ $post['id'] }}">...</article>
@endforeach
@if ($this->hasMore('posts'))
<div nitro:visible="$loadMore('posts')">Loading…</div>
@endif
Each closure runs only when the request wants its property. The merge rule travels with the signed reload,
so the next #[Server] call merges the same way, and a #[Locked] list keeps what
was loaded.
Scroll on the server, filter in the browser
Use scroll properties for long lists that the server pages through. For a list of a few hundred rows, send it whole and filter it in the browser with a #[Computed] method. Filtering then needs no request.
Sharing data with every component
Nitro::share() gives every component and page the same data. Call it in a middleware, so it
runs on every request:
use Nitro\Facades\Nitro;
Nitro::share('user', fn () => $request->user()?->only('id', 'name'));
Nitro::share('locale', app()->getLocale());
Nitro::shareOnce('countries', fn () => Country::pluck('name', 'code'));
Nitro::share('permissions', Nitro::once(fn () => $user->permissions())->until(3600));
<span>{{ $this->shared('user.name', 'Guest') }}</span>
- Read shared data with
$this->shared(), in views and in methods. It takes dot notation and a default. - The data is sent with the page, with each visit and with each
#[Server]answer. Closures are called when the data is read. - Use
shareOnce()andNitro::once()for data that rarely changes. The browser keeps it across visits and calls, and the server doesn't send it again until it expires or you send it with->fresh(). Set the expiry with->until(), as seconds, an interval or a date. Once data is shared at the top level, under a key without dots.
Everything shared is public
Shared data is sent to the browser, so never share anything secret.
Flashing data
Nitro::flash() sends data with the next response only, such as a message after a save. The
example above flashes a message from its #[Server] method, and its view shows the message with
$this->flashed().
<?php
namespace App\Nitro\Components;
use Nitro\Attributes\Server;
use Nitro\Component;
use Nitro\Facades\Nitro;
class Flash extends Component
{
#[Server]
public function save(): void
{
Nitro::flash('toast', 'Saved on the server at '.now()->format('H:i:s'));
}
}
<div class="flex w-full max-w-lg flex-wrap items-center gap-3 text-sm">
<button type="button" nitro:click="save" nitro:loading.attr="disabled" class="rounded-lg bg-slate-900 px-3 py-2 font-semibold text-white disabled:opacity-50 dark:bg-white dark:text-slate-900">Save</button>
@if ($this->flashed('toast'))
<span class="rounded-full bg-emerald-50 px-3 py-1 text-emerald-700 ring-1 ring-emerald-200 dark:bg-emerald-500/10 dark:text-emerald-300 dark:ring-emerald-500/20">{{ $this->flashed('toast') }}</span>
@endif
</div>
Compiling…
Flash data goes with this request's page, visit or #[Server] answer. After a redirect, it goes
with the page the redirect leads to. After that it is gone, and the back button doesn't bring it back. The
browser also emits a nitro:flash event with the data in event.detail.flash.
Flash data needs the session, so the request must run through the web middleware.