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

Modulite Laravel Package

panicdevs/modulite

Modulite automatically discovers and registers Filament panel providers, resources, pages, and widgets across modular Laravel apps. Supports nwidart/laravel-modules and panicdevs/modules, with file-based caching and commands for production performance.

View on GitHub
Deep Wiki
Context7

Getting Started

Modulite is a convention layer between a modular Laravel app (nwidart/laravel-modules or panicdevs/modules) and Filament. It removes the "remember to register your new resource in the panel provider" step. Install it once, then verify it with a throwaway module before you trust it with a real feature.

Four steps to a working panel:

composer require panicdevs/modulite
php artisan vendor:publish --tag=modulite-config

Then in app/Providers/Filament/AdminPanelProvider.php:

use PanicDevs\Modulite\Plugins\ModulitePlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        ->default()
        ->id('admin')
        ->path('/admin')
        ->plugins([
            ModulitePlugin::make(),   // <- the only line that matters
        ]);
}

Then confirm what the package actually sees:

php artisan modulite:status

Do not skip status. It is the ground truth for the whole package — config summary, discovered panels, discovered components per panel, and cache state. Reading config/modulite.php plus the output of status --vvv is effectively the entire API surface; the README is a good tour but the config file is authoritative.

First use case — prove discovery end to end in five minutes. Create a single resource in a module and watch it appear in the panel without touching a provider:

modules/User/Filament/Admin/Resources/UserResource.php

The class must extend Filament\Resources\Resource and be named *Resource. Hit /admin. If it shows up, discovery works and you can start migrating existing resources. If it does not, you have found the exact place to start debugging before you've moved fifty files.

Where to look first when evaluating fit:

  1. config/modulite.php — panels.locations, components.locations, modules.approach, cache. These four keys decide everything.
  2. php artisan modulite:status — after each structural change.
  3. bootstrap/cache/modulite.php — the generated artifact; if a component is missing in production but present locally, this file is why.

Note the requirement floor: PHP 8.2+, Filament 4.0+. Filament v5 support landed in v1.1.1, so pin ^1.1 at minimum rather than ^1.0 — v1.0.0 had breaking discovery changes and v1.1.0 fixed the panel resolver and cache issues.

Implementation Patterns

The directory contract is the API. Everything hinges on the layout, so codify it in the module stub (or a modules/stubs/ template) early:

modules/{Module}/
├── Providers/Filament/Panels/{Module}PanelProvider.php
└── Filament/{Panel}/
    ├── Resources/
    ├── Pages/
    └── Widgets/

{Panel} is the panel id in Studly case — admin → Admin, manager → Manager. Verify with modulite:status rather than assuming, because a mismatch here fails silently rather than loudly.

Pattern 1 — one panel, many modules (the common case). Each module contributes resources under Filament/Admin/, the app has a single AdminPanelProvider in app/Providers/Filament, and ModulitePlugin::make() is added there. This is the highest-value, lowest-risk configuration and the one to pilot.

Pattern 2 — multiple panels, per-module panel providers. A module that owns its own panel puts the provider at modules/Blog/Providers/Filament/Panels/BlogPanelProvider.php; the service provider discovers and registers it automatically. Each panel still needs its own ModulitePlugin::make() inside its plugins([...]) array — the plugin is per-panel, not global. Components then route by directory:

// modules/User/Filament/Admin/Resources/UserResource.php    -> admin panel
// modules/User/Filament/Manager/Resources/ProfileResource.php -> manager panel
// modules/User/Filament/Public/Pages/LoginPage.php          -> public panel

This is the pattern where Modulite earns its keep: value scales roughly with modules × panels. With one panel and three modules it adds a moving part without removing meaningful toil.

Pattern 3 — the deploy workflow. Treat modulite:cache as a sibling of optimize, in that order:

php artisan optimize
php artisan modulite:cache

And php artisan optimize:clear already clears it, so standard "something is stale" muscle memory works. The cache is a single PHP file at bootstrap/cache/modulite.php, structurally identical to config.php/routes.php — same gitignore story, same per-environment artifact. It is not committed; it is built on the deploy target.

Pattern 4 — environment-shaped config. Don't hard-code; let the shipped defaults do the work and only override in .env:

MODULITE_CACHE_ENABLED=true
MODULITE_CACHE_TTL=0          # never expire in production
MODULITE_LAZY_DISCOVERY=true
MODULITE_STRICT_INHERITANCE=false

Locally the defaults already disable caching under debug mode with a 5-minute TTL and auto_invalidate on, which is roughly what you want. Resist the urge to force caching on in dev — you'll spend the time you saved on modulite:cache --force.

Pattern 5 — enforce conventions on a large team. For a codebase with many contributors, tighten validation so a resource that extends an intermediate base class (or forgets the suffix) fails visibly instead of being quietly skipped:

'panels' => [
    'validation' => [
        'strict_inheritance' => true,
        'allow_custom_base_classes' => false,
    ],
],

The inverse is the default and is right for a small team: permissive, allow_custom_base_classes => true. Pick one deliberately — the worst outcome is strict validation nobody knows is enabled, producing an empty menu with no error.

Pattern 6 — escape hatch. If you need deterministic ordering or a component must be registered conditionally at runtime, turn discovery off for that concern:

'components' => [
    'registration' => ['auto_register' => false],
],

…and register manually as before. Useful when migrating incrementally: leave discovery on for Resources, keep Widgets manual until you've verified the dashboard.

Integration tip — migrate by directory move, not rewrite. For an existing app, move one module's resources into Filament/Admin/Resources/, delete its manual ->resources([...]) entries, run modulite:status, and confirm the count matches. Repeat per module. This makes each commit small and reversibble and keeps the diff reviewable.

Gotchas and Tips

The cache-poisoning history is the single biggest operational risk. v1.1.3 and v1.1.4 both address the same failure: a scan that failed silently under fail_silently returned an empty panel list, which was then cached with TTL=0 — permanently, in production. Result: every panel offline until someone manually cleared the cache. The fix (only caching scans that completed without error) means you should be on v1.1.4 or later. Two releases for the same bug also tells you this failure mode recurs in practice, so add a smoke test to your deploy that asserts the admin panel returns 200 after modulite:cache.

Never set TTL=0 and disabled=false in a debug environment. 0 means "never expires." That's correct for immutable production artifacts and wrong for a machine where you're editing files. The shipped default (!app()->hasDebugModeEnabled(), TTL 300 in debug) is sensible; if you override it in .env, override it per-environment and make sure your local .env doesn't inherit the production value through a shared example file.

Silent failure is the default failure mode. In production the package is designed to fail gracefully — which means a broken path pattern, a typo'd namespace, or a class that doesn't extend the expected base produces an empty menu, not an exception. When a component is missing, work the checklist in this order:

  1. php artisan modulite:status — is it listed?
  2. Filename ends in Resource / Page / Widget? (suffix is load-bearing)
  3. Class extends the correct Filament base class?
  4. Directory case matches the panel id (Admin, not admin)?
  5. Cache stale? → modulite:cache --force
  6. Still nothing? → MODULITE_LOGGING_ENABLED=true and MODULITE_LOG_LEVEL=debug locally.

modulite:cache is part of the deploy, not optional. New module deployed without re-running it = module's resources never appear. Wire it into the deploy script immediately after optimize, before any cache warm-up that would render a panel. If you use a release-per-directory deploy (Envoyer, Deployer), confirm bootstrap/cache is written in the new release directory before the symlink flips.

Config caching interaction. php artisan config:cache will bake the published config in. If you change components.locations and only clear the app cache, the pattern change won't take effect. Clear config cache and Modulite cache together — optimize:clear does both, which is why it's the safe habit.

{panel} is a placeholder, * is a module wildcard — and they're not interchangeable. Adding a non-standard module root (e.g. foundation/*/Providers/Filament/Panels, as the README shows) means editing panels.locations. The defaults only cover modules/* and foundation/*; a packages/ or domains/ root will be invisible until you add it. This is normal in an app that's half-migrated to modules.

Module enabled state matters. With scan_only_enabled => true, a disabled module's resources vanish from the panel — which is the intended behavior but surprising during a feature flag rollout. Check modules.*.enabled in modules_statuses.json before debugging discovery.

Don't use lazy_discovery as a fix for slowness. The benchmarks are already good: ~1–20ms cold, ~1–2ms warm. If the panel feels slow, the problem is almost certainly that caching is off (.env overriding the default) or modulite:cache isn't in the deploy — not that discovery is eager. Run modulite:status --vvv before tuning anything.

Watch for base-class drift. If your team has an internal BaseResource extending Filament's Resource, permissive mode handles it. But when Filament ships a major version, the must_extend / strict_inheritance combination is where the breakage surfaces first. Add "check modulite:status shows the expected component count" to the Filament upgrade runbook.

Small team-size signal worth noting during evaluation: 22 stars, 0 dependents, and a release cadence where the same production-down bug needed two attempts. The architecture is sound and the conventions are Laravel-idiomatic, but it is a young package with a small maintainer surface — there's no community to hit your edge case first. That argues for: pilot on one non-critical panel, pin ^1.1.4, keep auto_register toggles documented so you can fall back to manual registration in an hour, and keep the smoke test from the first tip in CI.

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.
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel