Nitro

Essentials

Properties

A component's public properties are its state. The server renders them, the browser keeps them, and your methods change them, wherever those methods run.

On this page

Every public property of a component is part of its state. The server renders the first page with it, and the browser then keeps it. Your methods change it, most of them in the browser and the #[Server] ones on the server. There is nothing to wire up. You declare a property and use it in the view.

A counter live, in this page View source ↓
Not saved yet

The component

The counter above has two properties. increment() changes $count in the browser, without a request. Its #[Validate] rule is checked on the server before save() runs, so a count sent from outside the page can't be negative or huge. $savedAt is marked #[Locked], so the browser shows it but only save() on the server can set it. Open Compiled to see what the browser runs.

Browser + server
<?php

namespace App\Nitro\Components;

use Nitro\Attributes\Locked;
use Nitro\Attributes\Server;
use Nitro\Attributes\Validate;
use Nitro\Component;

class Counter extends Component
{
    #[Validate('integer|min:0|max:1000')]
    public int $count = 0;

    #[Locked]
    public string $savedAt = '';

    public function mount(): void
    {
        $this->count = session('counter.count', 0);
        $this->savedAt = session('counter.saved_at', '');
    }

    public function increment(): void
    {
        $this->count++;
    }

    #[Server]
    public function save(): void
    {
        $this->savedAt = now()->format('H:i:s');

        // This demo keeps the count in the session. Your app would save it to the database.
        session(['counter.count' => $this->count, 'counter.saved_at' => $this->savedAt]);
    }
}

This demo saves to the session

So that the example works for every visitor, without an account or a database, save() keeps the count in your session. Your application would save it to the database in the same way. The call to the server, the validation and the new state that comes back all stay the same.

Property types

A property can hold null, a bool, an int, a float, a string, an array, a backed enum, an Eloquent model or collection, or an upload. A typed property always keeps its type. If the browser sends a value of another type, the server refuses it rather than converting it.

Type How it behaves
bool, int, float, string The browser can only set it to a value of the same type (an int is accepted for a float).
array A value, as in PHP: a copy never changes the property. Keys and their order survive the round trip.
Backed enum The browser holds the case as its value, and can only send the value of one of its cases. The enum must be marked #[Browser].
Model, Collection Sent to the browser as their visible attributes. Read again from the database on every #[Server] call, never taken from the browser.
Upload, #[Uploads] array Files from a file input, stored by a #[Server] method. See Uploads.

Model properties

Nitro reads a model property again from the database on each server call:

PHP
public ?Post $post = null;

public function mount(Post $post): void
{
    $this->post = $post;
}
  • A collection keeps its order, keeps each model as its own class, and leaves out rows that have been deleted since.
  • If a single model's row has been deleted, a nullable property (public ?Post $post) becomes null, so the component can tell and the view can show it. A property that isn't nullable answers with a 404.

Arrays are values

As in PHP, $copy = $this->items makes a copy, so changing $copy never changes the property. The browser follows this rule too.

To read and change arrays and models in your views and methods, and to choose between them, see Arrays and models.

Property attributes

You can mark a property with these attributes to change how it behaves:

Attribute What it does
#[Locked] The browser can read it, but only the server can change it.
#[Url] Keeps the property in the query string (with as, history, keep and except). Nitro reads it before mount() and keeps it in sync with the property.
#[Remember] Keeps the value in the browser’s history entry, so back, forward and a reload bring it back, such as a half-filled form.
#[Modelable] Lets a parent bind this property with nitro:model. Only one property can have it.
#[Validate] Rules the server checks whenever the browser changes the property.

Locking a property

The browser can read a property marked #[Locked], but only the server can change it, in mount() or in a #[Server] method. Because the state is signed, the server refuses a changed value.

app/Nitro/Components/Wallet.php
class Wallet extends Component
{
    public int $amount = 0;

    #[Locked]
    public int $balance = 100;

    #[Server]
    public function withdraw(): void
    {
        $this->balance -= $this->amount;
    }
}

Locked doesn't mean current

Signing stops the browser from changing $balance. It doesn't stop the same visitor from sending an older state from their own session. So read anything that must be current, such as a balance or a permission, from the database in the #[Server] method.

Keeping a property in the query string

#[Url] keeps a property in the query string, so a search or a page number survives a reload and can be shared:

PHP
#[Url]
public string $search = '';

#[Url(as: 'p', history: true)]
public int $page = 1;

Validating properties

The browser can set any property that isn't #[Locked] to any value of its type. Anyone can send a request without using the page, so $quantity could arrive as -500. Before a #[Server] method runs, the server checks the type of what changed, and its #[Validate] rules. If a rule fails, the method doesn't run, and the messages appear in $errors.

PHP Server
#[Validate('integer|min:1|max:10', as: 'quantity')]
public int $quantity = 1;

#[Server]
public function checkout(): void
{
    // $quantity is between 1 and 10 here
}

Validate everything a server method relies on

Put a rule on every property that a #[Server] method uses, or validate in the method itself. Calling $this->validate() without rules checks every property's #[Validate] rules, which is useful before saving a form.

Never put secrets in public properties

Every public property is sent to the browser, including #[Locked] ones, and a model property carries its visible attributes. Keep secrets in protected or private properties, or load them in the method that needs them.

Next steps