PHPackages                             awcodes/filament-curator - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Image &amp; Media](/categories/media)
4. /
5. awcodes/filament-curator

ActiveLibrary[Image &amp; Media](/categories/media)

awcodes/filament-curator
========================

A media picker plugin for FilamentPHP.

v5.1.1(4d ago)437356.9k—8.4%108[2 PRs](https://github.com/awcodes/filament-curator/pulls)17MITPHPPHP ^8.2CI passing

Since Apr 13Pushed 4d ago11 watchersCompare

[ Source](https://github.com/awcodes/filament-curator)[ Packagist](https://packagist.org/packages/awcodes/filament-curator)[ Docs](https://github.com/awcodes/filament-curator)[ GitHub Sponsors](https://github.com/awcodes)[ RSS](/packages/awcodes-filament-curator/feed)WikiDiscussions 5.x Synced 3d ago

READMEChangelog (10)Dependencies (40)Versions (257)Used By (17)

Filament Curator
================

[](#filament-curator)

A media picker/manager plugin for Filament Admin.

[![Latest Version](https://camo.githubusercontent.com/43476086851b5b42a6eceb370427b7f292248daf3f371280d4cd4a03b8016bdd/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f72656c656173652f6177636f6465732f66696c616d656e742d63757261746f722e7376673f7374796c653d666c61742d73717561726526636f6c6f723d626c7565266c6162656c3d52656c65617365)](https://github.com/awcodes/filament-curator/releases)[![MIT Licensed](https://camo.githubusercontent.com/a7e65aee57b11d28e4caff8b945729a66be0bb663f7f93bd24c5aa65699f148e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d626c75652e7376673f7374796c653d666c61742d737175617265)](LICENSE.md)[![Total Downloads](https://camo.githubusercontent.com/2e3bb5ad8a0956bb9f95b0a240471e34a3245a3b718ea13a007117fba4a68e6b/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6177636f6465732f66696c616d656e742d63757261746f722e7376673f7374796c653d666c61742d73717561726526636f6c6f723d626c7565266c6162656c3d446f776e6c6f616473)](https://packagist.org/packages/awcodes/filament-curator)[![GitHub Repo stars](https://camo.githubusercontent.com/4fa49e096b87d3b525a3355e0acac4a4e95bf7ccbbd2b778ea6b86ee0682d624/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f73746172732f6177636f6465732f66696c616d656e742d63757261746f723f7374796c653d666c61742d73717561726526636f6c6f723d626c7565266c6162656c3d5374617273)](https://github.com/awcodes/filament-curator/stargazers)

Warning

This package does not work with Spatie Media Library.

Compatibility
-------------

[](#compatibility)

Package VersionFilament Version1.x2.x2.x2.x3.x3.x4.x4.x5.x5.xInstallation
------------

[](#installation)

You can install the package via composer then run the installation command:

```
composer require awcodes/filament-curator
```

```
php artisan curator:install
```

Note

If you are using the stand-alone forms package then you will need to include the Curator modal in your layout file, typically you would place this, before the closing `body` tag.

```

```

Important

If you have not set up a custom theme and are using Filament Panels follow the instructions in the [Filament Docs](https://filamentphp.com/docs/4.x/styling/overview#creating-a-custom-theme) first.

After setting up a custom theme add the plugin's views and styles to your theme css file or your app's css file if using the standalone packages.

```
@import '../../../../vendor/awcodes/filament-curator/resources/css/plugin.css';

@source '../../../../vendor/awcodes/filament-curator/resources/**/*.blade.php';
```

Usage
-----

[](#usage)

### Global Settings

[](#global-settings)

Global settings can be managed through the plugin's config file. You can publish the config file using the following:

```
php artisan vendor:publish --tag="curator-config"
```

### With Filament Panels

[](#with-filament-panels)

If you are using Filament Panels you will need to add the Plugin to you Panel's configuration. This will register the plugin's resources with the Panel. All methods are optional, and will be read from the config file if not provided.

```
use Awcodes\Curator\CuratorPlugin;
use Filament\Support\Icons\Heroicon

public function panel(Panel $panel): Panel
{
    return $panel
        ->plugins([
            CuratorPlugin::make()
                ->label('Media')
                ->pluralLabel('Media')
                ->navigationIcon(Heroicon::OutlinedPhoto)
                ->navigationGroup('Content')
                ->navigationSort(3)
                ->showBadge(true)
                ->registerNavigation(true)
                ->curations(true)
                ->fileSwap(true),
        ]);
}
```

### Curator Picker Field

[](#curator-picker-field)

Include the CuratorPicker field in your forms to trigger the modal and either select an existing image or upload a new one. Some common methods from Filament's `FileUpload` component can be used to help with sizing, validation, etc. for specific instances of each CuratorPicker.

```
use Awcodes\Curator\Components\Forms\CuratorPicker;
use Filament\Support\Enums\Size;

CuratorPicker::make(string $fieldName)
    ->label(string $customLabel)
    ->buttonLabel(string | Htmlable | Closure $buttonLabel)
    ->color('primary|secondary|success|danger') // defaults to gray
    ->outlined(true|false) // defaults to true
    ->size(Size::Medium)
    ->constrained(true|false) // defaults to false (forces image to fit inside the preview area)
    ->pathGenerator(DatePathGenerator::class|UserPathGenerator::class) // see path generators below
    ->lazyLoad(bool | Closure $condition) // defaults to true
    ->listDisplay(bool | Closure $condition) // defaults to true
    ->tenantAware(bool | Closure $condition) // defaults to true
    ->defaultPanelSort(string | Closure $direction) // defaults to 'desc'
    // see https://filamentphp.com/docs/4.x/forms/file-upload for more information about the following methods
    ->preserveFilenames()
    ->maxWidth()
    ->minSize()
    ->maxSize()
    ->rules()
    ->acceptedFileTypes()
    ->disk()
    ->visibility()
    ->directory()
    ->imageCropAspectRatio()
    ->imageResizeTargetWidth()
    ->imageResizeTargetHeight()
    ->multiple() // required if using a relationship with multiple media
    ->relationship(string $relationshipName, string 'titleColumnName')
    ->orderColumn('order') // only necessary to rename the order column if using a relationship with multiple media
```

### Relationships

[](#relationships)

#### Single

[](#single)

Form component

```
CuratorPicker::make('featured_image_id')
    ->relationship('featured_image', 'id'),
```

Model

```
use Awcodes\Curator\Models\Media;

public function featuredImage(): BelongsTo
{
    return $this->belongsTo(Media::class, 'featured_image_id', 'id');
}
```

#### Multiple

[](#multiple)

Form component

```
CuratorPicker::make('product_picture_ids')
    ->multiple()
    ->relationship('product_pictures', 'id')
    ->orderColumn('order'), // only necessary if you need to rename the order column
```

Model

```
use Awcodes\Curator\Models\Media;

public function productPictures(): BelongsToMany
{
    return $this
        ->belongsToMany(Media::class, 'media_post', 'post_id', 'media_id')
        ->withPivot('order')
        ->orderBy('order');
}
```

### RichEditor Integration

[](#richeditor-integration)

Curator comes with built-in integration for Filament's RichEditor field.

```
use Awcodes\Curator\Components\Forms\RichEditor\AttachCuratorMediaPlugin;

\Filament\Forms\Components\RichEditor::make('content')
    ->tools([
        'attachCuratorMedia'
    ])
    ->plugins([
        AttachCuratorMediaPlugin::make(),
    ]),
```

### Path Generation

[](#path-generation)

By default, Curator will use the directory and disk set in the config to store your media. If you'd like to store the media in a different way Curator comes with Path Generators that can be used to modify the behavior. Just set the one you want to use globally in the config or per instance on your `CuratorPicker` field.

```
use Awcodes\Curator\View\Components\CuratorPicker;
use Awcodes\Curator\PathGenerators\DatePathGenerator;

public function register()
{
    CuratorPicker::make('image')
        ->pathGenerator(DatePathGenerator::class);
}
```

#### Available Generators

[](#available-generators)

- `DefaultPathGenerator` will save files in disk/directory.
- `DatePathGenerator` will save files in disk/directory/Y/m/d.
- `UserPathGenerator` will save files in disk/directory/user-auth-identifier

You are also free to use your own Path Generators by implementing the `PathGenerator` interface on your own classes.

```
use Awcodes\Curator\PathGenerators;

class CustomPathGenerator implements PathGenerator
{
    public function getPath(?string $baseDir = null): string
    {
        return ($baseDir ? $baseDir . '/' : '') . 'my/custom/path';
    }
}
```

### Curator Column

[](#curator-column)

To render your media in a table Curator comes with a `CuratorColumn` which has the same methods as Filament's ImageColumn.

```
CuratorColumn::make('featured_image')
    ->size(40)
```

For multiple images you can control the number of images shown, the ring size and the overlap.

```
CuratorColumn::make('product_pictures')
    ->ring(2) // options 0,1,2,4
    ->overlap(4) // options 0,2,3,4
    ->limit(3),
```

#### Relationships

[](#relationships-1)

If you are using a relationship to store your media then you will encounter n+1 issues on the column. In order to prevent this you should modify your table query to eager load the relationship.

For example when using the admin panel in your ListResource

```
protected function getTableQuery(): Builder
{
    return parent::getTableQuery()->with(['featured_image', 'product_pictures']);
}
```

Or, if you are using a Table class

```
public static function configure(Table $table): Table
{
    return $table
        ->modifyQueryUsing(fn (Builder $query) => $query->with('media', 'gallery'));
}
```

### Curations

[](#curations)

Curations are a way to create custom sizes and focal points for your images.

#### Curation Presets

[](#curation-presets)

If you have a curation that you are constantly using you can create Presets which will be available in the Curation modal for easier reuse. After creating curation presets, they can be referenced by their key to output them in your blade files.

```
use Awcodes\Curator\Curations\CurationPreset;
use Awcodes\Curator\Facades\Curation;

public function register(): void
{
    Curation::presets([
        CurationPreset::make('Thumbnail')
            ->height(200)
            ->format('webp')
            ->quality(80)
            ->width(200)
    ]);
}
```

### Glider Blade Component

[](#glider-blade-component)

To make it as easy as possible to output your media, Curator comes with an `` blade component.

See [Glide's quick reference](https://glide.thephpleague.com/2.0/api/quick-reference/) for more information about Glide's options.

**Special attributes**

- media: id (int) or model (Media) instance ***required***
- loading: defaults to 'lazy'
- glide: this can be used to pass in a glide query string if you do not want to use individual attributes
- srcset: this will output the necessary srcset with glide generated urls. Must be an array of srcset widths and requires the 'sizes' attribute to also be set.
- force: (bool) this can be used to force glider to return a signed url and is helpful when returning urls from cloud disks. This should be used with the knowledge that it could have performance implications.

```

```

#### Glider Fallback Images

[](#glider-fallback-images)

Glider allows for a fallback image to be used if the media item does not exist. This can be set by passing in the `fallback` attribute referencing one of your registered `GliderFallback`s.

```
use Awcodes\Curator\Glide\GliderFallback;
use Awcodes\Curator\Facades\Glide;

public function register(): void
{
    Glide::registerGliderFallbacks([
        GliderFallback::make('thumbnail')
            ->alt(string)
            ->height(int)
            ->source(string)
            ->type(string)
            ->width(int),
    ]);
}
```

Then you can reference your fallback in the blade component.

```

```

### Custom Glide Route

[](#custom-glide-route)

By default, Curator will use the route `curator` when serving images through Glide. If you want to change this you can update the `basePath` in a service provider.

```
use Awcodes\Curator\Facades\Glide;

public function register(): void
{
    Glide::basePath('media');
}
```

### Custom Glide Server

[](#custom-glide-server)

If you want to use your own Glide Server for handling served media with Glide you can pass the server config to the Glide facade in a service provider.

```
use Awcodes\Curator\Facades\Glide;

public function register(): void
{
    Glide::serverConfig([
        'driver' => 'imagick',
        'response' => new LaravelResponseFactory(app('request')),
        'source' => storage_path('app'),
        'source_path_prefix' => 'public',
        'cache' => storage_path('app'),
        'cache_path_prefix' => '.cache',
        'max_image_size' => 2000 * 2000,
    ]);
}
```

Important

**Using a cloud disk (S3, MinIO, etc.)?** The default config above points Glide's `source` at the local filesystem (`storage_path('app')` with `source_path_prefix => 'public'`). If your media lives on a cloud disk you **must** point the Glide `source` at that disk's Flysystem driver, otherwise Glide can't find the source images and they will fail to render.

```
use Awcodes\Curator\Facades\Glide;
use Illuminate\Support\Facades\Storage;

Glide::serverConfig([
    'response' => new LaravelResponseFactory(app('request')),
    'source' => Storage::disk('s3')->getDriver(),
    'source_path_prefix' => '', // see note below
    'cache' => Storage::disk('local')->getDriver(),
    'cache_path_prefix' => '.cache',
    'max_image_size' => 2000 * 2000,
]);
```

A few things to watch for:

- **`source_path_prefix`** must match where your objects actually live on the disk. Because a cloud disk's Flysystem is already rooted at the bucket (and your media `path` is stored relative to it), this is usually an empty string `''`. The `'public'` prefix in the default exists only because the local source is rooted at `storage_path('app')` while files live under `storage/app/public/`. A mismatched prefix is the most common cause of "images don't render" on cloud disks.
- **Keep `cache` on a fast local disk.** Transformed images are cached there, so only the first request per variant reads the source from the cloud. A cold cache on a remote source is slow; a warm local cache is fast.
- **Stray media on a different disk** (e.g. old records still on `public` while your source is S3) will fail source lookups and can slow things down — make sure existing records' `disk` matches your Glide source.

### Curation Blade Component

[](#curation-blade-component)

To make it as easy as possible to output your curations, Curator comes with an `` blade component.

**Special attributes**

- media: id (int) or model (Media) instance ***required***

```

```

### Practical use case

[](#practical-use-case)

Since curations may or may not exist for each media item it's good to use a fallback to the glider component in your blade file so images always get rendered appropriately. This also keeps you from having to create curations for every media item, only the ones where you're trying to change the focal point, etc.

```
@php
    $preset = new ThumbnailPreset();
@endphp

@if ($media->hasCuration('thumbnail'))

@else

@endif
```

### Custom Model

[](#custom-model)

If you want to use your own model for your media you can extend Curator's `Media` model with your own and set it in the config.

```
use Awcodes\Curator\Models\Media;

class CustomMedia extends Media
{
    protected $table = 'media';
}
```

```
'model' => \App\Models\Cms\Media::class,
```

Testing
-------

[](#testing)

```
composer test
```

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING](.github/CONTRIBUTING.md) for details.

Security Vulnerabilities
------------------------

[](#security-vulnerabilities)

Please review [our security policy](.github/SECURITY.md) on how to report security vulnerabilities.

Credits
-------

[](#credits)

- [Adam Weston](https://github.com/awcodes)
- [The PHP League](https://glide.thephpleague.com/) for the awesome Glide package.
- [Cropperjs](https://github.com/fengyuanchen/cropperjs) for their amazing Javascript package.
- [All Contributors](../../contributors)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

73

—

ExcellentBetter than 100% of packages

Maintenance99

Actively maintained with recent releases

Popularity59

Moderate usage in the ecosystem

Community42

Growing community involvement

Maturity81

Battle-tested with a long release history

 Bus Factor1

Top contributor holds 79.8% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~6 days

Recently: every ~0 days

Total

249

Last Release

4d ago

Major Versions

v4.0.6 → v5.0.62026-03-26

v4.0.7 → v5.0.72026-04-13

v4.0.8 → v5.0.82026-06-08

v4.1.0 → v5.1.02026-06-28

4.x-dev → v5.1.12026-06-30

PHP version history (4 changes)v0.0.1-alphaPHP ^8.0.2

v2.0.0PHP ^8.0

v3.0.0-alpha1PHP ^8.1

v4.0.0-alpha.1PHP ^8.2

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/3596800?v=4)[Adam Weston](/maintainers/awcodes)[@awcodes](https://github.com/awcodes)

---

Top Contributors

[![awcodes](https://avatars.githubusercontent.com/u/3596800?v=4)](https://github.com/awcodes "awcodes (545 commits)")[![howdu](https://avatars.githubusercontent.com/u/533658?v=4)](https://github.com/howdu "howdu (37 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (20 commits)")[![martinmildner](https://avatars.githubusercontent.com/u/789541?v=4)](https://github.com/martinmildner "martinmildner (11 commits)")[![martin-ro](https://avatars.githubusercontent.com/u/10107779?v=4)](https://github.com/martin-ro "martin-ro (11 commits)")[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (9 commits)")[![kirkbushell](https://avatars.githubusercontent.com/u/65171?v=4)](https://github.com/kirkbushell "kirkbushell (6 commits)")[![mszabeh](https://avatars.githubusercontent.com/u/53317316?v=4)](https://github.com/mszabeh "mszabeh (4 commits)")[![kuragehimekurara1](https://avatars.githubusercontent.com/u/98482562?v=4)](https://github.com/kuragehimekurara1 "kuragehimekurara1 (4 commits)")[![archilex](https://avatars.githubusercontent.com/u/6097099?v=4)](https://github.com/archilex "archilex (3 commits)")[![tlegenbayangali](https://avatars.githubusercontent.com/u/39906549?v=4)](https://github.com/tlegenbayangali "tlegenbayangali (3 commits)")[![dariox64](https://avatars.githubusercontent.com/u/86892155?v=4)](https://github.com/dariox64 "dariox64 (3 commits)")[![margarizaldi](https://avatars.githubusercontent.com/u/26832856?v=4)](https://github.com/margarizaldi "margarizaldi (3 commits)")[![mohamedsabil83](https://avatars.githubusercontent.com/u/10126040?v=4)](https://github.com/mohamedsabil83 "mohamedsabil83 (2 commits)")[![iotron](https://avatars.githubusercontent.com/u/50877415?v=4)](https://github.com/iotron "iotron (2 commits)")[![danharrin](https://avatars.githubusercontent.com/u/41773797?v=4)](https://github.com/danharrin "danharrin (2 commits)")[![shayan-yousefi](https://avatars.githubusercontent.com/u/19957980?v=4)](https://github.com/shayan-yousefi "shayan-yousefi (2 commits)")[![viraljetani](https://avatars.githubusercontent.com/u/1935495?v=4)](https://github.com/viraljetani "viraljetani (2 commits)")[![JarkaP](https://avatars.githubusercontent.com/u/3973865?v=4)](https://github.com/JarkaP "JarkaP (2 commits)")[![Log1x](https://avatars.githubusercontent.com/u/5745907?v=4)](https://github.com/Log1x "Log1x (2 commits)")

---

Tags

filamentfilament-pluginpluginlaravelfilamentawcodescurator

###  Code Quality

TestsPest

Static AnalysisRector

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/awcodes-filament-curator/health.svg)

```
[![Health](https://phpackages.com/badges/awcodes-filament-curator/health.svg)](https://phpackages.com/packages/awcodes-filament-curator)
```

###  Alternatives

[rawilk/profile-filament-plugin

Profile &amp; MFA starter kit for filament.

3914.6k](/packages/rawilk-profile-filament-plugin)[stephenjude/filament-jetstream

A Laravel starter kit built with Filament inspired by Jetstream.

17760.2k3](/packages/stephenjude-filament-jetstream)[stephenjude/filament-two-factor-authentication

Filament Two Factor Authentication: Google 2FA + Passkey Authentication

84215.9k9](/packages/stephenjude-filament-two-factor-authentication)[croustibat/filament-jobs-monitor

Background Jobs monitoring like Horizon for all drivers for FilamentPHP

274326.6k8](/packages/croustibat-filament-jobs-monitor)[marcelweidum/filament-passkeys

Use passkeys in your filamentphp app

6649.5k1](/packages/marcelweidum-filament-passkeys)[relaticle/custom-fields

User Defined Custom Fields for Laravel Filament

16354.2k](/packages/relaticle-custom-fields)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
