peltonsolutions/filament-common
Shared utilities and reusable components for Filament admin panels, offering common helpers, form and table conveniences, and small building blocks that keep your Filament projects consistent and reduce duplicated code.
Treat the source as the specification. There is no docs site, no README worth the name, and one release (v1.0.1, "set minimum-stability to stable"). You will learn this package by reading it, so budget a half-day spike before you write a single line of application code.
Step 1 — Pin and install.
composer require peltonsolutions/filament-common:1.0.1 --prefer-dist
Pin the exact version, not a caret range. With a single release and no changelog discipline, ^1.0 gives you no guarantee that 1.1 won't silently change a base class you inherit from.
Step 2 — Read composer.json in vendor/peltonsolutions/filament-common/ before anything else. This is your only compatibility statement. Check:
filament/filament constraint — is it ^3.0, ^4.0, or unconstrained? This determines whether the package is usable at all on your panel.php, laravel/framework, livewire/livewire constraints.require-dev — if there are no dev dependencies, there are almost certainly no tests, and you should assume zero test signal.license — verify it before it lands in a business-critical repo.Step 3 — Read the service provider. This is the map of the whole package. Look for what it actually does:
register() — Macroable registrations on Field, Column, Action, Table, Form; container bindings; config merges.boot() — loadViewsFrom, loadTranslationsFrom, publishes, Blade::component, Filament::register* calls, and any Panel::configureUsing() hooks.Plugin class implementing Filament\Contracts\Plugin (register it in PanelProvider::panel()->plugin(...)) or is pure trait/macro sugar (no registration needed).If it auto-discovers (Laravel extra.laravel.providers in composer.json), you're done installing. If it ships a plugin, add it explicitly:
->plugin(\Peltonsolutions\FilamentCommon\FilamentCommonPlugin::make())
Step 4 — Inventory the public surface. Walk src/ top-down and enumerate, in a scratch file:
Concerns/ or Traits/ — the traits are the real product. Each is a reusable behavior you'd otherwise copy-paste.Resources/ / Pages/ / Widgets/ — any Base* abstract classes. These are where inheritance cost lives.Macros/ or Macros.php — closures registered onto Filament builder objects.Support/ / Helpers/ — static utilities with no Filament coupling.config/ — if there's a publishable config, php artisan vendor:publish --tag=filament-common-config and read every key.Step 5 — First use case: adopt exactly one trait on one low-risk resource. Do not refactor a panel on day one. Pick a resource that isn't on a critical path, apply a single trait or a single macro (e.g. a shared table action or a house-style currency column), confirm it renders and behaves in the browser, and only then consider a second adoption.
// A typical shape, verify names against the vendor namespace
use Peltonsolutions\FilamentCommon\Concerns\HasCommonTableColumns;
class OrderResource extends Resource
{
use HasCommonTableColumns;
public static function table(Table $table): Table
{
return $table->columns([
...static::commonTableColumns(),
TextColumn::make('total')->money('usd'),
]);
}
}
Where to look first when something breaks: vendor/peltonsolutions/filament-common/src/ — specifically the trait or macro you consumed, then the service provider. There is no issue tracker history to search, no Stack Overflow corpus, and no community to ask. Debugging time is source-reading time.
Wrap, don't scatter. The highest-value pattern with a package like this is a thin local adapter layer so application code never imports the vendor namespace directly:
app/Filament/Support/
├── CommonTableColumns.php // re-exports / re-implements the vendor behavior
├── CommonFormSchema.php
└── CommonActions.php
Your resources call app/Filament/Support/*. Those classes call the vendor package. When the vendor package dies — and with a bus factor of one, plan for it — you replace three files instead of grepping forty resources.
namespace App\Filament\Support;
class CommonTableColumns
{
public static function standard(): array
{
return [
\Peltonsolutions\FilamentCommon\Macros\Tables::timestampColumn('created_at'),
];
}
}
Trait composition over base-class inheritance. If the package ships both a BaseResource and traits, prefer the traits. Inheriting from a vendor abstract class couples you to its constructor, its static method signatures (which change across Filament majors), and any protected helpers it renames. Traits let you adopt one behavior at a time and drop it when it hurts.
If you must use a base class, put your own App\Filament\Resources\BaseResource in between so you have a single seam to patch:
abstract class BaseResource extends \Peltonsolutions\FilamentCommon\Resources\Resource
{
// everything in the app extends this, never the vendor class directly
}
Adopt piecemeal, verify each piece. Because the surface is undocumented, test each trait/macro in isolation before stacking them. A macro that looks harmless on a TextColumn can conflict with another plugin's macro of the same name (Filament macros are global and last-registered-wins).
Version pinning plus a fork plan. In composer.json, pin the exact version, and add a patch or replace strategy up front:
"require": {
"peltonsolutions/filament-common": "1.0.1"
},
"replace": {
"peltonsolutions/filament-common": "self.version"
}
The replace entry lets you git clone into packages/filament-common, patch it, and point a path repository at it — an afternoon's work done in advance rather than during an incident on a Filament major upgrade.
Testing pattern. Since you inherit whatever quality exists (probably none), add characterization tests at your adapter boundary, not the vendor's:
it('renders standard columns', function () {
livewire(OrderResource\Pages\ListOrders::class)
->assertCanSeeTableRecords(Order::factory()->count(3)->create())
->assertTableColumnExists('created_at');
});
These survive a vendor forking event; tests written against the vendor namespace do not.
Compatibility matrix in CI. Run the app against the package on your actual PHP/Laravel/Filament/Livewire versions. Then add one throwaway job against the next Filament major (in a matrix include: block) so you find out about a break before a Filament upgrade does it for you in production.
The Filament major version is unstated, and that is the biggest risk. v1.0.1 of the package is not the same thing as a Filament v3/v4 compatibility guarantee. Check the constraint in composer.json and check the API usage in src/ — if it calls Filament::registerNavigationGroups() (v2) versus Panel::configureUsing() (v3+), you know what it targets. If it's unconstrained or spans both, assume nothing.
Macros are global and silently collide. Field::macro('money', ...) is process-wide. If this package registers a macro name that a community plugin also registers, whichever service provider boots last wins, and the failure mode is a wrong-looking column, not an exception. Inventory macro names early (grep -rn "::macro" vendor/peltonsolutions/filament-common/src) and note them against your other plugins.
Asset and Tailwind registration. If the package ships Blade views with classes, Filament v3 needs those views in your Tailwind content paths or the styling won't compile:
// tailwind.config.js
content: [
'./vendor/peltonsolutions/filament-common/resources/views/**/*.blade.php',
],
Symptom: components render but look unstyled. If it publishes assets, php artisan filament:assets after vendor:publish.
Config caching bites. If there's a publishable config, php artisan config:cache in deploy means the values are frozen. Change a config value, forget to rebuild, and the panel shows old behavior with no error. Standard Laravel problem, but more confusing here because there's no documentation telling you the config exists.
Navigation and panel registration are not automatic. A package can define navigation groups or resources that simply don't appear until you register the plugin in the PanelProvider and the user has the right panel access. If a shared page 404s or is missing from the sidebar, check in this order: plugin registered → panel ID matches → canAccess() / policy → navigation group sorting.
N+1 queries from shared table columns. A shared "user" or "team" column that calls $record->user->name without eager loading will produce N+1 on every list page you apply it to — across every resource, at once. When you adopt a shared column set, immediately run DB::listen() or Laravel Debugbar on the biggest table and check query counts before and after.
Debugging a macro or trait you can't trace. Two tools that cut straight to it:
// dump the resolved column/field object and inspect its closure source
ray($table->getColumns());
and reflection on the registered macro:
$ref = new ReflectionFunction(\Filament\Tables\Columns\TextColumn::getMacros()['timestampColumn']);
dump($ref->getFileName(), $ref->getStartLine());
That tells you the exact vendor file and line — usually faster than grepping the whole package.
Do not adopt on a single panel or a critical surface. The PM/TPM assessments are right about the economics: the package pays off when it removes drift across multiple Filament surfaces. On one panel it's overhead; on a revenue- or compliance-sensitive admin it's an unhedged bet on a zero-star, zero-dependent, single-maintainer dependency. Use it — or its ideas — on internal back-office tooling with a feature-flag fallback, and keep a written rollback path.
Better extension point than patching in place: if you find an obvious bug, prefer a Composer patch (cweagans/composer-patches) with a tracked patch file over committing to vendor/. When the next version lands (or never lands), the patch set is your diff against upstream and documents exactly what you changed. Combined with the replace entry above, you can promote a patch into a proper fork the day the package stops working on a new Filament major.
How can I help you explore Laravel packages today?