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 Modules Laravel Package

savannabits/filament-modules

Organize your Filament admin panel into autonomous Laravel modules that work as plugins, complete with their own resources, pages, widgets, and clusters. A wrapper around nwidart/laravel-modules for easy sharing and reuse.

View on GitHub
Deep Wiki
Context7

Getting Started

Version-matrix check first. This is the single biggest install-time failure source. v5.x pairs Laravel 11/12/13 with Filament 4.x/5.x, PHP 8.3+, and nwidart/laravel-modules 11/12/13. If you're on Filament 3, you need the 4.x branch of this package, not v5.

composer require coolsam/modules

The package pulls a compatible nwidart/laravel-modules automatically, but it does not configure it for you. Before touching anything Filament-related:

  1. Configure nwidart/laravel-modules per its own docs (module paths, namespace, stub overrides).
  2. Add the merge-plugin block to composer.json — without this, module autoloading silently fails with Class not found errors later:
"extra": {
    "laravel": { "dont-discover": [] },
    "merge-plugin": { "include": ["Modules/*/composer.json"] }
}
  1. Publish config and scaffold:
php artisan modules:install        # interactive; publishes config + sets up
# or: php artisan vendor:publish --tag="modules-config"
  1. Wire the plugin into your panel — this is the one line that makes everything appear:
// app/Providers/Filament/AdminPanelProvider.php
use Coolsam\Modules\ModulesPlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->plugin(ModulesPlugin::make());
}

First use case worth building: a small self-contained module (e.g. Blog) with one Resource and one Cluster. That exercises the whole loop — module creation, Filament scaffolding, auto-discovery, cluster navigation — in about ten minutes, and reveals any autoload or config problems before you invest in a real feature module.

php artisan module:make Blog
php artisan module:filament:install Blog     # interactive: clusters? default cluster?
php artisan module:filament:resource         # prompt for module + panel/cluster target

Where to look first when something is off, in order: config/filament-modules.php → the generated Modules/Blog/app/Providers/Filament/BlogServiceProvider.php → the generated Modules/Blog/app/Filament/BlogPlugin.php → your AdminPanelProvider.

Implementation Patterns

Pick a discovery mode deliberately

filament-modules.mode (a ConfigMode enum) drives everything:

Mode Behaviour Use when
plugins Each module's *Plugin registers into your existing panel(s) Most apps. One admin panel, modules contribute resources to it.
panels Each module gets its own Panel, linked from the main panel nav Modules are large enough to deserve their own nav context, or you're building a modular monolith with per-domain admins.
both Default — plugins and panels Only if you genuinely want both; it's the most confusing to reason about during debugging.

For a typical SaaS admin, start on plugins. Switching to panels later means regenerating module panel providers, so decide early.

Auto-registration and the ModulesPlugin contract

With auto-register-plugins => true, registering ModulesPlugin::make() on the panel is sufficient — every module plugin and (in panel mode) every module panel link is picked up. Set it to false when you want explicit control per panel:

$panel->plugins([
    ModulesPlugin::make(),
    \Modules\Blog\Filament\BlogPlugin::make(),
    \Modules\Billing\Filament\BillingPlugin::make(),
]);

This is the right choice when modules are feature-flagged per tenant or per environment — you can conditionally include them.

Adopt clusters from day one, or never

clusters.enabled changes where generated code lands (Modules/X/app/Filament/Clusters/X/Resources vs Modules/X/app/Filament/Resources). Because the namespace follows the path, retrofitting clusters onto an existing module means moving files and rewriting namespace declarations in every generated class. Decide at module:filament:install time and stay consistent.

clusters.use-top-navigation => true is worth enabling in cluster mode: cluster links become top-level tabs with the cluster's own resources as side nav. It reads far better than a flat sidebar of forty items.

The day-to-day generation loop

# Resources
php artisan module:make:filament-resource        # aliases: module:filament:resource, module:filament:make-resource
# Pages
php artisan module:make:filament-page
# Widgets
php artisan module:make:filament-widget
# Clusters
php artisan module:make:filament-cluster
# Plugins
php artisan module:make:filament-plugin
# Panels
php artisan module:make:filament-panel

The triple-alias situation is real: muscle memory from Filament's native make:filament-resource will fail here because the module variant prompts for a module first. Standardise on module:make:filament-* in your team docs so nobody half-remembers module:filament:make-resource.

The important structural difference from vanilla Filament: you are not passing a target directory. The command resolves module → panel → cluster from interactive prompts. That means command output is only as reliable as your config, so verify the generated namespace line matches the file path after each generation during early adoption.

Per-panel scoping

If a module hosts multiple panels, each gets its own *PanelServiceProvider with its own discoverResources() / discoverPages() / discoverWidgets() paths. To keep a resource in one panel, generate it into that panel's subdirectory and let only that provider discover the path:

Modules/Blog/app/Filament/AdminPanel/Resources/PostResource.php
Modules/Blog/app/Filament/PublicPanel/Resources/ArticleResource.php

This package wires modules to panels; it deliberately does not override Filament's per-panel discovery model. Don't expect a magic attribute to move a resource between panels.

Access control wrappers

Instead of Filament\Resources\Resource, extend the module base classes so module resources participate in the package's access checks:

use Coolsam\Modules\Resource;       // instead of Filament\Resources\Resource
use Coolsam\Modules\Page;           // instead of Filament\Pages\Page
use Coolsam\Modules\TableWidget;    // instead of Filament\Widgets\TableWidget
use Coolsam\Modules\ChartWidget;
use Coolsam\Modules\StatsOverviewWidget;

Or apply CanAccessTrait directly if you need to keep the original parent (e.g. a resource that must extend a domain-specific base class). Note the README marks this section WIP — treat it as "read the trait source before relying on it for real authorization" rather than a security boundary.

Reuse across projects

The real payoff: a module with composer.json, app/Filament/BlogPlugin.php, its own resources and migrations can be composer require'd into another Laravel app and appear in the panel by virtue of ModulesPlugin alone. Keep modules free of host-app assumptions — reference contracts and config keys, never env() or hardcoded panel class names — and this works. Break that discipline and you get a module that only runs in one repo.

Gotchas and Tips

Class "Modules\X\..." not found after creating a module. Almost always the merge-plugin block, or a missing composer dump-autoload. Run composer dump-autoload after every module:make. In CI, remember fresh checkouts need it too — it's not in the lockfile behaviour you'd expect.

Config caching hides your config edits. filament-modules.php is read at panel boot. php artisan config:clear after every change, and never assume a mode switch took effect without clearing.

The install command is interactive. modules:install and module:filament:install prompt for cluster choices. Don't put them in deploy scripts or Dockerfiles — run them once locally, commit the generated files and config. Likewise module:make:filament-* will hang in non-TTY contexts.

Register module panel providers in bootstrap/providers.php. In panel mode, generated *PanelServiceProvider classes need to be in the provider list (Laravel 11+ style) or auto-discovered via the module's service provider. If module panel links don't appear in the main navigation, this is the first thing to check — followed by whether mode actually includes panels.

Navigation grouping only applies in panel mode. panels.group, group-icon, open-in-new-tab, and group-sort do nothing under mode => plugins. Setting them and expecting a sidebar group is a common dead end.

Maturity risk is real. 212 stars, near-zero dependents, fast-moving major versions, and several features explicitly marked WIP (widgets, themes, access control). Pin the exact version in composer.json, read the source of anything WIP before depending on it, and budget time for upgrades between minor releases — the changelog shows frequent patch releases, so ^5.3 with a lockfile is safer than ^5.

Debugging discovery, in order: dump config('filament-modules') → check the module's module.json providers list → confirm the generated plugin class exists and its getId() is unique → verify ModulesPlugin is registered on the panel you're actually viewing. Resource-not-appearing bugs are almost always one of these four, not a Filament bug.

Extension points to prefer over forking: each generated plugin and cluster class is plain PHP you own — override register(), boot(), navigation methods there. The panel provider's discover*() calls are your hook for scoping. Forking the package should be a last resort; the base classes (Coolsam\Modules\Resource etc.) are thin wrappers you can replace if needed.

Keep module migrations and seeders inside the module and run them via module:migrate rather than copying into database/migrations. Copying defeats the exportability that makes this package worth using.

Test module panels as real panels. Because panel mode produces standalone Panel objects, Livewire::test() against a module page without the correct panel context gives false passes or confusing 404s. Set the current panel in tests (or hit the panel route) rather than testing components in isolation.

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.
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log
hsluv/hsluv
seampact/codegen
seampact/hooks
seampact/ir
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table