edalzell/forma
Forma provides a simple, expressive way to build and manage forms in Laravel applications. Define fields, validation, and layout in code, then render clean markup—keeping form logic organized, reusable, and easy to maintain.
Forma is a small Statamic addon (10 stars, last release 2025-08-29) that lets an existing Statamic addon ship a Control Panel settings screen without hand-rolling routes, views, and a save controller. Instead of writing routes/cp.php, a Blade form, and a validation layer for every addon, you describe the settings as Statamic fieldtypes and let Forma render the page.
Minimal setup for an addon you already maintain:
composer require edalzell/formaServiceProvider::boot(), register Forma with a namespace/key and a title:use Edalzell\Forma\Forma;
public function boot(): void
{
Forma::make('my-addon')
->title('My Addon')
->nav('My Addon', 'Addons'); // label + existing CP nav section
}
namespace Vendor\MyAddon;
class Settings
{
public function fields(): array
{
return [
'api_key' => [
'type' => 'text',
'display' => 'API Key',
'instructions' => 'Used for outbound requests.',
'validate' => 'required',
],
'environment' => [
'type' => 'select',
'options' => ['sandbox' => 'Sandbox', 'live' => 'Live'],
'default' => 'sandbox',
],
];
}
}
Forma::make('my-addon')
->settings(Settings::class)
->title('My Addon');
Read the settings back anywhere in your addon with Forma's settings accessor (typically Forma::settings('my-addon') or the same static handle you registered with), which returns an array keyed by field handle.
Where to look first: vendor/edalzell/forma/src/ — at 10 stars the source is the documentation. Start with the class that exposes make(), then trace how it persists values (config file, YAML, or database) so you know what to back up and where to diff during debugging. The README on GitHub is short; the tests directory is a better reference for edge cases and expected field arrays.
First real use case: replacing a scattered config/my-addon.php that nobody remembers to publish with a proper CP page for API keys and mode switches. That is the sweet spot — small number of scalar fields, admin-only, no complex relationships.
One settings class per addon, registered once. Keep Forma registration in your addon's ServiceProvider::boot() behind the usual runningInConsole() guard for asset publishing. Treat the Settings class as the source of truth for shape; treat the persisted file as an opaque value store.
Model fields after Statamic fieldtypes, not raw HTML. Anything Statamic exposes as a fieldtype (text, textarea, select, toggle, color, assets, entries, code, markdown, date) can typically be used. This is Forma's main win over hand-written Blade: you inherit Statamic's Vue components, validation display, and dark mode for free.
Group with sections/panels. If the package exposes a section or tab concept, use it — an "API" section and a "Behaviour" section read far better than a flat list. If not, prefix handles (api_key, api_secret, webhook_url) so they read as a group in the UI and in stored config.
Read settings through one accessor. Wrap Forma's accessor in a small helper on your addon so the rest of your code never depends on Forma directly:
namespace Vendor\MyAddon;
class Config
{
public static function get(string $key, $default = null)
{
return data_get(\Edalzell\Forma\Forma::settings('my-addon'), $key, $default);
}
}
If Forma's API shifts (likely at this star count), you only patch one file.
Cache-aware reads. If Forma writes to a config file, wrap the accessor in config() after a config()->set() call in the service provider, or explicitly read from the file each request. Do not assume the value is live in the container across deploys.
Multiple addons, one CP section. Forma is designed for the "each addon gets a small settings page" pattern. If you maintain several addons, register them all under Addons so the CP nav stays tidy — this is exactly the problem the package exists to solve.
Testing. In feature tests, seed settings by writing the underlying persisted file (or via whatever Forma's test helpers expose) rather than clicking through the CP. Statamic's own Statamic::fake()-style helpers cover most of the auth/asset surface you'll need.
Very low adoption — pin it. At 10 stars, 0 dependents, and a 0.3 score, Forma is effectively a personal utility that happens to be public. Pin an exact version in composer.json ("edalzell/forma": "1.0.3"), read the source before upgrading, and be ready to fork. Do not build a product roadmap on its API stability.
Fieldtype compatibility is not automatic. Forma renders Statamic fieldtypes, which means every fieldtype's own quirks and Vue dependencies come along for the ride. If a third-party fieldtype renders but doesn't save (common with picker-style fields), test with a scalar text field first to isolate whether the bug is Forma or the fieldtype.
Permissions. CP routes generated by addon settings pages should be gated behind Statamic's super-admin / role permissions. Verify that a non-super-admin can't hit the save endpoint directly — small packages sometimes rely on nav visibility alone as authorisation, which is not enough.
Publish and cache. If Forma ships a published config or view, run php artisan vendor:publish --tag=forma-config (or whatever tags the provider defines) and add php artisan config:cache / view:clear to your deploy script. Forgetting this is the single most common "my settings disappeared" cause.
Validation lives on the field definition. Put 'validate' => 'required|url' (or Laravel rule syntax) on the field array. If you need cross-field validation (e.g. api_secret required when environment is live), use a required_if rule rather than a custom save hook — custom hooks are the least stable extension point.
Debugging saves. When a save silently no-ops, log the $request->all() inside the package's save controller (temporarily) and confirm the field handles match your fields() keys exactly. Mismatched handles are the usual culprit, and the CP form will still render happily.
Extension points to prefer. If the package supports it, override the Blade view via a published template rather than subclassing the controller. Views are far more stable across versions than PHP classes, and swapping a view is reversible if you later drop Forma.
When not to use it. Anything beyond scalar/simple settings — relationship builders, importers, multi-step wizards — is outside the sweet spot. For those, use Statamic's own CP resources or statamic/cms utility pages directly. Forma saves you maybe 200 lines on a simple settings page and costs you a dependency; that trade is only worth it for the "many small addons, one consistent settings UI" case.
How can I help you explore Laravel packages today?