chengkangzai/filament-qrcode-scanner-html5
A Filament form component that adds an HTML5 QR code scanner using the device camera. Scan codes directly in the browser and populate form fields without a native app. Easy to drop into Filament forms for quick, mobile-friendly scanning workflows.
This package gives a Filament form field a camera affix action that opens a scanner modal and writes the decoded barcode back into the field via $wire.set(). Install it, then attach BarcodeScannerAction to a text input's suffix/prefix action:
use Vendor\BarcodeScanner\Actions\BarcodeScannerAction;
TextInput::make('sku')
->suffixAction(BarcodeScannerAction::make()),
Look first at the action class itself for its configuration surface (make() arguments, accepted formats, modal settings) and at the field it's attached to — the action is a plain Filament affix action, so everything you know about affix actions (icons, tooltips, visibility, ->visible()/->hidden()) applies.
First useful case: a barcode/SKU field on a create or edit form where a warehouse operator scans a label instead of typing. Add the action, scan once, confirm the value lands in the field and saves.
If you're upgrading from before v2.1.3, the one behaviour change to know is that the action now hides itself when its field is disabled (including inherited disabled state from a container, and including the edit form rendered on a ViewRecord page). Nothing to configure — just expect the camera button to disappear on read-only forms.
Basic affix action on a text input. The common wiring is ->suffixAction(BarcodeScannerAction::make()) on a TextInput. Keep validation on the field (->rules(), ->unique()) rather than in the action; the action only sets the value, all normal Filament validation still runs on save.
Scan-only fields. When you want to block manual typing but still persist the scanned value, you disable the field and opt back into the button:
TextInput::make('barcode')
->disabled()
->dehydrated()
->suffixAction(BarcodeScannerAction::make()->hideWhenFieldDisabled()),
->dehydrated() is essential here — a disabled field is normally stripped from the payload, so without it the scan appears to work and then silently never saves. This is exactly the trap the default guard in v2.1.3 was introduced to prevent.
Dynamically disabled fields. Because the guard now cascades from containers, you can wrap fields in a Section/Fieldset with ->disabled($locked) and the camera buttons disappear with everything else. That's the tidy way to model "read-only until the user clicks Edit."
Conditional visibility. Use Filament's own ->visible(fn ($record) => ...) on the action for business rules (e.g. only show the scanner for certain product types). Don't reach for ->hidden() to express "field is disabled" — that's what the new default guard is for, and it's implemented in isHidden() so your own ->hidden() condition is still honoured independently.
Multiple fields, one action type. Define a small factory or a custom action class in your app that wraps BarcodeScannerAction::make() with your house defaults (icon, modal heading, scan format), then reuse it across resources. Keeps a single place to change when the package's fluent API shifts.
View pages. If you render the edit form on a ViewRecord, fields come through disabled. Verify after upgrading that any scanner you did want on a view page is either re-enabled explicitly or given hideWhenFieldDisabled() plus dehydrated() if scanning there is intentional (rare — usually you want a dedicated action instead).
isVisible() only and never cascades disabled. If you're pinned below v2.1.3, guard manually with ->visible(fn (Get $get) => ...) or upgrade.->dehydrated().hideWhenFieldDisabled() only affects visibility. It doesn't make the field save. Pair it with ->dehydrated() every time, and be aware you're taking responsibility for a field the user can't correct by hand.->hidden() expecting to override the guard. The package overrides isHidden() rather than calling ->hidden() internally specifically so your own hidden condition doesn't get clobbered — but the two combine, so a field that's both disabled and ->hidden() will still be hidden.disabled() (including parent containers), then the action's visible()/hidden() conditions, then hideWhenFieldDisabled(). The action's own isHidden() is the final arbiter.$wire.set() without checking field state, so any future guard needs to live in visibility, not in the write path. Treat "action is hidden" as the only thing protecting a disabled field.^2.1.3 is the safe constraint once you've audited your disabled fields.How can I help you explore Laravel packages today?