Filament v4 Custom Field Plugin: Signature Pad | Mohamed Said       [Skip to content](#main)  [ ![](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png) Mohamed SaidLaravel Backend Engineer ](https://www.msaied.com/public) - [Home](https://www.msaied.com/public)
- [Projects](https://www.msaied.com/public/projects)
- [Articles](https://www.msaied.com/public/articles)
- [Certificates](https://www.msaied.com/public/certificates)
- [About](https://www.msaied.com/public#about)

           [  Contact](https://www.msaied.com/public#contact) Menu 

Menu
----

Close 

 - [HomeStart here](https://www.msaied.com/public)
- [ProjectsCase studies](https://www.msaied.com/public/projects)
- [ArticlesEngineering notes](https://www.msaied.com/public/articles)
- [CertificatesCredentials](https://www.msaied.com/public/certificates)
- [AboutHow I work](https://www.msaied.com/public#about)
- [ContactGet in touch](https://www.msaied.com/public#contact)

  [Start a conversation](https://www.msaied.com/public#contact) [WhatsApp](https://wa.me/201094619204) [Email](mailto:hello@msaied.com) 

 1. [Home](https://www.msaied.com/public)
2. /
3. [Articles](https://www.msaied.com/public/articles)
4. /
5. Filament v4 Custom Field Plugins: Building a Reusable Signature Pad Component

 Filament v4 Custom Field Plugins: Building a Reusable Signature Pad Component
==============================================================================

 Learn how to build a production-ready Filament v4 custom field plugin — a signature pad — covering asset registration, schema integration, state hydration, and Livewire interop.

 ![](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp) [Mohamed Said](https://www.msaied.com/public#person) Published 5 Jul 2026 · Updated 5 Jul 2026 · 3 min read

ShareCopy linkCopied

 ![Filament v4 Custom Field Plugins: Building a Reusable Signature Pad Component](https://cdn.msaied.com/369/4654a4b25d88862c21064130ec028ffb.png) 

  On this page +1. [Building a Reusable Signature Pad Field for Filament v4](#building-a-reusable-signature-pad-field-for-filament-v4)
2. [Project Structure](#project-structure)
3. [Registering Assets the Right Way](#registering-assets-the-right-way)
4. [The Field Class](#the-field-class)
5. [The Blade View and Alpine Wiring](#the-blade-view-and-alpine-wiring)
6. [The Alpine Component](#the-alpine-component)
7. [Consuming the Field](#consuming-the-field)
8. [Storing the Payload](#storing-the-payload)
9. [Key Takeaways](#key-takeaways)

 Building a Reusable Signature Pad Field for Filament v4
-------------------------------------------------------

Filament v4's unified Schema API makes custom field plugins more composable than ever. Rather than fighting the framework with raw Blade hacks, you can ship a self-contained package that drops cleanly into any `Form`, `Infolist`, or `Table` schema. This walkthrough builds a real signature pad field — the kind you'd use in a contracts or onboarding flow — and covers every layer from asset bundling to state hydration.

### Project Structure

```php
src/
  SignaturePad.php          # Field class
  SignaturePadServiceProvider.php
resources/
  js/signaturePad.js        # Alpine component
  views/
    signature-pad.blade.php

```

### Registering Assets the Right Way

Filament v4 uses `FilamentAsset` for scoped asset loading — assets are only injected when the component is actually rendered, not globally.

```php
// SignaturePadServiceProvider.php
use Filament\Support\Assets\AlpineComponent;
use Filament\Support\Facades\FilamentAsset;

public function boot(): void
{
    FilamentAsset::register([
        AlpineComponent::make('signature-pad', __DIR__ . '/../dist/signature-pad.js'),
    ], package: 'acme/filament-signature-pad');

    $this->loadViewsFrom(__DIR__ . '/../resources/views', 'signature-pad');
}

```

Using `AlpineComponent` instead of a raw `Js` asset tells Filament to lazy-load and register the Alpine component automatically — no manual `Alpine.data()` call in your app.

### The Field Class

```php
// SignaturePad.php
namespace Acme\FilamentSignaturePad;

use Filament\Forms\Components\Field;

class SignaturePad extends Field
{
    protected string $view = 'signature-pad::signature-pad';

    protected bool $clearable = true;
    protected string $format = 'image/png';

    public function clearable(bool $condition = true): static
    {
        $this->clearable = $condition;
        return $this;
    }

    public function format(string $mimeType): static
    {
        $this->format = $mimeType;
        return $this;
    }

    public function getClearable(): bool
    {
        return $this->clearable;
    }

    public function getFormat(): string
    {
        return $this->format;
    }
}

```

Keep the field class thin. Business logic (e.g., converting the base64 payload to a stored file) belongs in a dedicated action or cast, not here.

### The Blade View and Alpine Wiring

```blade
{{-- resources/views/signature-pad.blade.php --}}

            Clear

```

The `ax-load` / `ax-load-src` pattern is Filament v4's lazy Alpine loader. It fetches the component script only when the DOM node is present.

### The Alpine Component

```javascript
// resources/js/signaturePad.js
export default function signaturePad({ state, clearable, format }) {
    return {
        state,
        clearable,
        drawing: false,
        init() {
            const canvas = this.$refs.canvas;
            const ctx = canvas.getContext('2d');
            canvas.addEventListener('mousedown', () => { this.drawing = true; ctx.beginPath(); });
            canvas.addEventListener('mousemove', (e) => {
                if (!this.drawing) return;
                const r = canvas.getBoundingClientRect();
                ctx.lineTo(e.clientX - r.left, e.clientY - r.top);
                ctx.stroke();
            });
            canvas.addEventListener('mouseup', () => {
                this.drawing = false;
                this.state = canvas.toDataURL(format);
            });
        },
        clear() {
            const canvas = this.$refs.canvas;
            canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height);
            this.state = null;
        }
    };
}

```

State is synced back to Livewire via `$entangle` on `mouseup` — not on every `mousemove` — keeping wire calls minimal.

### Consuming the Field

```php
use Acme\FilamentSignaturePad\SignaturePad;

SignaturePad::make('signature')
    ->clearable()
    ->format('image/svg+xml')
    ->required()
    ->columnSpanFull(),

```

### Storing the Payload

The field stores a raw base64 data URL. Convert it on save using a model observer or a dedicated action:

```php
// In your CreateRecord action or observer
$data['signature'] = (new StoreSignatureImage)($data['signature']);

```

Keep the conversion outside the field — the field's only job is capturing and surfacing state.

### Key Takeaways

- Use `FilamentAsset::register` with `AlpineComponent` for scoped, lazy asset loading.
- `ax-load` / `ax-load-src` defers Alpine component hydration until the DOM node exists.
- `$entangle` + `$applyStateBindingModifiers` is the canonical way to sync custom field state with Livewire.
- Keep field classes thin; push persistence logic to actions or observers.
- Publish views via `loadViewsFrom` so consumers can override templates without forking the package.

- [filament](https://www.msaied.com/public/articles?search=filament)
- [laravel](https://www.msaied.com/public/articles?search=laravel)
- [livewire](https://www.msaied.com/public/articles?search=livewire)
- [plugins](https://www.msaied.com/public/articles?search=plugins)

 Frequently asked questions 
---------------------------

  How do I handle touch events for mobile signature capture?Add `touchstart`, `touchmove`, and `touchend` listeners in your Alpine component alongside the mouse events. Extract the coordinates with `event.touches\[0\].clientX` and call `preventDefault()` on touchmove to stop page scrolling during drawing.

   Can I validate the signature field to ensure it isn't empty?Yes. Because the field stores a string (the data URL) or null, you can chain `-&gt;required()` on the field. For a more precise check — ensuring the canvas isn't just a blank white rectangle — add a custom Filament validation rule that inspects the data URL length or pixel entropy.

   How do I make the component work inside a Filament Infolist for read-only display?Create a companion `SignaturePadEntry` class extending `Filament\\Infolists\\Components\\Entry` with its own Blade view that renders an `&lt;img&gt;` tag from the stored data URL. The field and entry are separate classes in Filament v4's unified Schema API.

   ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)About the author
----------------

[Mohamed Said](https://www.msaied.com/public#person)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

[About](https://www.msaied.com/public#about) [GitHub ↗](https://github.com/EG-Mohamed) [LinkedIn ↗](https://www.linkedin.com/in/msaiedm/) [WhatsApp ↗](https://wa.me/201094619204) [Email Address ↗](mailto:hello@msaied.com) [My CV ↗](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)  

   [Previous articleAdvanced Authorization in Laravel: Gates, Policies, and Response-Based Access Control](https://www.msaied.com/public/articles/advanced-authorization-in-laravel-gates-policies-and-response-based-access-control-2) [Next articleOctane Worker Lifecycle, State Leakage, and Safe Singleton Patterns in Laravel](https://www.msaied.com/public/articles/octane-worker-lifecycle-state-leakage-and-safe-singleton-patterns-in-laravel)  

   On this page
-------------

1. [Building a Reusable Signature Pad Field for Filament v4](#building-a-reusable-signature-pad-field-for-filament-v4)
2. [Project Structure](#project-structure)
3. [Registering Assets the Right Way](#registering-assets-the-right-way)
4. [The Field Class](#the-field-class)
5. [The Blade View and Alpine Wiring](#the-blade-view-and-alpine-wiring)
6. [The Alpine Component](#the-alpine-component)
7. [Consuming the Field](#consuming-the-field)
8. [Storing the Payload](#storing-the-payload)
9. [Key Takeaways](#key-takeaways)

 ###  Have a technical challenge?

 Tell me what you’re building. I reply within two working days.

[Start a conversation](https://www.msaied.com/public#contact) 

   Related articles
-----------------

 [ ![](https://cdn.msaied.com/743/8998fac3a41451ab3fe1588194e17a43.png) Filament · 3 min read### Securing Filament Plugins with Plumb: Automated Security Scoring for PHP Packages

5 Oct 2026 ](https://www.msaied.com/public/articles/securing-filament-plugins-with-plumb-automated-security-scoring-for-php-packages) [ ![](https://cdn.msaied.com/742/2d02018669cdeedccb5de2efb898f0ee.png) Filament · 3 min read### Filament v3.3.56 Released: File Hash Names and Livewire Upload Fix

5 Oct 2026 ](https://www.msaied.com/public/articles/filament-v3356-released-file-hash-names-and-livewire-upload-fix) [ ![](https://cdn.msaied.com/741/5b55c123ad08e4d34e1f4b99ad6a428b.png)  · 3 min read### Filament v4 Schema-Based Forms, Infolists, and the Unified Schema API

5 Oct 2026 ](https://www.msaied.com/public/articles/filament-v4-schema-based-forms-infolists-and-the-unified-schema-api-5) 

  Have a technical challenge?
----------------------------

Tell me what you’re building. I reply within two working days.

 [Discuss your project ↗](https://www.msaied.com/public#contact) 

  © 2026 Mohamed Said · Built with Laravel, meant to last.Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

 - [Home](https://www.msaied.com/public)
- [Articles](https://www.msaied.com/public/articles)
- [Certificates](https://www.msaied.com/public/certificates)
- [GitHub](https://github.com/EG-Mohamed)
- [LinkedIn](https://www.linkedin.com/in/msaiedm/)
- [WhatsApp](https://wa.me/201094619204)
- [Email Address](mailto:hello@msaied.com)
- [My CV](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
- [Sitemap](https://www.msaied.com/public/sitemap.xml)
