Nitro

Getting started

Introduction

Nitro is a full-stack framework for Laravel. You write components and pages as a PHP class and a Blade view, just as you write Laravel code today, and Nitro compiles them to JavaScript that runs in the browser.

On this page

What Nitro does

  • The server renders the first page. Blade renders it, so it appears quickly and search engines can read all of it.
  • The browser takes over from there. Clicks, typing, filtering and re-rendering all happen in the page, without a request.
  • The server is one attribute away. When a method needs the database, mail or the session, mark it #[Server]. It then runs on the server, with the component's signed state.
  • Rendering is fine-grained. A change updates only the parts of the page that show it. There is no virtual DOM and no HTML diffing.
A component live, in this page View source ↓
Not saved yet

A first component

The counter above is a class and a view. The increment() method runs in the browser, so clicking it doesn't send anything to the server. The save() method is marked #[Server], so it runs on the server. It keeps the count in the session and sends the new state back. When the page loads again, mount() reads the count back, so it survives a reload. Open Compiled to see the JavaScript that Nitro makes from them.

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.

You can place the component in any Blade view with its tag:

any view
<nitro:counter />

Where your code runs

Every part of a component runs in one place. If you put code in the wrong place, Nitro tells you. For example, a browser method that queries the database fails the build, and the error names the file and line.

Code Runs
mount(), boot(), hydrate(), dehydrate() Server On the server, when the component is created or called.
#[Server] methods Server On the server, when the browser calls them with the signed state.
All other methods, #[Computed], updated*() Browser In the browser, compiled to JavaScript.
The view Browser + server On the server for the first render, then in the browser.

Methods the browser never reaches are never compiled

A private helper that you call only from mount() or from #[Server] methods can use anything PHP can, such as queries, services and the clock.

Requirements

  • PHP 8.3 or higher
  • Laravel 13
  • Node.js and esbuild, for production builds (nitro:build) only

Nitro's browser versions of Laravel's helpers (Str::, route(), __()) follow Laravel 13, so earlier Laravel versions are not supported. Nitro uses only Laravel's public APIs. It can also run on the same page as Livewire, because its tags, attributes, scripts and endpoints all use the nitro: prefix.

Next steps