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

Filament Custom Attribute File Upload Laravel Package

elmudo-dev/filament-custom-attribute-file-upload

A Filament plugin for Laravel that adds file upload support to custom attributes, making it easy to attach, store, and manage uploaded files through Filament forms and resources in admin panels. Designed for Filament admin panels, it simplifies adding file fields to custom attributes without custom...

View on GitHub
Deep Wiki
Context7

Getting Started

1. Confirm compatibility before anything else. The package is small (8 stars, 0 dependents), so its composer.json is the real spec. Check the Filament constraint (^3.0 vs ^2.0) and the Laravel/PHP floor. Match it to your app before running composer require; a loose constraint here is the usual source of "works on my machine."

composer require elmudo-dev/filament-custom-attribute-file-upload

2. Read the source, not just the README. For a package this size the source is the documentation. Open, in order:

  • src/ — the field/component class. Look at what it extends (Filament\Forms\Components\FileUpload most likely) and which public setters it exposes.
  • resources/views/ — the Blade partial that renders the caption input next to the upload. This tells you exactly what markup and Livewire bindings you're getting.
  • config/ and the service provider — whether there is anything to publish.

3. Publish what's offered, if anything.

php artisan vendor:publish --tag="filament-custom-attribute-file-upload-config"
php artisan vendor:publish --tag="filament-custom-attribute-file-upload-views"

If the service provider registers a Filament Plugin, add it to your panel provider (->plugins([...]) in AdminPanelProvider). If it's only a form component (likely, given the description), there is nothing to register — just import the class in your Resource.

4. First use case — a captioned cover image. The canonical scenario is a Post resource where the editor uploads a cover and types a caption/alt text that travels with the file. Swap your existing field:

use ElmudoDev\FilamentCustomAttributeFileUpload\Forms\Components\CustomAttributeFileUpload;

public static function form(Form $form): Form
{
    return $form->schema([
        CustomAttributeFileUpload::make('cover')
            ->image()
            ->directory('posts/covers'),
        // ... rest of the form
    ]);
}

Confirm the exact namespace and setter names against src/ — with a package this young, the README and the class name may drift. The first render should show your normal FileUpload UI plus a per-file text input for the caption.

Implementation Patterns

Store the attribute where the file already lives. The component's value is only useful if the caption survives edit/reload. Two shapes are common and you should pick deliberately:

  • JSON on the model — a cover_meta (or similar) json column cast to array, holding ['path' => ..., 'caption' => ...].
  • Spatie Media Library custom properties — $media->setCustomProperty('caption', $value)->save(), which composes naturally if you already use SpatieMediaLibraryFileUpload.

Check which one the package assumes (look at the dehydrated() / saveUploadedFileUsing() logic) before designing your migration.

Render it in Blade from a single accessor. Keep templates dumb:

// Post.php
public function coverCaption(): ?string
{
    return data_get($this->cover, 'caption');
}
<figure>
    <img src="{{ Storage::url($post->cover['path']) }}" alt="{{ $post->coverCaption() }}">
    <figcaption>{{ $post->coverCaption() }}</figcaption>
</figure>

Multiple files / repeaters. For galleries, the field typically needs to run inside a Repeater so each item owns its caption, or the component needs to iterate state. Verify the state shape in the Livewire payload ($wire.get('data.cover')) before wiring the caption back — arrays of TemporaryUploadedFile vs stored paths behave differently on create vs edit.

Table previews. Add a TextColumn for the caption and an ImageColumn for the file, rather than trying to render the component in the table. Filament table columns are read-only; the upload component belongs in forms only.

Validation. Caption length is a content concern, not a file concern — put ->maxLength(120) and any required rules on the attribute in your form schema, and mirror them in a Form Request if the same model is written outside Filament.

Testing. Use livewire(EditPost::class)->fillForm([...])->call('save') and assert the caption column/JSON matches. Because the package adds a second input, assert on the persisted structure, not the rendered HTML.

Gotchas and Tips

  • Audit the release metadata. A last release dated in the future (2026) and zero dependents means you are the de facto QA. Pin the exact version in composer.json, read the diff of any bump, and consider vendoring the component into app/Forms/Components if you only need the caption input — that removes the maintenance risk entirely.
  • Orphaned captions on replace. Replacing an image often leaves the old caption in state because Filament's FileUpload keeps state across the delete+upload cycle. Explicitly reset the attribute when the file path changes, or you'll ship a caption describing the wrong photo.
  • Local disk vs S3 in Livewire. Captions are plain state and upload fine either way, but remember FileUpload writes to livewire.temporary_file_upload.disk first. Don't validate the caption against the temp file's existence — validate it independently.
  • Stale views after publish. If you publish and edit the Blade partial, clear views (php artisan view:clear). Filament caches compiled component views in some setups, and a "my change didn't apply" bug is almost always this.
  • Disk and directory on the attribute input. The component inherits ->disk() / ->directory() from FileUpload; if captions "disappear," confirm storage succeeded first (Storage::disk(...)->exists($path)), then check the JSON, then the Livewire state — in that order.
  • Debugging in three places. (1) $wire.get('data') in the browser console for state shape, (2) the DB row for persistence, (3) ->dd() on the component's afterStateUpdated for the moment the caption attaches. The failure is nearly always between (1) and (2).
  • Extension points. Extend the component class rather than editing published views: override the setter for the attribute name, add your own ->captionLabel(), and keep the base view untouched so future patches apply cleanly. If the package exposes macros, register them in a service provider so the customization is discoverable.
  • Compatibility drift. Filament 3 → 4 will change FileUpload internals; a component that subclasses it will break silently (form renders, caption never saves). Add a test asserting the caption persists so a Filament upgrade fails loudly.
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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console