joaopaulolndev/filament-general-settings
Filament General Settings adds a customizable settings page to your Filament admin panel for managing site identity and contact info—logos, favicons, site name, description, social links, email, phone, and address—without editing config files. Easy to install and extend for Laravel/Filament apps.
Install the plugin and publish its assets before touching any UI:
composer require joaopaulolndev/filament-general-settings
php artisan vendor:publish --tag="filament-general-settings-migrations"
php artisan vendor:publish --tag="filament-general-settings-config"
php artisan migrate
If the settings page ships any CSS/JS or image placeholders, also publish the views/assets tags advertised in the README, and run php artisan storage:link so uploaded logos and favicons resolve from the public disk.
Register the plugin on the panel you want the settings page to live on — typically app/Providers/Filament/AdminPanelProvider.php:
->plugin(GeneralSettingsPlugin::make())
Then look at these three places first, in order:
config/filament-general-settings.php — this is the source of truth for which fields appear, navigation label/group/icon, and whether fields like mail config, social links, or custom scripts are enabled.general_settings table so you understand the row/key shape your code will read from.First real use case: pull the site name into your layout instead of hardcoding it.
{{-- resources/views/layouts/app.blade.php --}}
<title>{{ \JoaoPauloLndev\FilamentGeneralSettings\Models\GeneralSettings::first()?->site_name ?? config('app.name') }}</title>
Wrap that in your own helper or view composer rather than calling the model from Blade everywhere (see Gotchas).
Wrap reads in your own accessor. Don't scatter plugin model calls through controllers and Blade. Add a thin service or helper (Settings::siteName(), Settings::logo()) that returns null gracefully when the table row doesn't exist yet — a fresh install or a wiped DB should not 500 your frontend.
Cache the settings row. General settings are read on nearly every request (site name, logo, SEO meta, analytics snippets) but written a handful of times a year. Read once per request, or better, cache the row and forget the cache key from a model saved/deleted observer so saving the settings page busts it automatically.
Use it as the runtime config layer for mail. The common pattern is: settings page stores SMTP host/port/username/password/encryption/from-address, and a service provider pushes those into config(['mail.mailers.smtp' => [...]]) at boot. This lets a non-developer fix mail credentials without a deploy. Keep .env values as the fallback when the DB row is empty or the column is null.
Layer custom fields rather than forking. The v1.0.27 docs addition for the RichEditor field type with toolbarButtons confirms the intended extension path: configure field types in the config file instead of editing package views. If you need a footer text block or policy copy, add it as a rich editor field in config and render it in your Blade layout — you get a WYSIWYG editor in the admin for free and stay upgrade-safe.
Gate the page behind a policy/permission. General settings include mail credentials, custom <script> injection, and maintenance toggles. Restrict access with Filament Shield or a simple canAccessPanel/policy check so only super-admins reach it. Treat the settings page as an admin-only surface, not an editor one.
Maintenance mode as an app-level switch. If you enable the maintenance toggle, wire it into middleware you control (or check the flag in your own middleware) rather than assuming the package blocks frontend traffic for you. Verify the behaviour on a staging deploy before trusting it.
Multi-panel / multi-tenant. If you register the plugin on more than one panel, decide deliberately whether settings are global or per-tenant. If the package stores a single row, registering it on two panels means two entry points editing the same values — either consolidate to one panel or add your own tenant scoping.
Seeding. Write a seeder that inserts sensible defaults (site name, from-address, social URLs) so local and CI environments render the same as production instead of falling through to ?? fallbacks everywhere.
config:cache doesn't help you here. Values live in the database, not config, so any config('...') you synthesize from settings must be set at runtime in a service provider. If you cache config in your deploy pipeline, confirm your provider still runs and reads the DB (or cache) after the settings row is available.protected $casts = ['mail_password' => 'encrypted']); if it isn't, cast it yourself in a subclass or wrap your own storage. Don't log the settings model — toArray() on save/debug will leak credentials into your logs.storage:link, a FILESYSTEM_DISK mismatch between local and production, or an asset URL cached by the browser. Version your favicon URL (e.g. append the file's updated_at) — browsers cache favicons aggressively and users will report "the logo didn't change."GeneralSettings::first() returns null before the first save. Always use ?-> and a fallback. In tests, either seed the row or assert against your fallback path.config() during a web request are not present in a long-running queue:work process unless the same provider runs there (it does, on boot) and the worker is restarted after a settings change. Restart workers as part of your deploy/reload routine.esbuild bump is dev-only. GHSA-67mh-4wv8-2f99 (CVSS 5.3) affects the local dev server's CORS behaviour, not your production bundle. You get the fix by updating the package, but it's not a reason to schedule an emergency release.config:cache serving an old provider path, (4) is the frontend reading a hardcoded value you forgot to replace.How can I help you explore Laravel packages today?