mortalkiller/filament-package-template
A Laravel package template for building Filament plugins and extensions, offering a ready-made scaffold with sensible structure and conventions to help you start, organize, and ship reusable Filament components faster. Ideal for developers seeking a clean starting point for custom Filament packages.
mortalkiller/filament-package-template is a scaffolding template, not a runtime dependency — you consume it once to generate a new package repository, then you never require it at runtime. Treat the version constraint as "the version of the generator I used", not something Composer will upgrade for you.
Start by creating a project from the template (or using the repo as a GitHub template), then let the interactive installer ask which profile you want:
composer create-project mortalkiller/filament-package-template:^2.0.1 my-plugin --stability=stable
cd my-plugin
composer install
Pin ^2.0.1 explicitly. v2.0.0 was tagged from the pre-v2 source and is not a valid starting point — it will give you the old template with none of the profile/opt-in machinery described in these notes.
Where to look first, in order:
composer.json — the generated scripts (test, analyse, format, rector, etc.) are the real table of contents for the template's tooling.src/*ServiceProvider.php — this is where Laravel Package Tools wiring lives and where you will spend most of your scaffolding time.workbench/ — a miniature Laravel app with a Filament panel that mounts your plugin.tests/ — the strict PHPUnit config and the browser-test setup tell you exactly what CI expects.First use case: pick the plugin profile, run composer test, then boot the Workbench panel to see your (empty) plugin rendered inside a real Filament panel before you write a line of feature code. That loop — edit provider/resource, run tests, refresh workbench — is the day-to-day rhythm.
Choose the profile deliberately — it is close to a one-way door. The five profiles map to genuinely different skeletons:
plugin — a Filament plugin (panel extension via the Plugin contract), the default for most packages.theme — a Filament theme package with asset/CSS pipeline emphasis.forms / tables — focused packages extending Filament form or table behaviour, typically without a panel.library — no Filament panel dependency; the Laravel Package Tools skeleton with the Filament-specific bits stripped out.Switching afterwards means copying directories by hand, so decide before create-project.
Opt-in features are additive, not retroactive. Config, migrations, views, translations, stubs, assets, Workbench, browser tests, and Rector are all selected at scaffold time. Each one has two halves that must both be present:
config/, database/migrations/, resources/views/, resources/lang/, resources/dist/, stubs/).configurePackage() flag in your service provider.If you skipped a feature and want it later, the cleanest path is to scaffold a throwaway package with that feature enabled and copy both the files and the provider lines across.
Lean on Laravel Package Tools for the runtime wiring. Most of your provider is declarative:
public function configurePackage(Package $package): void
{
$package
->name('filament-package-standard-example')
->hasConfigFile()
->hasViews()
->hasTranslations()
->hasMigrations(['create_examples_table'])
->hasAssets()
->hasInstallCommand(fn (InstallCommand $command) => $command->publishesMigrations()->publishesAssets());
}
Resist the urge to hand-roll loadViewsFrom / mergeConfigFrom calls — the template's tests and the install command both assume the Package Tools conventions.
Filament plugin registration pattern. For plugin-profile packages, implement the Plugin contract and keep register(Panel $panel) a thin list of resources/pages/widgets. Register the plugin in the Workbench panel provider so tests and the browser suite exercise the real panel:
public function panel(Panel $panel): Panel
{
return $panel
->id('admin')
->plugin(ExamplePlugin::make());
}
Use the generated scripts as your gate, not ad-hoc commands. Run composer test (strict PHPUnit), composer analyse (Larastan 6+), composer rector (if opted in), and the browser suite in that order before pushing. The CI matrix (PHP 8.2–8.5 × Laravel 12/13, plus a Windows job) is expensive to iterate on remotely, so reproduce its lowest boundary locally — PHP 8.2 + Laravel 12 + Filament 5.8 — before opening a PR.
v2.0.0 is a poisoned tag. It was cut from pre-v2 source. Never scaffold from it and never let a lockfile resolve to it. ^2.0.1 is correct.src/ even though the top of the matrix allows it. The Windows job exists for template tooling compatibility — it does not guarantee your package's browser tests pass on Windows.phpunit.xml.dist before adding tests.@extends, @property, and @method docblocks to Resources, Pages, and Table/Form schemas. Do it as you go — bolting docblocks on at level 6 later is miserable.src/. Keep the boundary explicit; the library profile exists precisely for cases where the Workbench/panel coupling is unwanted.resources/dist/. Forgetting to build before tagging ships a broken style to consumers, and browser tests won't catch it if they run against source.uses: you add must use a full commit SHA, not a tag, or the audit job fails. Fork PRs also won't have access to repository secrets — keep workflows secret-free or guard on pull_request_target carefully.stubs/ directory (if opted in) lets you customise what your own package publishes via hasInstallCommand; configurePackage() is the single lever for feature toggling; the profile prompts themselves live in the template tooling, so if you need a new profile, fork the template rather than fighting the generated output.composer require mortalkiller/filament-package-standard:^2.0 and re-scaffold — the ^2.0.1 constraint applies to the template at creation time.How can I help you explore Laravel packages today?