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

Nova Signature Laravel Package

lab404/nova-signature

Nova Signature adds a drawable signature pad field to Laravel Nova resources. Users sign with a mouse, finger, or stylus and the result is saved as an image on your model, with configurable pad options for a smooth signing experience. Simple to install and customize.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the field with Composer alongside Nova 2/3 (the package has not been updated since January 2020):

composer require lab404/nova-signature

The field is auto-discovered via its service provider; there is nothing to publish for a basic setup.

First, add a column to the model that will store the signature. The field persists a base64 data-URI string (typically data:image/png;base64,...), so use a text column with headroom:

Schema::table('contracts', function (Blueprint $table) {
    $table->longText('signature')->nullable();
});

Then attach the field in the Nova resource. The most important first step is hiding it from the index view, because the raw base64 string will destroy your table layout otherwise:

use Lab404\NovaSignature\NovaSignature;

public function fields(Request $request)
{
    return [
        ID::make()->sortable(),
        Text::make('Title'),
        NovaSignature::make('Signature')
            ->hideFromIndex()
            ->rules('required', 'max:200000'),
    ];
}

Cast it to string (or leave it uncast) on the model. A realistic first use case: a contract or waiver resource where a user draws their signature on create/update, and an admin views the rendered image on the detail page.

Implementation Patterns

Basic form capture. Drop the field on the resource, mark it ->hideFromIndex(), and let signature_pad render the canvas. It submits a single string, so no file upload or storage disk is involved.

Detail-view rendering. The stored value is a data URI, so display it as an image rather than text. You have two clean options:

// Option A: inline img through a display callback
NovaSignature::make('Signature')
    ->hideFromIndex()
    ->displayUsing(function ($value) {
        return $value ? "<img src=\"{$value}\" style=\"max-width:300px\">" : '—';
    })->asHtml(),

// Option B: write the decoded file to storage on save and show the URL

Option B is the pattern most production teams end up with: decode the data URI in an observer or saving model event, store the PNG on a disk, and keep the base64 only long enough to convert it. This keeps rows small and lets you generate PDFs or signed documents later.

Conversion helper for downstream use. When you need the signature outside Nova (PDF generation, e-mail attachments, API responses), normalize it:

protected function signaturePng(?string $value): ?string
{
    if (! $value || ! str_starts_with($value, 'data:image/png;base64,')) {
        return null;
    }
    return base64_decode(substr($value, strlen('data:image/png;base64,')));
}

Multiple signatures on one resource. Create separate columns (customer_signature, witness_signature) and separate field instances. Avoid array/JSON storage here — the field submits a flat string and you would be fighting the package.

Validation pattern. There is no built-in server-side validation, so pair a length rule with a small custom rule or closure that checks the prefix and that base64_decode succeeds. This prevents garbage from non-browser clients hitting your table.

Conditional fields. Combine with Nova's ->dependsOn() / ->onlyOnForms() as usual. Signing on create is common; editing a signature later should usually require an explicit "re-sign" toggle, so consider ->readonly() once a boolean signed_at is set.

Form submission guard. If you add a "clear" workflow, make sure the field can be explicitly emptied (nullable column plus a sometimes rule), otherwise users get stuck re-signing to overwrite.

Gotchas and Tips

  • Column size is the first failure mode. MySQL TEXT is capped at 65,535 bytes, and a large canvas at high pixel density can approach or exceed that once base64-encoded (~33% overhead). Use LONGTEXT or MEDIUMTEXT and cap the canvas size client-side.
  • Never show it on the index. The raw data URI will bloat the response and break tables. hideFromIndex() should be considered mandatory, and hideFromDetail() too if you use a decoded file URL instead.
  • Nova 4+ is very likely broken. The last release predates Nova 4's Vue 3 rewrite; the field's compiled component almost certainly won't register or render. Verify against your Nova major version before committing, and budget for a fork if you're on Nova 4/5.
  • The package is effectively unmaintained (14 stars, single 2020 release, no dependents). Treat it as a starting point: vendor it into nova-components/ or copy the field class and Vue component into your app so you can patch it. For many teams, wrapping signature_pad in a homegrown Nova field is less risk than depending on this repo.
  • Empty values are inconsistent. Depending on the browser and how the user interacts, you can receive null, an empty string, or a bare data:, prefix. Guard every render and decode with a strict str_starts_with('data:image/png;base64,') check.
  • Canvas sizing inside Nova forms is finicky. The field renders in Nova's grid; on narrow screens or inside modals the drawing area may be squashed or the aspect ratio may skew the saved image. Test the create page at mobile widths and add CSS overrides in your tool.scss/resource styles if needed.
  • Watch touch behavior. signature_pad needs touch-action: none on the canvas for stylus/touch devices; if the page scrolls while drawing on a tablet, that CSS is missing or being overridden by Nova's styles.
  • Do not trust the client. A malicious or scripted submission can post an arbitrary string into a longText column. Enforce a max length rule, validate the data-URI prefix server-side, and consider re-encoding the PNG server-side before persisting.
  • Debugging tips: inspect the network payload in the Nova form submission to see the exact string shape; if the pad renders but the value is empty, check that the component's value/handleChange wiring isn't being clobbered by your field's ->default() or ->fillUsing().
  • Extension points are thin. Expect to customize via displayUsing(), fillUsing(), model observers, and direct Vue component edits rather than through documented hooks; there is no configuration file to rely on.
  • Testing. In feature tests, post a small fixture data URI (a 1x1 PNG is fine) to the Nova endpoint and assert the column round-trips; this catches the prefix/column-size issues cheaply without exercising the canvas.
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.
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment