The standalone page
The plugin registers a panel-level explorer page and, optionally, a flat files table beside it. No Eloquent record is involved: the page gets its scope key and root folder from a resolver.
Config or plugin
Everything in the standalone config block can also be set fluently on the plugin, and the plugin wins. That is not a convenience — it is what keeps the settings panel-scoped:
FilamentFileExplorerPlugin::make()
->slug('library') // route: /{panel}/library
->scopeKey('shared-library') // namespace handed to the authorizer
->rootFolder('Shared library') // root folder name
->navigationLabel('Library')
->navigationIcon('heroicon-o-folder-open')
->navigationGroup('Content')
->navigationSort(20)
->withFilesNavigation() // also show the flat files table in the menu
->authorizer(\App\Support\FileExplorerAuthorizer::class)
An application with an admin panel and a customer panel can give each its own library, its own authorizer and its own quota without either of them mutating global config. Where there is no panel at all — a console command, a test — the reader falls back to config.
One consequence worth knowing. A command has no panel, so a fluent setting is invisible from the console.
php artisan file-explorer:demoprints the scope and root it used for exactly that reason: if your panel calls->scopeKey('docs')rather than setting the config key, pass--scopeexplicitly. See Commands.
Behaviour, not just chrome
The same rule covers the settings that change what the explorer does:
FilamentFileExplorerPlugin::make()
->quota(10 * 1024 ** 3) // bytes this scope may hold
->refreshEvery(20) // seconds between automatic refreshes
->defaultViewMode('columns') // grid, columns or details
->maxFolderDepth(6)
->storageWidget() // the dashboard widget
->tableColumns(fn (array $columns) => Arr::except($columns, ['preview']))
->quota(null) and ->refreshEvery(null) mean no limit rather than no opinion, so they can lift a limit config had set.
->tableColumns() takes a closure and so cannot live in a config file — it is plugin-only. The columns arrive keyed by name, so the callback can add, drop or reorder them.
The full set of switches
| Method | |
|---|---|
->slug($slug, $filesSlug = null) |
Route slugs, relative to the panel path |
->scopeKey($key) |
Authorization and session namespace |
->rootFolder($name, $slug = null) |
Name of the root folder created on first visit |
->rootResolver($class) |
Where the scope key and root come from — see resolvers |
->authorizer($class) |
Your FileExplorerAuthorizer |
->explorerPage($class) / ->filesPage($class) |
Your own page classes |
->quota($bytes) |
Bytes the scope may hold, null for no cap |
->refreshEvery($seconds) |
Automatic refresh, null for none |
->defaultViewMode($mode) |
grid, columns or details |
->maxFolderDepth($depth) |
How deep the tree may go |
->tableColumns($callback) |
Adjust the flat files table’s columns |
->storageWidget() / ->withoutStorageWidget() |
The dashboard widget |
->navigationLabel() ->navigationIcon() ->navigationGroup() ->navigationSort() |
Menu entry |
->withFilesNavigation() |
Also show the files table in the menu |
->withoutFilesPage() |
Register the explorer page only |
->withoutNavigation() |
Register the pages, hide the menu entries |
->withoutPages() |
Register nothing; you register the page classes yourself |
->disabled() |
Turn the standalone page off entirely |
Replacing the pages
The packaged pages are enough for most applications. To customise one — a different heading, an extra widget, your own canAccess() — generate it:
php artisan filament-file-explorer:make-page --standalone
then point the plugin at what was generated:
FilamentFileExplorerPlugin::make()
->explorerPage(\App\Filament\Pages\FileExplorer::class)
->filesPage(\App\Filament\Pages\FileExplorerFiles::class)
The generated classes extend the package’s abstract pages, so they inherit the scope resolution and both guards. What they add is yours.
The files table
Beside the explorer, a flat table of every file in the scope — sortable and searchable across folders, which is the one thing a tree cannot do. Off in the navigation by default (->withFilesNavigation() shows it), and it obeys the same containment rule as everything else: a row is only listed if its folder sits under the scope’s root.
Its columns go through ->tableColumns(), keyed by name:
->tableColumns(fn (array $columns) => Arr::except($columns, ['preview', 'uploader']))
The dashboard widget
What the standalone scope holds, on the panel’s dashboard — the same figures the explorer’s sidebar draws, where someone sees them without going to the explorer first. A quota nobody sees until it refuses an upload is a quota that surprises people.
FilamentFileExplorerPlugin::make()->storageWidget()
Off by default: a widget appearing on someone’s dashboard the day they upgrade would be a surprise, and a dashboard is not the package’s. With a quota set it draws the bar and warns at 85%; without one it shows the total and the file count, which is the useful half — a bar with nothing to fill would only invent a limit.
It describes the standalone scope only. Record-scoped pages have one root per record, so there is no single figure a dashboard widget could stand for; read Support\Quota directly for a record you know. And it hides itself when the scope is refused, when browse is denied, and before the scope has a root at all — it never creates one.