ibrahim-bedir/filament-dynamic-settings-page
A Filament plugin that generates dynamic settings pages from a simple configuration array, letting you build and manage app settings with form fields, tabs, and sections without writing custom pages or boilerplate code.
composer require ibrahim-bedir/filament-dynamic-settings-page
php artisan vendor:publish --tag="filament-dynamic-settings-page-migrations"
php artisan migrate
Optionally publish the config (--tag="filament-dynamic-settings-page-config") to change the settings table name, cache key prefix, or storage driver.
Register the package's page (or plugin) in your Filament panel provider:
->plugin(\IbrahimBedir\FilamentDynamicSettingsPage\FilamentDynamicSettingsPagePlugin::make())
or, if you are extending the base page manually, add your page class to ->pages([...]).
Create a settings page by extending the package's SettingsPage base class and defining the form schema with Filament components (TextInput, Toggle, Select, FileUpload, etc.). Each field's name becomes the persistent settings key. Load the panel and navigate to the registered page — you should see a single "Save" action that persists the form state to the settings table.
First use case: global, non-tenant application settings — site name, logo, support email, feature toggles, SEO defaults. This is the package's sweet spot; anything more relational (per-user preferences, per-team config) needs extra scoping work (see below).
Tabs or Section components to break settings into "General", "Mail", "Branding", "Integrations". The package stores keys flat, so grouping is purely presentational — keep naming prefixes (mail.from_address, branding.logo) consistent so lookups stay predictable.app(SettingsService::class)->get('mail.from_address', $default)) so tests can swap the implementation and you can add casting/caching in one place.afterSave hook (or an observer on the model). A Cache::rememberForever + explicit forget on write is usually enough.CheckboxList / Repeater), dates, and file paths all come back as JSON-decoded scalars or strings, not necessarily the type you expect. Add a cast map in your accessor service rather than relying on the storage layer.canAccess() / canViewAny() on your page so only admins see it in the navigation. It is easy to forget because the page auto-registers.tenant.42.branding.logo) and gate reads through a resolver that prepends the current tenant. Do not try to add a tenant_id column to the published migration without also patching every read path.composer.json constraints before installing in a v3 or v4 panel; if it only declares ^2.0 or ^3.0, you may need --with-all-dependencies and manual verification. Do not assume forward compatibility.Form or Page APIs. The migration and one page class are small enough to inline into your app if you want to drop the dependency later.settings table; if you already use spatie/laravel-settings, filament-spatie-settings, or a hand-rolled settings table, rename via the config before migrating. Two packages writing to settings with different column shapes is a painful debug.php artisan cache:clear." Add the forget call in the same request as the save.beforeSave or model observer, and configure a dedicated disk/folder so cleanup is bounded.->rules(). If a downstream service trusts the value shape (e.g. an enum or a URL), re-validate at the consumption site or add a custom cast that throws.config:cache interaction. If the package reads config at boot for cache prefix or table name, run php artisan config:clear after editing published config or your changes will appear to have no effect in local dev.getFormSchema(), getSettingsKey() (if present), and the save/reset action methods rather than editing vendor files. That keeps forks to a single page class when the package falls behind Filament.How can I help you explore Laravel packages today?