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

Filament Settings Laravel Package

outerweb/filament-settings

Outerweb Filament Settings adds a settings page to your Filament admin panel where you define typed settings classes, edit values with familiar form fields, and save them to the database. Includes caching and validation.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require outerweb/filament-settings
php artisan vendor:publish --tag=settings-migrations   # check README for exact tag
php artisan migrate

The package is a thin Filament layer over outerweb/settings. Two things matter:

  1. A settings class (lives in app/Settings/) that extends the package's base Settings class and declares its fields using ordinary Filament form components. These components are the schema — the same TextInput/Toggle/FileUpload you already use in resources.
  2. A Filament page extending the package's SettingsPage, which renders those classes in tabs/sections and wires up the save action.

Minimal first use case — a "General" settings tab for site name, logo, and a maintenance toggle:

// app/Settings/General.php
namespace App\Settings;

use Filament\Forms\Components\{TextInput, Toggle, FileUpload};
use Outerweb\Settings\Settings;

class General extends Settings
{
    public static function fields(): array
    {
        return [
            TextInput::make('site_name')->required(),
            FileUpload::make('logo')->image()->directory('branding'),
            Toggle::make('maintenance_mode'),
        ];
    }
}
// app/Filament/Pages/ManageSettings.php
namespace App\Filament\Pages;

use App\Settings\General;
use Outerweb\FilamentSettings\Pages\SettingsPage;

class ManageSettings extends SettingsPage
{
    protected static ?string $navigationIcon = 'heroicon-o-cog-6-tooth';

    public function getSettings(): array
    {
        return [General::class];
    }
}

Look first at: the published migration (to confirm the settings table shape and whether it's polymorphic), the base Settings class in vendor/outerweb/settings/src, and the package README's example page. Those three tell you everything the docs gloss over. Then read a value back in a Blade view or service with General::get('site_name') — that round trip is the whole point of the package.

Implementation Patterns

One class per logical group. Don't build a 40-field monolith. Split into General, Mail, Social, Seo, and let the page render them as tabs. Each class is independently readable and testable, and the tab labels come from the class (usually a $title/getTitle() or the navigation label — check the base class).

Reading settings. The common pattern is static access from anywhere:

General::get('site_name', config('app.name'));   // with fallback
General::set('maintenance_mode', true);          // programmatic write

Use it in Blade ({{ General::get('site_name') }}), in a view composer, or in a View::share() call in a service provider — not directly inside config/*.php or a migration, both of which run before/without a database.

Model-scoped settings. The underlying settings package is polymorphic, so a model can hold its own settings via the HasSettings trait. This is the right escape hatch for per-user or per-team preferences; panel-wide settings should stay global. With Filament tenancy, decide explicitly: is the settings page global, or should each tenant get its own row?

Conditional field visibility. Because fields are Filament components, ->visible(fn (Get $get) => $get('enabled')) and ->live() work exactly as in resources. This is the main reason to prefer this package over hand-rolling a settings form: you get Filament's whole form API for free.

Custom save hooks. When a setting has side effects (logo replacement, cache warm-up, queue restart), override the page's save method or listen for the model's saved event rather than stuffing logic into the field definition. Keep the fields declarative.

Authorization. Override canView()/canAccess() on the page so only admins see ManageSettings. These settings are app-wide; leaking the page to all users is a common mistake.

Gotchas and Tips

  • Casting is on you. Stored values often come back as strings or JSON. Booleans read as "1"/"0", arrays need explicit encoding. Define casts on the settings class (or ->boolean()-style handling in the component) and verify with tinker — General::get('maintenance_mode') returning "0" and being truthy is the classic bug.
  • FileUploads store paths, not URLs. Save the disk path, resolve with Storage::disk('public')->url(...) at render time. Replacing an upload doesn't delete the old file unless you handle it.
  • The table may not exist yet. Any ::get() call in a service provider, middleware, or Blade layout will explode on a fresh clone before migrate. Either default aggressively or guard with Schema::hasTable('settings'). Tests need RefreshDatabase plus the published migration.
  • Caching. Settings are typically cached per class/request. If you write via ::set() outside the Filament page (a console command, an importer), clear the cache or the panel keeps serving stale values until the next save. Check whether the base class exposes a flushCache()/clearCache().
  • Defaults belong in code, not the DB. Give every field a ->default(...) so a missing row renders sensibly instead of an empty required input that blocks the first save.
  • Dot notation collisions. Keys like mail.host in one class and a nested structure in another can confuse lookups. Flat snake_case names per class are safest.
  • Filament version lock-in. This is a 36-star, zero-dependent package wrapping another small package. Pin the major version, confirm it supports your Filament major (v3 vs v4 branches diverge), and be prepared to patch a vendor class if needed. Read the source before upgrading Filament.
  • Debugging. Query the settings table directly to see raw stored values vs. what the form renders — most "it didn't save" reports are a cast mismatch or a validation rule on a hidden field. Log::info($getState()) inside the save is faster than guessing.
  • Extension point. If you outgrow it, the seam is the Settings base class: replace its get/set with your own storage or swap to spatie/laravel-settings for typed, cast-aware settings classes while keeping the Filament page.
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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource