Nitro

Essentials

Directives

Directives are nitro: attributes that connect a view to its component. Use them to call methods on events, bind inputs to properties, and add behaviours such as loading states, polling and confirmations.

On this page

Every directive is an HTML attribute that starts with nitro:. Its value is a method name, a method call with arguments, or one of Nitro's built-in actions. You don't need to write any JavaScript.

Keys, clicks outside and polling live, in this page View source ↓
Click outside to close it.

On this page for 0s (nitro:poll, paused while the tab is hidden)

The component

Pressing Enter calls add(), and pressing Escape clears the field with $set. The menu opens with $toggle and closes when you click outside it. nitro:poll.1s calls tick() every second. All of this runs in the browser.

Browser + server
<?php

namespace App\Nitro\Components;

use Nitro\Component;

class Shortcuts extends Component
{
    public string $draft = '';

    public array $notes = [];

    public bool $menu = false;

    public int $seconds = 0;

    public function add(): void
    {
        if (trim($this->draft) !== '') {
            array_unshift($this->notes, trim($this->draft));
            $this->draft = '';
        }
    }

    public function tick(): void
    {
        $this->seconds++;
    }
}

Handling events

You can listen for any DOM event, such as nitro:click, nitro:submit, nitro:input or nitro:keydown. The value is a method name, a method call such as remove($index), or an action.

Blade
<button nitro:click="save">Save</button>
<form nitro:submit="add">...</form>
<input nitro:keydown.enter="search">
<div nitro:click.outside="$set('open', false)">...</div>
<input nitro:input.debounce.300ms="suggest">

nitro:submit always stops the browser from posting the form and reloading the page, so you don't need .prevent on it.

Modifiers change how an event is handled:

Modifier Does
.prevent , .stop Call preventDefault() or stopPropagation()
.self Only when the event starts on the element itself
.once Only the first time
.window , .document Listen on the window or the document
.outside A click outside the element
.passive A passive listener: the handler cannot call preventDefault() , so scrolling and touch stay smooth
.capture Handle the event on its way down, before the elements inside the element see it
.enter , .escape , .space , ... Only for that key (see Keys below), or a letter or digit: .k , .1
.shift , .ctrl , .alt , .meta Only while that key is held, on a key press or a click
.debounce.300ms Run the handler once the events stop for that long (250ms without a time)
.throttle.1s Run it at most once in that time (250ms without a time)

Keys

On nitro:keydown and nitro:keyup, a key modifier runs the handler only for that key. Give several, and any of them runs it. A modifier key (.shift, .ctrl, .alt, .meta) must be held as well, and it works on clicks too:

Blade
<input nitro:keydown.enter="search">                  {{-- Enter --}}
<input nitro:keydown.enter.space="choose">            {{-- Enter or Space --}}
<div nitro:keydown.escape.window="close">...</div>    {{-- Escape, anywhere on the page --}}
<textarea nitro:keydown.ctrl.enter="send"></textarea> {{-- Ctrl+Enter --}}
<ul nitro:keydown.up.prevent="previous" nitro:keydown.down.prevent="next">...</ul>

<div nitro:keydown.ctrl.k.window.prevent="openSearch">...</div>   {{-- Ctrl+K, anywhere: a search box --}}
<div nitro:keydown.meta.k.window.prevent="openSearch">...</div>   {{-- and Command+K on a Mac --}}

<button nitro:click.shift="selectRange($i)">...</button>   {{-- a click with Shift held --}}
Modifier Key
.enter Enter
.escape , .esc Escape
.space The space bar
.tab Tab
.up , .down , .left , .right The arrow keys
.delete , .backspace Delete and Backspace
.a to .z , .0 to .9 That letter or digit: .k is K, .1 is 1
.shift , .ctrl , .alt , .meta Held down: Shift, Control, Alt (Option on a Mac), and Meta (Command on a Mac, the Windows key)
  • Other keys held don't matter. nitro:keydown.enter also runs for Ctrl+Enter. To tell them apart, give the Ctrl one a handler of its own: nitro:keydown.ctrl.enter.
  • Letters in either case. .k runs for k and for K, so nitro:keydown.shift.k works though Shift types a capital. With Alt held (Option on a Mac, which types other characters), it is the K key on the keyboard.
  • Digits by their key. .2 runs for the 2 key even with Shift held, when it types "@".
  • Shortcuts for the whole page go on an element with .window, and .prevent stops the browser's own (Ctrl+K moves to the address bar in some browsers). Give Mac users .meta as well as .ctrl.
  • A wrong name fails the build, naming the line: nitro:keydown.entr never silently does nothing.

Using actions

Actions do common things without a method of your own:

Action Does
$set('prop', value) Set a property
$toggle('prop') Flip a boolean
$refresh Render again (with a #[Server] round trip when the component has server methods)
$dispatch('event', ...) Dispatch an event; $dispatchTo('name', 'event', ...) and $dispatchSelf('event', ...) narrow it
$parent->save() Call the parent component's method
$reload('prop', ...) Reload page properties from the page's route (pages only)
$loadMore('prop') Load the next page of a scroll property (pages only)
$visit(url) Go to another page, as a nitro:navigate link does

Binding inputs

nitro:model keeps an input and a property in sync, in both directions.

Blade
<input nitro:model="email">                        {{-- Updates on every keystroke --}}
<input nitro:model.blur="name">                    {{-- Updates when the field loses focus --}}
<input nitro:model.debounce.300ms="search">        {{-- Updates 300ms after typing stops --}}
<input type="number" nitro:model.number="age">     {{-- Stores the value as a number --}}
<input nitro:model="form.email">                   {{-- Binds a key of the $form array --}}

You can add the .blur, .debounce.300ms, .number, .trim and .boolean modifiers. Each change runs the component's updated*() hooks (see Methods).

Other attributes

Nitro has these other attributes:

Attribute Does
nitro:model="email" Two-way binding (above)
nitro:loading Shown while a #[Server] call runs. .remove , .class="..." , .class.remove="..." , .attr="disabled" ; .delay ( .delay.500ms ) waits first; nitro:target="save,delete" limits it to those methods
nitro:dirty Shown while the page has changes the server hasn't seen. The same forms as nitro:loading ; nitro:target="title" limits it to those properties
nitro:key Keeps an element's identity in a list
nitro:navigate A link to another page, rendered without a reload (see Navigation). .keypress , .preserve-scroll , .transition
nitro:prefetch Beside nitro:navigate : asks for the page before the click, on hover ( .150ms : after that wait) or .visible when on screen; .keep.60s keeps it that long
nitro:current="active" A class while the link points at the current page; .exact for the same path only
nitro:ignore The element and what is in it start once (their handlers, your own attributes), and no render changes them after: for markup a library owns (see Third-party libraries)
nitro:confirm="Delete it?" Asks before the element's handlers run
nitro:poll.30s="refresh" Calls a method every interval (2.5s by default)
nitro:init="load" Calls a method once, when the element appears
nitro:visible="loadMore" Calls a method when the element comes into view
nitro:ref="chart" The element, as $refs.chart in $this->js() and for the browser APIs
nitro:transition Animates the element as it enters and leaves (see Transitions)

Asking for confirmation

nitro:confirm asks the visitor before the element's handlers run. With .prompt, the visitor must type a word to confirm, which is the word after the last |:

Blade
<button nitro:click="delete" nitro:confirm="Delete this post?">Delete</button>

<button nitro:click="destroy" nitro:confirm.prompt="Type DELETE to confirm|DELETE">
    Delete the account
</button>

Polling and reacting to visibility

These attributes call a method on a timer, when an element appears, or when it comes into view:

Blade
<div nitro:poll.30s="refresh">...</div>            {{-- Every 30 seconds, paused while the tab is hidden --}}
<div nitro:poll.visible="refresh">...</div>        {{-- Only while the element is on screen --}}
<div nitro:init="load">...</div>                   {{-- Once, when the element appears --}}
<div nitro:visible.200px="loadMore">...</div>      {{-- When the element comes within 200px of the screen --}}
  • A bare nitro:poll refreshes the component. Polling pauses while the tab is hidden. Add .keep-alive to keep it going, or .visible to poll only while the element is on screen.
  • Add .once to nitro:visible to call the method only the first time. While the element stays in view, the method is called again after each call that changed something, so a "load more" row keeps loading until the screen is full.

Next steps