Nitro

Essentials

Server methods

When a method needs the server, mark it with #[Server]. It can then use the database, mail, the filesystem and anything else PHP can do. The browser calls it with the component's signed state and receives the new state back.

On this page

Most of a component runs in the browser. When a method needs the server, add the #[Server] attribute to it. Nothing else changes. Your view calls it the same way, and the method reads and changes the component's properties like any other method.

A check on the server live, in this page View source ↓

The component

When you type, $name changes in the browser. Clicking Check calls check(), which runs on the server. It validates the name, looks it up and sets $result. The taken() method is private, and only check() calls it, so Nitro never compiles it. Open Compiled and you'll see that it isn't there.

Browser + server
<?php

namespace App\Nitro\Components;

use Nitro\Attributes\Server;
use Nitro\Component;

class Username extends Component
{
    public string $name = '';

    public string $result = '';

    #[Server]
    public function check(): void
    {
        $this->validate(['name' => 'required|alpha_dash|min:3|max:20']);

        $this->result = in_array(strtolower($this->name), $this->taken(), true)
            ? "{$this->name} is taken"
            : "{$this->name} is free (checked on PHP ".PHP_VERSION.')';
    }

    /** Only check() calls this method, so it is never sent to the browser. */
    private function taken(): array
    {
        // This demo uses a fixed list. Your app would query the users table.
        return ['admin', 'taylor', 'nitro'];
    }
}

What happens during a call

  1. The browser sends the state

    It sends the component's state, which the server signed when it last sent it, along with what the browser changed since, the method's name and its arguments.

  2. The server checks it

    The server checks the signature and the method, which must be #[Server]. It also checks the types of what changed and of the arguments, and the #[Validate] rules of what changed. If a rule fails, the call stops here and the messages are in $errors.

  3. Your method runs

    Your method runs after boot() and hydrate(). Model properties are read again from the database first.

  4. The new state comes back

    After dehydrate(), the server signs the new state and sends it back. The browser then renders what changed.

Passing arguments

Arguments come from the browser, so the server checks each one against its type. A parameter typed as a model or as a backed enum is resolved from what the browser sends. A parameter typed as any other class is resolved from the container.

Blade
@foreach ($posts as $post)   {{-- public Collection $posts: Post models --}}
    <button nitro:click="remove($post)">Delete</button>
    <button nitro:click="setStatus($post->id, 'published')">Publish</button>
@endforeach
PHP Server
use App\Enums\Status;
use App\Models\Post;
use App\Services\Search;
use Illuminate\Support\Facades\Gate;
use Nitro\Attributes\Server;

#[Server]
public function remove(Post $post): void              // Receives the record the key names, or answers 404.
{
    Gate::authorize('delete', $post);
    $post->delete();
}

#[Server]
public function setStatus(Post $post, Status $status): void   // Receives the case the value names.
{
    Gate::authorize('update', $post);
    $post->update(['status' => $status]);
}

#[Server]
public function search(Search $search): void          // Resolved from the container.
{
    $this->results = $search->for($this->query);
}
Parameter Receives
A model The record its key names, resolved as route model binding resolves one (its route key, or its own resolveRouteBinding() ). You can also pass the row itself: remove($post) . A missing record answers 404, and a nullable ?Post receives null .
A backed enum The case its value names. Any other value answers 403.
Any other class An instance resolved from the container. It takes nothing from the browser.
int , string , array , ... A value of that type. A value of another type answers 403.

Resolving a record doesn't authorize it

The key comes from the browser, and anyone can send any key. Always check that the visitor may act on the record, for example with a policy and Gate::authorize(), or with a query scoped to the visitor.

If the browser could never fill a parameter, such as a variadic model, an abstract model or a pure enum, the build fails.

Validating data

To validate the component's properties, call $this->validate([...]). When a rule fails, the method stops, and the messages are available in $errors for @error in the view. If you call it without rules, it checks every property's #[Validate] rules. See Forms and validation for more.

Keeping a slow result

A #[Server] method that only loads data, such as a lookup or a search, can keep its result with #[Cached]: the next call with the same arguments and state gets it without running the method, until Nitro::expire() forgets it. See Caching.

Redirecting

To send the browser to another page once the call is done, call $this->redirect($url):

PHP Server
#[Server]
public function checkout(Payments $payments): void
{
    $this->validate(['items' => 'required|array|min:1']);

    $payments->charge($this->items);

    $this->redirect(route('orders.index'));
}

If you call $this->redirect($url, navigate: true), the browser goes there the way a nitro:navigate link does, without a full reload. Nitro accepts only http(s) URLs and paths.

Calling a server method from a browser method

A browser method can call a #[Server] method, but only as a statement. Its return value doesn't reach the browser, so the server method should change properties instead of returning a value.

PHP Browser
public function finish(): void   // Runs in the browser.
{
    $this->step = 'done';
    $this->save();               // Calls a #[Server] method as a statement.
}

Showing that a call is running

Add nitro:loading to an element to show it while a #[Server] call runs. With .remove, the element is hidden instead. With .attr="disabled" or .class="...", an attribute or classes change instead. To react to one method only, add nitro:target.

Blade
<button nitro:click="save" nitro:loading.attr="disabled">
    <span nitro:loading.remove nitro:target="save">Save</span>
    <span nitro:loading nitro:target="save">Saving…</span>
</button>

Waiting before showing it

Most calls finish quickly, and a spinner that flashes for a moment is just noise. Add .delay, and the element changes only once the call has taken 200ms. Give it a time to wait longer or shorter. A call that ends sooner shows nothing.

Blade
<span nitro:loading.delay nitro:target="save">Saving…</span>             {{-- after 200ms --}}
<span nitro:loading.delay.1s nitro:target="save">Still working…</span>   {{-- after 1 second --}}
<div nitro:loading.delay.class="opacity-50">...</div>                   {{-- dimmed after 200ms --}}
  • Any form can wait. Put .delay first: .delay.remove, .delay.class="...", .delay.class.remove="...", .delay.attr="...".
  • A time in ms or s. .delay.500ms, .delay.1s. Without one, it waits 200ms.
  • A wrong modifier fails the build, naming the line, rather than doing nothing in the browser.

The runtime also dispatches the nitro:request and nitro:response events around each call. You can show the progress bar during calls by setting server_calls in config/nprogress.php.

What you can trust

  • The state is signed. Nitro signs it with the application key, the session's secret and the logged-in user. A changed state, or one from another session or user, is refused.
  • Unlocked properties accept any value of their type. Add #[Validate] rules to the properties a server method relies on, or validate them in the method.
  • #[Locked] means unchangeable, not current. Read anything that must be current, such as a balance or a permission, from the database in the method.
  • Every public property is sent to the browser. Keep secrets in protected or private properties.

See Security for everything the endpoint refuses.

Next steps