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

realmrhex/filament-modular

Filament Modular helps organize Laravel Filament applications into self-contained modules, automatically discovering and registering resources, pages, widgets, and plugins so large admin panels stay clean, scalable, and easy to maintain across teams.

View on GitHub
Deep Wiki
Context7

Getting Started

Prerequisites (check these first — they are the usual failure point):

composer require nwidart/laravel-modules
php artisan vendor:publish --provider="Nwidart\Modules\LaravelModulesServiceProvider"
composer require realmrhex/filament-modular

You also need a working Filament panel (php artisan filament:install --panels) and a decision on Filament major version — see Gotchas before you commit, because the last release is April 2023.

Minimal setup:

  1. Read the README top-to-bottom. This is a 61-star, single-maintainer package; the README is the spec, and there is no separate docs site. Budget ten minutes here rather than guessing at class names.
  2. Publish any package config:
    php artisan vendor:publish --tag=filament-modular-config
    
    Confirm the tag from the README — if the package ships no config, skip this and look at its service provider instead (vendor/realmrhex/filament-modular/src/), which will show you exactly which Filament registries it hooks into.
  3. Scaffold a module and a Filament artifact inside it:
    php artisan module:make Blog
    php artisan module:list          # verify it registered
    composer dump-autoload
    
  4. Create your first resource inside the module (either via the package's generator, if one is provided, or by hand at Modules/Blog/Filament/Resources/PostResource.php with namespace Modules\Blog\Filament\Resources).
  5. Clear everything and load the panel:
    php artisan optimize:clear
    php artisan filament:optimize-clear
    

Where to look first when something doesn't appear: the panel provider's panel() method, then config/modules.php (paths.modules, namespace), then the package service provider. If a resource is missing from the sidebar, it's almost always a namespace/path mismatch between those three, not a package bug.

Ideal first use case: one module owning one bounded context end-to-end — migrations, models, policies, and a Filament resource — so you feel the seam between "module code" and "panel registration" on a small surface before scaling to ten modules.

Implementation Patterns

Let the module own its slice, keep the panel thin. The healthiest pattern with this package is: a module contains Models/, Database/Migrations/, Policies/, and Filament/Resources|Pages|Widgets/. The app/Filament directory shrinks to near-empty or is removed entirely, and panel providers become pure configuration.

Convention-based discovery is the primary workflow. Rather than registering each class, point the panel at module paths:

// app/Providers/Filament/AdminPanelProvider.php
public function panel(Panel $panel): Panel
{
    return $panel
        ->id('admin')
        ->discoverResources(in: module_path('Blog', 'Filament/Resources'), for: 'Modules\\Blog\\Filament\\Resources')
        ->discoverPages(in: module_path('Blog', 'Filament/Pages'), for: 'Modules\\Blog\\Filament\\Pages')
        ->discoverWidgets(in: module_path('Blog', 'Filament/Widgets'), for: 'Modules\\Blog\\Filament\\Widgets');
}

If the package already does this for all enabled modules, use its mechanism and skip the boilerplate — but verify the discovery is per-panel, not global, or every panel will show every module's resources.

Register a panel provider per module for large installs. Instead of one AdminPanelProvider discovering everything, give heavy modules their own provider (Modules/Blog/Providers/BlogPanelServiceProvider.php) registered in module.json's providers array. Benefits: modules stay removable, panel-specific navigation/colors/->path() are local, and a broken module can't take down the whole admin.

Multi-panel by audience, not by module. A common mistake is one panel per module. Better: admin (internal staff) and portal (customers), each discovering the same module directories but with different ->login(), ->colors(), and canAccessPanel() gates. Guard access with a role check, not with panel separation alone.

Cross-module references. When Module A's resource needs Module B's model in a Select::make('team_id')->relationship(), depend on the model, not the resource. Keep modules decoupled at the Eloquent layer; use events or a small shared contract module for anything richer. Registering B's resource from A's provider creates a hard load-order dependency that bites during module:disable.

Policies need explicit wiring. Filament's auto-discovery of policies assumes App\Models\*. Module models under Modules\Blog\Models\Post will silently lose authorization. Register explicitly:

// Modules/Blog/Providers/BlogServiceProvider.php
protected function gate(): void
{
    Gate::policy(\Modules\Blog\Models\Post::class, \Modules\Blog\Policies\PostPolicy::class);
}

Commands as the daily driver. Wire module generators into your workflow (php artisan module:make-model, module:make-migration, plus the package's Filament generator if present) and standardize on php artisan module:migrate Blog / module:migrate --all. Never run bare php artisan migrate expecting module migrations — depending on config/modules.php they may be excluded from the default path.

Testing per module. Livewire-test each resource inside its own module test suite so a module can be extracted or disabled without dragging tests along:

livewire(ListPosts::class)->assertCanSeeTableRecords($posts);

Run with php artisan test --testsuite=Modules or a path-filtered PHPUnit config.

Navigation as the module's contract. Set protected static ?string $navigationGroup and a module-prefixed $navigationLabel inside each resource. This is the cheapest way to make a 15-module panel navigable without a custom navigation builder.

Gotchas and Tips

The big one: version drift. Last release 2023-04-24 means this was built against the Filament v2 era (and possibly early v3). Filament v3 split forms/infolists out of Resource, renamed namespace roots (Filament\Resources\Resource → largely stable, but getForms()/getInfolists() replaced form()/table()-adjacent APIs), and v4 changed more again. Before installing, check composer.json in the package for its filament/filament constraint. If it caps at ^2.0 or ^3.0, treat it as a reference implementation, not a dependency — the discovery code you need is genuinely ~15 lines (see the snippet above).

Composer autoload is not automatic for new modules. module:make writes files; your autoloader may not know about them until composer dump-autoload. Symptom: "Class not found" on a class that visibly exists. If you use a modules/ root with PSR-4 in composer.json, ensure the namespace map matches config/modules.php exactly.

Caches fight with discovery — in three separate layers. Clear all of them when debugging a missing resource:

php artisan optimize:clear        # config/route/view
php artisan filament:optimize-clear
php artisan module:clear-compiler 2>/dev/null || true

In production, php artisan filament:optimize and php artisan optimize must both be run after modules are in place, and Filament's component cache will not notice a module enabled post-deploy without a re-optimize.

Config cache breaks dynamic module paths. module_path() calls inside a panel provider are fine, but if the package resolves enabled modules at boot and you cache config, newly enabled modules won't appear until cache clear. Prefer php artisan optimize:clear && php artisan filament:optimize as a single deploy step.

module:disable is not graceful for Filament. Disabling a module whose resources are referenced by other modules' forms yields runtime errors at render time rather than boot time. If removable modules are a real requirement, gate cross-module relationships behind class_exists() checks or shared interface bindings, and add a smoke test that boots the panel with each module disabled.

Asset and Vite paths per module are not handled. Module-scoped CSS/JS needs a custom vite.config.js input and a @vite call in the panel's ->renderHook() or a custom theme. Don't expect the package to solve this.

Slug collisions are silent. Two modules each with a PostResource produce duplicate $slug values and the later registration wins on route resolution. Prefix slugs (blog-posts) or rely on distinct navigation groups plus explicit $slug.

Debugging checklist, in order: php artisan module:list → php artisan about (confirm providers) → php artisan route:list | grep -i blog → dump the panel's registered resources via Filament::getPanel('admin')->getResources() in tinker → check the generated class's namespace against the for: string character-by-character. That last step resolves the majority of "it just doesn't show up."

Bus factor and maintenance. 61 stars, MIT, one release two years stale. Mitigations: pin the exact version, avoid spreading its API across dozens of files (wrap it behind your own ModulePanelRegistrar if you must), and keep the fallback in your back pocket — the equivalent of this package is a handful of ->discoverResources(in:, for:) calls plus a foreach over Module::allEnabled(). If you outgrow the package or need current Filament support, replacing it is an afternoon, not a quarter.

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.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle