Skip to content

Media Library ​

A multi-file media manager field: drag-and-drop (or click) to upload many files, see a thumbnail grid, reorder by dragging, delete, and preview. It works standalone, and integrates with spatie/laravel-medialibrary when that package is installed — collections, conversions and ordering included.

Media library gridMedia library grid

The field ​

php
use Happones\Kinetix\Forms\Components\MediaLibrary;

MediaLibrary::make('gallery')
    ->collection('images')          // spatie collection / logical group
    ->image()                       // restrict to images + show thumbnails
    ->conversions(['thumb'])        // spatie conversions to surface in the grid
    ->maxFiles(10)
    ->disk('public')
    ->directory('products');

Builds on FileUpload — same signed upload token, disk, directory and size/type constraints. Multiple + reorderable by default; turn dragging off with ->reorderable(false).

The field value is an ordered array of media items:

ts
{ id?, path?, url, name, size?, mime?, thumb? }[]

Newly uploaded files carry a path (the stored temp path); existing spatie media carry an id. Reordering just reorders the array: while dragging, the in-flight tile travels through the grid as a translucent live preview of its landing position; the new order is applied once on drop, and a cancelled drag reverts the preview.

Each tile's grip (top-left) is also a real control. On touch screens, drag the tile by its grip: the drag starts as soon as your finger moves, with the same live preview, and a long library scrolls near its edges. Letting go outside the grid puts the tile back. The grip and the remove button are always visible on touch screens, and show on hover or focus elsewhere. Keyboard users focus the grip and press the arrow keys to move the tile one place earlier or later. The new position is announced. A disabled field shows no grip and can't be reordered.


Standalone (no spatie) ​

Without spatie the field is a self-contained uploader: uploaded files land on the configured disk via the shared {prefix}/uploads/store endpoint, and you store the value array in your own column (e.g. a JSON gallery column).

The submitted array is client state, so don't store it as-is. Pass it through KinetixMedia::keep() with what the record already holds:

php
use Happones\Kinetix\Media\KinetixMedia;

$state = $form->getState($request->all());

$product->gallery = KinetixMedia::keep(
    $state['gallery'] ?? [],
    current: $product->gallery ?? [],
    directory: 'products', // the field's directory()
);

keep() returns the items in the submitted order:

  • an item already stored (matched by path in current) keeps its stored data;
  • a new item is kept only if it's a file the current user uploaded for this field (their upload directory) and it exists on the disk;
  • url, size and mime of a new item are read from the disk, never taken from the request. name is only a label: the original file name the upload reported (stored files are renamed), reduced to a plain single-line file name, or the stored name when none was sent;
  • anything else is dropped: another user's upload, a path outside the directory, a .. path, a missing file, a duplicate.

Removing an item from the array doesn't delete the file. Clean up stored files yourself if you need to.


With spatie/laravel-medialibrary ​

Install the package and make your model implement HasMedia:

bash
composer require spatie/laravel-medialibrary
php
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;

class Product extends Model implements HasMedia
{
    use InteractsWithMedia;

    public function registerMediaConversions(?Media $media = null): void
    {
        $this->addMediaConversion('thumb')->width(200)->height(200);
    }
}

Then hydrate the field from the collection and sync on save via the KinetixMedia helper:

php
use Happones\Kinetix\Media\KinetixMedia;

// edit() — fill the field from the model's media
$form = ProductResource::form(Form::make($product))->fill([
    'gallery' => KinetixMedia::items($product, 'images', 'thumb'),
]);

// update() — reconcile the collection with the submitted state
$state = $form->getState($request->all());
KinetixMedia::sync($product, 'images', $state['gallery'], directory: 'products');

sync() adds newly uploaded files, removes the ones the user deleted, and persists the new order — all on the bound collection. Pass the field's directory(): a new file is only accepted from the current user's uploads for it (when the field has no directory, the default uploads applies). It's a no-op when spatie isn't installed (or the record isn't HasMedia), so the same form code is safe either way.


API ​

MethodEffect
collection(string)spatie collection name / logical group
conversions([...])spatie conversion names surfaced as thumbnails
reorderable(bool)drag-to-reorder (default true)
image()restrict to images + thumbnail previews
maxFiles(int)cap the number of files
disk() · directory() · acceptedFileTypes() · maxSize()inherited from FileUpload

KinetixMedia::items($record, $collection, $conversion?) → the field's item array. KinetixMedia::sync($record, $collection, $items, $disk?, $directory?) → persist to spatie. KinetixMedia::keep($items, $current?, $directory?, $disk?) → the items safe to store in your own column.

Folders & native variants

v1 covers the grid, reordering, upload and the spatie bridge. Folders and native (non-spatie) image variants are not included yet — use spatie conversions for variants.

Released under the MIT License.