Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Elfinder Bundle Laravel Package

barryvdh/elfinder-bundle

Laravel bundle that integrates the elFinder file manager into your application. Browse, upload, edit and manage files across local and remote filesystems via Flysystem, with thumbnail previews, archive support and easy CKEditor integration.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install and let auto-discovery do its job.

    composer require barryvdh/laravel-elfinder
    

    Laravel discovers Barryvdh\Elfinder\ElfinderServiceProvider; you should not need to add it to config/app.php on any modern Laravel version. If you do register it manually (older apps, or you've disabled discovery), remove the duplicate registration — double registration causes duplicate route names and a confusing Route [elfinder.show] not defined / "already defined" mix.

  2. Publish config and assets — these are two separate things.

    php artisan vendor:publish --provider="Barryvdh\Elfinder\ElfinderServiceProvider"
    

    This gives you config/elfinder.php and copies elFinder's client bundle into public/vendor/elfinder. If the file manager loads but renders a white box, an unstyled shell, or the browser console shows 404s for elfinder.min.js, you published the config but not the assets. Check public/vendor/elfinder exists and contains js/, css/, img/, and sounds/.

  3. Read config/elfinder.php before writing any code. It is the whole package in one array:

    • roots — the volumes elFinder can browse (one entry per disk/directory).
    • route (or the route group config, depending on version) — where the manager page and connector are mounted.
    • middleware — if your version exposes it, the auth layer wrapping the routes.
    • dir / asset path — where the client assets are served from.

    There is no meaningful business logic to learn; the config array is the API.

  4. First working use case — browse one directory. Point a single root at a storage path and visit /elfinder:

    // config/elfinder.php
    'roots' => [
        [
            'driver' => 'LocalFileSystem',
            'path'   => storage_path('app/public'),
            'URL'    => '/storage',
        ],
    ],
    

    Then php artisan storage:link so /storage actually resolves, and log in as an authorized user. You should see the file tree, upload, rename, and download working immediately.

  5. When you outgrow LocalFileSystem, install elfinder-flysystem-driver and switch the root to a Laravel disk ('driver' => 'Flysystem', 'disk' => 'public'). This is the same move that lets you point elFinder at S3 or any other Flysystem-backed disk. Note that v0.6.0 bumped this driver's version constraint — if you already pull league/flysystem-* in the app, run composer update --dry-run before committing, because a Flysystem major mismatch is the most likely upgrade failure.

Implementation Patterns

Multiple volumes = multiple roots entries. Each root gets its own alias (the label shown in elFinder's UI), its own driver, and its own permission set. This is how you expose "Public Images" and "Private Documents" side by side without building a second UI:

'roots' => [
    [
        'driver' => 'Flysystem',
        'disk'   => 'public',
        'alias'  => 'Public Media',
        'URL'    => '/storage',
    ],
    [
        'driver' => 'Flysystem',
        'disk'   => 's3-assets',
        'alias'  => 'S3 Assets',
        'defaults' => ['read' => true, 'write' => false],
    ],
],

The alias must be unique — duplicate aliases cause elFinder to silently drop or overwrite a volume, which looks like "my second root just doesn't appear."

Lock down write access per root. defaults (read, write, locked, hidden) is the intended lever. Use 'write' => false for a read-only asset library and 'hidden' => true for volumes referenced by files but not browsable.

Per-user roots. Config is static, so the standard pattern is to compute roots at runtime in a service provider or by publishing into config() inside a middleware before the manager route runs:

// app/Providers/AppServiceProvider.php (boot)
if (request()->is('elfinder*') && auth()->check()) {
    config(['elfinder.roots' => $this->rootsForUser(auth()->user())]);
}

Guard it behind the same auth as the route so unauthenticated requests never reach the config mutation.

Embedding in a WYSIWYG / "browse server" button. The package ships popup/editor views (elfinder::popup, and the CKEditor/TinyMCE variants) plus matching route names. The pattern is: open the popup with the elFinder JS filePicker callback, receive the selected file's URL back into the field. This is the highest-value integration for CMS work — you get a media picker for free instead of writing an upload modal.

Restrict uploads at the connector, not just in JS. Set acceptedMimeTypes on the root and/or use the disabled array on the connector config to turn off risky commands (archive, extract, chmod, mkfile) that you don't need. Client-side validation in elFinder is cosmetic; the connector is where the decision is enforced.

Customize the client via clientOptions. Toolbar layout, contextmenu, ui, locale, and default view are all set here rather than by editing published JS (which you'd lose on the next vendor:publish).

Listen to elFinder's JS events (elFinder:upload, elFinder:added, elFinder:removed) from your own page to sync file paths back into an Eloquent model. Do the DB write via your own endpoint — don't try to shoehorn it into the connector.

Gotchas and Tips

  • The routes are not auth-protected by default. This is the single most important thing to verify. /elfinder/connector is a read/write filesystem API — if it's reachable by an unauthenticated request, so is your storage. Wrap the manager and connector in an admin middleware (auth, a gate, or an IP allowlist) and confirm by hitting the connector in an incognito tab.

  • Don't strip the web middleware group to work around CSRF. The connector POSTs include a CSRF token; removing web breaks uploads with vague 403s. If you must relax middleware, relax auth, not CSRF.

  • URL must match reality. A wrong URL gives a working file tree with broken thumbnails and broken preview/download links — a confusing half-broken state that looks like a bug in elFinder rather than a config mismatch. Verify the storage:link target and the URL prefix agree.

  • Config is cached. After editing config/elfinder.php on a production-like environment, php artisan config:cache (or config:clear) or your changes silently do nothing.

  • Republish assets after upgrades. The published public/vendor/elfinder copy is frozen at publish time. If you bump the package and the UI misbehaves (missing dialogs, JS errors), republish with --force. Also re-check for stale copies if asset paths moved.

  • Debug the connector directly, not through the UI. GET /elfinder/connector?cmd=open returns raw JSON. When the UI hangs, this tells you instantly whether the problem is server-side (PHP exception, disk permission, path wrong) or client-side (assets/JS).

  • Thumbnails need a writable tmbPath and GD. If thumbnails 404 or show placeholders, the temp/thumbnail directory isn't writable or the gd extension is missing. It's never a Laravel problem.

  • path must be absolute. storage_path() and base_path() are fine; 'app/public' is not.

  • v0.6.0 refactored the Laravel-specific layer (PR #320). If you subclassed the controller, overrode service-provider bindings, or relied on internal class names, diff your overrides before upgrading. Prefer extending via config and a thin custom controller over editing package internals — every internal you touch is an upgrade cost. Pin 0.5.5 in production until you've diffed the provider, config, and routes and run the smoke path: browse, upload, rename, delete, and one permission-denied case.

  • Large directories are slow by design. elFinder lists eagerly. For S3 or network disks, cap depth/size at the root config level or you'll get connector timeouts that look like flaky uploads.

  • Extension point hierarchy, in order of preference: config/elfinder.php → clientOptions → your own route wrapper → a custom controller extending the package's → forking internals. Stay as high on that list as possible.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core