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

Laravel Filemanager Laravel Package

unisharp/laravel-filemanager

A responsive file manager for Laravel with a clean, mobile-friendly UI. Browse, upload, crop, resize, and organize files and folders, then integrate it with any WYSIWYG editor via CKEditor, TinyMCE, or Summernote adapters, with configurable storage and access control.

View on GitHub
Deep Wiki
Context7

Getting Started

Where to look first

  1. config/lfm.php after publishing — it is the real API surface of this package. Disks, folder paths, per-category mime/size rules, multi-user mode, routes and middleware all live here. Read it top to bottom before writing a line of integration code; most "the package is broken" reports are a config key set to the wrong value.
  2. The docs site linked in the README (installation, integration, config, customization, events) — the README itself is a feature list, not a guide.
  3. The Events page — this is the extension seam you will actually use in production (post-upload processing, audit logging, cache busting).

Minimal setup

composer require unisharp/laravel-filemanager
php artisan vendor:publish --tag=lfm_config
php artisan vendor:publish --tag=lfm_public
php artisan storage:link   # only for the local "public" disk

Register the routes. The default use_package_routes behaviour gives you web middleware only, which is almost never what you want in an admin app — prefer explicit routes:

// routes/web.php
Route::group([
    'prefix' => 'laravel-filemanager',
    'middleware' => ['web', 'auth', 'can:manage-media'],
], function () {
    \UniSharp\LaravelFilemanager\Lfm::routes();
});

Then set 'use_package_routes' => false in config/lfm.php so you don't get two competing registrations, and confirm the disk, folder_path (images / files), and middlewares keys match your app.

First use case to prove out: a thumbnail picker on a blog post form

This exercises the whole loop — popup UI → selection callback → persisted path → rendered URL — in about fifteen minutes, and it's the pattern 90% of teams actually need:

<div class="input-group">
  <span class="input-group-btn">
    <a id="lfm" data-input="thumbnail" data-preview="holder" class="btn btn-primary">
      Choose
    </a>
  </span>
  <input id="thumbnail" class="form-control" type="text" name="image" value="{{ old('image', $post->image) }}">
</div>
<img id="holder" style="margin-top:15px;max-height:100px;" src="{{ $post->image ? Storage::disk(config('lfm.disk'))->url($post->image) : '' }}">

@push('scripts')
<script src="{{ asset('vendor/laravel-filemanager/js/stand-alone-button.js') }}"></script>
<script>
  $('#lfm').filemanager('image', { prefix: '/laravel-filemanager' });
</script>
@endpush

If that works end to end, everything else is configuration. If the image renders as a broken link, you have a disk/URL problem, not a filemanager problem — see Gotchas.

Implementation Patterns

WYSIWYG integration (the primary use case)

The package is designed as an editor's file browser. For CKEditor 4 style config:

CKEDITOR.replace('editor', {
  filebrowserImageBrowseUrl: '/laravel-filemanager?type=Images',
  filebrowserImageUploadUrl: '/laravel-filemanager/upload?type=Images&_token={{ csrf_token() }}',
  filebrowserBrowseUrl: '/laravel-filemanager?type=Files',
  filebrowserUploadUrl: '/laravel-filemanager/upload?type=Files&_token={{ csrf_token() }}'
});

For TinyMCE, use file_picker_callback with a lfm() wrapper that opens the popup and resolves via window.SetUrl, then pass the returned path into cb(url, {alt: path}). Keep the lfm() helper in one shared JS file rather than duplicating it per editor — CKEditor 5 has no native file browser and needs a custom button/plugin, so budget a little extra time there compared to CKEditor 4 / TinyMCE.

The field_id + SetUrl contract

Two mechanisms make this package composable; understand both:

  • ?field_id=thumbnail — the popup writes the chosen path into #thumbnail on the opener window. Zero JS glue needed for simple forms.
  • window.SetUrl = function(url, path) {} — the popup calls this on the opener for callback-driven flows (TinyMCE, multi-field forms, React/Vue islands listening on a custom event).

?type=Images vs ?type=Files selects the category, which selects the folder path and validation rules. Mixing them up is the most common cause of "uploaded file doesn't appear in the list".

Persist paths, not URLs

Store the relative path returned by the manager (photos/2026/06/hero.jpg) in a string column and resolve the URL at render time through Storage::disk(config('lfm.disk'))->url($path). This is what makes the local-disk-now / S3-later migration a config change instead of a data migration. Add an accessor or a small MediaPath cast so templates never touch Storage directly.

Multi-user / private folders

Enable allow_multi_user, set user_field to a stable, unique, URL-safe attribute (not name if names are editable), and decide allow_shared_folder per category — since v2.5.0 this can be set per folder category, which is useful (shared files/, private images/). The package gives you the folder structure; it does not give you the tenant boundary. You still own the policy that stops user A reading user B's path, and the default routes ship without auth.

Events as the extension point

Rather than forking the package, hook the lifecycle: ImageIsUploading / FileIsUploading (validate, scan, reject), ImageWasUploaded / FileWasUploaded (store a Media row, push to a queue, generate responsive variants), ImageIsResizing / ImageIsCropping (custom filter chains), plus rename/remove/move events for audit trails. Register listeners in EventServiceProvider (or Event::listen in a dedicated provider) and keep them idempotent — retries after a failed thumbnail job will re-fire them.

Customization ladder, cheapest first

  1. Config only (paths, mimes, sizes, thumbnails, localization via app.locale).
  2. Publish and override Blade views under resources/views/vendor/laravel-filemanager for branding/layout.
  3. Custom routes + middleware (the normal case).
  4. Events for behaviour.
  5. Only then patch internals — and prefer wrapping a controller of your own over editing vendor code.

Testing

Treat it as an HTTP surface: feature-test your own routes and policies, assert the manager's routes are behind auth, and mock/Storage::fake() around the upload endpoint rather than driving the JS UI. Image manipulation paths need the gd (or imagick) extension present in CI — a green local suite and a red CI suite is usually a missing image driver.

Gotchas and Tips

Authentication is your job, not the package's. With use_package_routes enabled the package's routes get web middleware — meaning an anonymous visitor can reach the browser UI. Always pin an auth/authorization middleware, and add a test that asserts a guest gets redirected or 403. This is the single highest-risk misconfiguration.

Executable uploads have a patchy history. v2.5.1, v2.6.2 and v2.9.1 all shipped hardening for executable extensions, and v2.15.1 blocks empty extensions. Treat these as defense in depth, not your defense: set an explicit valid_mime/extension allowlist per category in config, and serve user content from a disk/domain that never executes PHP.

Pin intervention/image deliberately. The major-version axis has been genuinely unstable: v2.7.0 supported 2.x, v2.8.1 removed 3.* due to a compatibility break, v2.11.0 moved to 3.x only, v2.15.0 added 4.x. Check the filemanager version ↔ intervention version pair in the release notes before composer update, and remember v2.11.0+ requires PHP 8.1+ and supports Laravel up to 13.

Broken thumbnails / 404 images. Almost always one of: no storage:link, disk pointing at a disk without a public URL, or APP_URL wrong so the generated S3/local URL is unreachable. Debug by resolving a known path manually through Storage::disk(...)->url(...) outside the package.

Popup blockers. window.open must be inside a user gesture — never open the manager from an async callback after a fetch. Also verify the _token query param on upload URLs; dropzone/token handling has been patched more than once (v2.12.0), so keep the package current if you use drag-and-drop inside a modal.

Resize/crop is destructive by default. Users can overwrite originals from the UI. Use the "resize as new image" feature (v2.9.0+) where available, or gate crop/resize behind a permission, or keep originals on a separate read-only-by-policy disk.

Thumbnails and cloud disks cost money per listing. Thumbnail creation plus directory mtime lookups on S3 has caused real performance complaints (fixed incrementally in v2.1.0 and v2.12.0). If you're on S3: enable the cached adapter behaviour, keep pagination on, and avoid huge flat folders — the UI searches only the current working directory, so structure matters.

Filename hygiene. Set alphanumeric_filename or a rename strategy if you have non-Latin filenames; unicode and spaces in URLs break naive <img src> concatenation. Also be aware the package slugs/renames folders — don't encode business meaning in a folder name and expect it to survive.

Multi-user folder orphaning. Private folders are keyed off user_field. If that value changes (username edit) or a user is deleted, files are stranded and re-login lands the user in an empty folder. Use an immutable ID, and write a small backfill command before you ever rename.

Storage cache staleness. Files added outside the package (queue jobs, other tools) may not appear until the filesystem cache invalidates. If "my uploaded file is missing" only reproduces with a cached adapter, that's the cause — clear the cache or bypass the adapter for that disk.

Upgrades are not cosmetic. The v2.0 line restructured routes, repositories and namespacing; config keys have been added per-release (allow_private_folder, reverse ordering, routing config, search). Read the upgrade doc, diff your published config/lfm.php against the new stub with vendor:publish --force into a scratch dir, and re-publish assets — stale JS assets in public/vendor/laravel-filemanager are a classic "the fix didn't apply" trap.

Windows/case sensitivity. Windows path handling and file-move bugs have been fixed repeatedly (v2.0.0-alpha7, v2.12.0). Keep the package updated and use forward slashes in any config path you supply — a trailing backslash or mixed separator will silently break folder resolution.

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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package