Nitro

Features

Uploads

When you bind a file input with nitro:model, the file uploads as soon as the visitor chooses it. The component holds a signed reference to it, the view can preview it, and a #[Server] method validates and stores it.

On this page

As soon as the visitor chooses a file, it is sent to the server, where it waits in a private directory. The property becomes an Upload. The browser can show the file's name, its size and a preview, and a #[Server] method validates and stores it like any uploaded file in Laravel.

An upload, checked on the server live, in this page View source ↓

The component

Browser + server
<?php

namespace App\Nitro\Components;

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

class PhotoCheck extends Component
{
    public ?Upload $photo = null;

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

    #[Server]
    public function check(): void
    {
        $this->validate(['photo' => 'required|image|max:2048']);
        [$width, $height] = getimagesize($this->photo->getRealPath());

        // A demo: the photo is checked, not kept. Your app would store it with $this->photo->store('photos', 'public').
        $this->result = "{$this->photo->getClientOriginalName()}: {$width} × {$height} pixels, ".number_format($this->photo->getSize() / 1024, 1).' KB, checked on the server.';
        $this->reset('photo');
    }
}

Checked, not kept, for this demo

So that any visitor can try it, the example reads the photo's size on the server and keeps nothing. These docs accept images up to 2 MB. Your app would store the photo with $this->photo->store('photos', 'public').

Accepting one file or several

A ?Upload property takes one file. To take the files of an input with multiple, use an array property marked #[Uploads]:

PHP Server
use Nitro\Attributes\Uploads;
use Nitro\Upload;

public ?Upload $photo = null;   // One file, from <input type="file" nitro:model="photo">

#[Uploads]
public array $photos = [];      // Several files, from <input type="file" multiple nitro:model="photos">

#[Server]
public function save(): void
{
    $this->validate(['photos' => 'max:5', 'photos.*' => 'image|max:2048']);

    foreach ($this->photos as $photo) {
        $photo->store('gallery', 'public');
    }

    $this->reset('photos');
}

Upload is Laravel's UploadedFile, so validation rules such as image, mimes and max, and store(), all work as usual. You can use these methods in the view, on the server and in the browser:

Method Gives
temporaryUrl() A URL to preview the file, for images
getClientOriginalName() Its name on the visitor's computer
getClientOriginalExtension() Its extension
getSize() Its size, in bytes
getMimeType() Its type, such as image/png

Showing progress while it uploads

nitro:loading nitro:target="photo" shows an element while the file is on its way, just as it does for a #[Server] call. The input also fires events that report the progress:

JavaScript
const input = document.querySelector('#photo');

input.addEventListener('nitro-upload-start', () => bar.hidden = false);
input.addEventListener('nitro-upload-progress', (e) => bar.value = e.detail.progress);
input.addEventListener('nitro-upload-finish', () => bar.hidden = true);
input.addEventListener('nitro-upload-error', () => bar.hidden = true);

What Nitro checks

  • As each file arrives, Nitro checks it against nitro.uploads.rules. If a rule refuses the file, the property gets an $errors message and the upload stops there.
  • In your method, you check your own rules with $this->validate().
  • The reference the browser holds is signed, and it only works in its own session. Nitro refuses a made-up or changed reference. Only Upload and #[Uploads] properties accept files.
  • Leftover uploads that no component stored are removed after a day.
PHP
// config/nitro.php
'uploads' => [
    'directory' => storage_path('app/nitro-uploads'),   // Where uploads wait until you store them.
    'rules' => ['file', 'max:12288'],                    // Checked as each file arrives.
    'max_files' => 20,                                   // Files in one input at once.
    'throttle' => '60,1',                                // Uploads a minute, per visitor.
],

Next steps