
Summary
In-browser voice recorder with real-time frequency visualizer, inline playback (waveform + play/pause), and FilamentFileUpload storage integration. Records audio from the microphone, previews locally, then uploads to Livewire temporary storage and persists to disk on form save.
| Class | Bjanczak\FilamentFlexFields\Filament\Forms\Components\VoiceNoteRecorderField |
| State type | string|null — stored path on disk after save; keyed TemporaryUploadedFile object during upload |
| FieldType | — (use the PHP class directly; not mapped via FieldType) |
| Extends | FlexFileUpload → Filament\Forms\Components\FileUpload |
Basic usage
Default — upload on form submit
Recording stays in the browser until the form is submitted. A loader is shown while the file uploads before save.Immediate upload after recording
Uploads to Livewire temporary storage right after recording stops. Delete removes the file from storage (whendeleteFileOnRemove() is enabled — default in setUp()).
Upload flow
| Stage | What happens |
|---|---|
| Record | Audio captured in the browser (MediaRecorder); playback uses a local blob URL |
| JS upload | $wire.upload('{statePath}.{uuid}', file) — file lands in Livewire temp (config/livewire.php → temporary_file_upload) |
| Form save | Filament beforeStateDehydrated → saveUploadedFiles() moves/stores the file to disk() + directory() |
| Persisted state | Relative path string, e.g. voice-notes/01H….webm |
FileUpload API:
State format
| Phase | State shape | Example |
|---|---|---|
| Empty | null | — |
| After JS upload (before save) | Associative array keyed by UUID | ['a1b2…' => TemporaryUploadedFile] |
| After form dehydrate / save | string — path on disk() | 'voice-notes/01H….webm' |
| Existing record (edit) | string path | Loaded for playback via getInitialAudioUrl() |
Validation
| Rule | Detail |
|---|---|
required() | Recording must be present before submit |
Inherited FileUpload | maxSize(), acceptedFileTypes(), etc. |
setUp()): audio/*, audio/mpeg, audio/wav, audio/webm, audio/ogg, audio/x-m4a, audio/aac.
Configuration API
maxDuration(int|Closure $seconds)
Maximum recording length in seconds. Timer stops recording automatically. Default: 120 (2 minutes).
uploadImmediately(bool|Closure $condition = true)
Upload to Livewire temp storage immediately after recording. Playback stays visible; background upload progress is shown on the pill.
uploadOnSubmit(bool|Closure $condition = true)
Defer upload until form submit (default). Shows “Preparing voice note for save…” while uploading on submit.
Icon overrides
| Method | Default (Gravity UI) | Config key (filament-flex-fields.ui) |
|---|---|---|
playIcon() | PlayFill | audio_play_icon |
pauseIcon() | PauseFill | audio_pause_icon |
microphoneIcon() | Microphone | microphone_icon |
stopIcon() | Minus | stop_icon |
trashIcon() | TrashBin | trash_icon |
checkmarkIcon() | Check | checkmark_icon |
size(string|ControlSize|Closure $size)
sm, md, lg. Inherited from FlexFileUpload / HasControlSize.
focusOutline(bool|Closure $condition = true)
Inherited from HasFieldFocusOutline via FlexFileUpload. Default: false. Adds the shared focus ring on the recorder shell when enabled.
Inherited FileUpload API
VoiceNoteRecorderField uses the standard Filament file upload pipeline (not FilePond UI). Common options:
| Method | Description |
|---|---|
disk(string|Closure|null $name) | Target filesystem disk for persisted files |
directory(string|Closure|null $directory) | Subdirectory on that disk |
visibility(string|Closure|null $visibility) | public or private |
maxSize(int|Closure|null $size) | Max file size in KB |
required() / nullable() | Validation |
deleteFileOnRemove() | Remove file from disk when user deletes recording (enabled by default) |
storeFileNamesIn(string|Closure|null $path) | Sibling state for original filenames |
preserveFilenames() / moveFiles() | See FlexFileUpload |
acceptedFileTypes(array|Closure $types) | Override MIME whitelist (defaults set in setUp()) |
imageEditor(bool|Closure $condition = true) | Not used by voice UI — leave disabled (default) |
multiple(bool|Closure $condition = true) | Single recording per field — keep multiple(false) |
maxFiles(int|Closure $max) | Typically 1 for a single voice note |
openable() / downloadable() / previewable() | FileUpload flags; voice field uses custom playback UI |
Flex fields context
There is noFieldType enum mapping for VoiceNoteRecorderField. Use the PHP class directly in Filament schemas, or register a custom flex-field handler if you need schema-driven forms.
Model & persistence
Recipes
Support ticket voice note — deferred upload
Immediate upload — feedback widget
Edit form — existing file playback
Private S3 disk — short 30s limit
Custom control icons
Public helper methods
| Method | Returns | Description |
|---|---|---|
getMaxDuration() | int | Max recording seconds |
shouldUploadImmediately() | bool | Immediate vs deferred upload |
getInitialAudioUrl() | string|null | Public URL for existing persisted file (edit forms) |
getPlayIcon() / getPauseIcon() / … | string|BackedEnum|Htmlable | Resolved control icons |
CSS classes
| Class | Role |
|---|---|
fff-voice-recorder | Root wrapper |
fff-voice-recorder--{sm|md|lg} | Size modifier |
fff-voice-recorder__record-btn | Start recording |
fff-voice-recorder__recording | Active recording UI + canvas visualizer |
fff-voice-recorder__playback-pill | Playback bar (play, waveform, time, delete) |
fff-voice-recorder__waveform | Scrubbable waveform bars |
fff-voice-recorder__container.is-submitting | Deferred upload in progress on submit |
voice-note-recorder-field (built to resources/dist/components/voice-note-recorder-field.js).
Playground
Registered under Audio field playground (AudioFieldPlayground):
| Variant key | Description |
|---|---|
voice_note__basic | Default recorder, deferred upload |
voice_note__sm / voice_note__lg | Size variants |
voice_note__with_limit | maxDuration(30) |
voice_note__immediate | uploadImmediately() |
livewire-file:…). Permanent disk storage requires a real form save (playground has no save action).
Implementation notes
- Requires microphone permission and a browser with
MediaRecorder(Chrome, Safari, Firefox). - Prefers
audio/mp4when supported (Safari), falls back toaudio/webm/audio/ogg. - Local playback uses blob URLs; duration falls back to measured recording time when WebM metadata is missing.
- Deferred upload hooks the parent
<form>submitevent (capture phase), uploads via Livewire, then callsform.requestSubmit(). - Delete calls
removeUploadedFile(temp) ordeleteUploadedFile(persisted) viacallSchemaComponentMethodwhenschemaComponentKeyis available. - Translations:
filament-flex-fields::default.audio.*(record_label,uploading_on_submit, etc.).