Packstub Form Builder
CommunityBuild forms in your Filament panel, render them with Blade, Livewire or JSON, and collect, export and forward submissions.
Author:
Packstub
Package health
BetaAutomated checks of this plugin's Composer package
15 checks
- Passed: GitHub Actions pinned to SHA
- Skipped: GitLab CI includes pinned to SHA
- Passed: Open security advisories
- Passed: Dependabot PR responsiveness — No open Dependabot PRs.
- Skipped: Renovate MR responsiveness
- Passed: Dependabot or Renovate configured
- Passed: Dependency update cooldown configured
- Passed: Provides a security policy
- Passed: Abandoned or archived — No consulted source marks the package abandoned (packagist, github).
- Passed: Commit and release recency — Active: last commit 1 days ago; last release 2 days ago.
-
Passed:
composer.lock not committed by library
—
composer.lockis absent from the released dist archive. - Passed: Dist archive is lean
-
Passed:
Current Laravel version supported
—
Package dependencies resolve together with current Laravel
13.0. -
Passed:
Current PHP version supported
—
Constraint
^8.3supports current PHP8.5. - Skipped: Current Symfony version supported
filament/
namespace. Review the source and install at your own risk. Found
malware or an unresolved security issue the author won't
address?
Report it
.
Documentation
- Features
- Compatibility
- Installation
- Building a form
- Rendering a form
- Submissions
- Notifications and sinks
- Spam protection
- Settings
- Extending
- Theming
- Configuration
- Documentation
- Testing
- Changelog
- Security
- Credits
- License
Build forms in your Filament panel, put them on your site with one Blade tag, a Livewire component or a JSON call, and read the submissions where you already work.
#Features
- Builder in the panel — a Forms resource with a block per field type: text, email, phone, URL, number, long text, dropdown, radio buttons, checkbox, checkbox list, date, hidden, heading and paragraph. Every field has a label, key, placeholder, help text, default, required flag, width and any extra Laravel validation rule you type in.
- Three ways to render — a plain Blade component that works on cached and session-less pages, a Livewire component with Filament fields and in-place validation, and a JSON API for SPAs and mobile apps. All three go through the same validation and storage.
- Submissions — stored with the values, the page they came from and the labels at the time, listed per form with a details slide-over, read / unread state, filters, bulk actions and a CSV export. Unread counts on the navigation item.
- Notifications and sinks — an email per submission to any list of addresses, a
SubmissionReceivedevent, and aSubmissionSinkcontract for the CRM, webhook or mailing list you want to feed. - Spam protection without a captcha — a honeypot, a time trap (an encrypted token issued with the form) and a per-IP rate limit. Bots see the success message; nothing is stored.
- Availability — a success message or a redirect, an active switch, an opening and closing date, an optional login requirement, and a "do not store" mode for forms that should only be emailed.
- Extensible — write a field type class with its own settings, rules and views; swap the models and table names; submit from code with
FormBuilder::submit(). - Themeable and translatable — the Blade renderer uses CSS variables with sensible defaults and ships its own small stylesheet; every string lives in a language file.
#Compatibility
| Plugin | Filament | Laravel | PHP |
|---|---|---|---|
| 1.x | 4.x, 5.x | 12.x, 13.x | 8.3+ |
#Installation
composer require packstub/filament-form-builder
php artisan packstub-form-builder:install
Register the plugin on your panel:
use Packstub\FormBuilder\FormBuilderPlugin;
$panel->plugin(FormBuilderPlugin::make());
A Forms resource appears in the navigation.
#Building a form
Create a form, give it a name, and add fields from the block picker. Each block carries the settings of its type: choices for a dropdown or a checkbox list, a range for numbers and dates, rows for long text, the level of a heading. Keys are derived from labels and kept unique, so values in submissions and exports keep a stable name.
The Settings tab holds the submit button label, the success message or a redirect URL, notification addresses, whether submissions are stored, an opening and closing date, a login requirement and the spam settings. The Embed tab shows the snippets for the form you are editing.
#Rendering a form
#Blade
<x-form-builder::form form="contact" />
A plain HTML form posting to /forms/contact. Without JavaScript the browser posts and comes back to the page with the success message or the errors and the old input. With the small script the component inlines (frontend.enhance), the form submits with fetch and shows the result in place, without reloading.
The component works on pages without a session: errors and old input travel in an encrypted query parameter instead of the session. The submit route runs the web middleware by default; on a session-less site set routes.middleware to [] and rely on the honeypot, the time trap and the rate limit.
Options: :enhance="false" for a plain POST only, :styles="false" when your site ships its own CSS, action and return to override the endpoint and the page to come back to.
#Livewire
<livewire:form-builder form="contact" />
The fields as Filament components, validated in place. The page needs Filament's frontend assets:
@filamentStyles
@filamentScripts
#JSON
GET /forms/contact/definition
POST /forms/contact Accept: application/json
The definition lists the fields with their type, rules and choices, plus a fresh protection token and the names of the anti-spam fields to send back. The POST answers { "ok": true, "message": "...", "redirect": null, "id": 12 } or 422 with errors keyed by field.
#Hosted page
Every form is also served on its own at /forms/{slug} (switch off with routes.page, change the layout with routes.page_layout).
#Submissions
Submissions live under the form as a relation manager: the date, a summary of the first values, the page they came from and the channel (web, json, livewire, code). Opening one shows every value and the details, and marks it read. Bulk actions mark as read, export or delete. The CSV export lists the form's current fields, then any key an older submission still carries.
use Packstub\FormBuilder\Models\FormSubmission;
FormSubmission::query()->unread()->count();
$submission->value('email');
$submission->formatted(); // ['email' => ['label' => 'Email', 'value' => 'ada@example.com'], ...]
#Notifications and sinks
Add addresses to Notify by email and each submission is emailed (queued when a queue is configured). For anything else, write a sink:
use Packstub\FormBuilder\Contracts\SubmissionSink;
use Packstub\FormBuilder\Models\FormSubmission;
class PushToCrm implements SubmissionSink
{
public function handle(FormSubmission $submission): void
{
Crm::createLead($submission->form->slug, $submission->data);
}
}
Register it in config/packstub-form-builder.php under sinks, or at runtime with FormBuilder::sink(PushToCrm::class). The SubmissionReceived and SpamDetected events are dispatched as well.
#Spam protection
- Honeypot — a hidden input a person never sees; a filled one drops the submission.
- Time trap — the form carries an encrypted token with the time it was rendered; a submission posted faster than
spam.min_seconds(default 2) is dropped. Headless clients get the token from the definition endpoint. - Rate limit —
submissions.throttle(default10,1) per IP on the submit endpoint.
Both checks can be tuned per form in its Settings tab. Dropped submissions look like a success to the sender and fire SpamDetected.
#Settings
| Setting | What it does |
|---|---|
| Active | Off: the form renders as closed and rejects submissions |
| Submit button, success message | Shown by every renderer |
| Redirect after submit | Sends the visitor to a URL instead of showing the message |
| Notify by email | One email per submission to each address |
| Store submissions | Off: the submission is only emailed and passed to sinks |
| Opens at, closes at | An availability window |
| Require a logged-in user | Rejects anonymous submissions |
| Honeypot, minimum seconds | Per-form spam settings |
#Extending
A field type is a class: its id, an icon, the settings it shows in the builder, its validation rules, how it normalises a value, a Blade view and a Filament component.
use Filament\Forms\Components\TextInput;
use Packstub\FormBuilder\Fields\Field;
use Packstub\FormBuilder\Fields\Types\NumberField;
class RatingField extends NumberField
{
public static function id(): string
{
return 'rating';
}
public function rules(Field $field): array
{
return ['integer', 'between:1,5'];
}
}
Register it in the config under field_types, on the plugin with FormBuilderPlugin::make()->fieldTypes([RatingField::class]), or with FormBuilder::registerFieldTypes([...]). Hide built-ins with ->withoutFieldTypes([...]). Name the label in your language file under packstub-form-builder::form-builder.types.rating.
Submit from code:
use Packstub\FormBuilder\Facades\FormBuilder;
FormBuilder::submit('contact', ['name' => 'Ada', 'email' => 'ada@example.com', 'message' => 'Hi']);
#Theming
The Blade renderer scopes everything under .fb-form and reads these variables, each with a fallback:
:root {
--fb-color-text: #0f172a;
--fb-color-muted: #475569;
--fb-color-border: #cbd5e1;
--fb-color-surface: #fff;
--fb-color-primary: #2563eb;
--fb-color-primary-contrast: #fff;
--fb-color-danger: #dc2626;
--fb-color-success: #15803d;
--fb-radius: .5rem;
--fb-font: inherit;
}
Publish the stylesheet and script with php artisan vendor:publish --tag=packstub-form-builder-assets and set frontend.styles / frontend.enhance to false to ship them your own way. Views publish with --tag=packstub-form-builder-views, the language file with --tag=packstub-form-builder-translations.
#Configuration
php artisan vendor:publish --tag=packstub-form-builder-config
| Key | Default | What it does |
|---|---|---|
tables.*, models.* |
form_builder_* |
Table names and model classes |
field_types |
the built-ins | Field types offered in the builder |
routes.prefix |
forms |
URL prefix of the submit, definition and hosted page routes |
routes.middleware |
['web'] |
Middleware of the submit and definition routes |
routes.page, page_middleware, page_layout |
on, ['web'], package layout |
The hosted page |
submissions.throttle |
10,1 |
Rate limit per IP; null to disable |
submissions.store_ip, store_user_agent |
true |
What the submission row records |
submissions.queue_notifications |
true |
Queue the notification emails |
spam.honeypot, honeypot_field, min_seconds, token_field |
on, _fb_website, 2, _fb_token |
Spam defaults (per form in the panel) |
sinks |
[] |
SubmissionSink classes |
frontend.styles, frontend.enhance |
true |
Inline the stylesheet and the fetch script |
navigation.*, gate |
— | Navigation group, icon, sort, unread badge; an ability to check |
Plugin methods: fieldTypes(), withoutFieldTypes(), resource(), withoutResource(), navigationGroup(), navigationIcon(), navigationSort(), navigationBadge(), authorize().
#Documentation
Installation · Building forms · Rendering · Submissions · Spam protection · Extending · Configuration
#Testing
composer test
#Changelog
See CHANGELOG.
#Security
Report vulnerabilities to support@packstub.dev rather than the issue tracker.
#Credits
#License
The MIT License (MIT). See LICENSE.
The author
Packstub builds plugins for Filament and Laravel, from open-source utilities to paid infrastructure for multi-tenant SaaS. It is the author of Packstub Tenancy, which brings stancl/tenancy's database-per-tenant model to Filament panels, and Filament Account Switcher, which adds linked accounts, impersonation, and developer logins with an audit trail. Packstub is run by XLITE, a web development studio based in Moldova.
From the same author
Packstub Flow
Visual workflows in your panel: record, schedule and webhook triggers, conditions, email, Slack and HTTP actions, approvals.
Author:
Packstub
Account Switcher
Switch between accounts without signing out: linked sub-accounts with password-confirmed escalation, impersonation with a switch-back banner, and one-click developer logins.
Author:
Packstub
Packstub Tenancy
Multi-database tenancy powered by stancl/tenancy v4 — one isolated database per tenant or shared databases with tenant_id scoping, async provisioning, and a load-balanced database pool that scales tenants across multiple database servers.
Author:
Packstub
Packstub Agents
An AI assistant in your panel and an MCP server for Claude Code from one tool list, with the panel's own authorization.
Author:
Packstub
Featured Plugins
A selection of plugins curated by the Filament team
Custom Dashboards
Let your users build and share their own dashboards with a drag-and-drop interface. Define your data sources in PHP and let them do the rest.
Filament
Noir Theme
A theme that gives panels a focused, refined look with near-black surfaces, crisp actions, and restrained color.
Filament
Advanced Tables (formerly Filter Sets)
Supercharge your tables with powerful features like user-customizable views, quick filters, multi-column sorting, advanced table searching, convenient view management, and more. Compatible with Resource Panel Tables, Relation Managers, Table Widgets, and Table Builder!
Kenneth Sese
