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

jeffersongoncalves/filament-teams

jeffersongoncalves/filament-teams adds team and membership management to Filament panels. Create teams, invite members, assign roles, and manage team resources through a ready-made admin interface built to integrate with Laravel models and permissions.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and register (5 minutes)

composer require jeffersongoncalves/filament-teams:"^1.2"
php artisan vendor:publish --tag="filament-teams-config"
php artisan vendor:publish --tag="filament-teams-migrations"
php artisan migrate

Register the plugin on the panel that should be team-aware, in your PanelProvider:

->plugin(FilamentTeamsPlugin::make())
->tenant(Team::class)          // Filament tenancy
->tenantMenu(true)

Make sure your User model has the team relationship the package expects (teams() / ownedTeams() — check the published migration and the package's Concerns traits), and that the Team model is a proper Filament tenant (slug + getFilamentName()).

Where to look first

  1. config/filament-teams.php — the knobs you'll actually touch (model bindings, invitation expiry, feature toggles, route/slug prefix).
  2. resources/lang/ inside the package — since 1.2.0 this ships 17 locales (ar, az, de, es, fa, fr, hi, it, ja, nl, pl, pt, ru, tr, uk, uz, zh_CN) and is the reference for every translatable string.
  3. The plugin class itself (FilamentTeamsPlugin) — it's the extension point for adding/removing resources and pages without forking.

First use case to validate the install

Log in, create a team from the tenant menu, invite a second user by email, accept the invite in an incognito window, then switch between teams and confirm that tenant-scoped resources show different records per team. If that loop works, the wiring (tenancy middleware, policies, invitation flow) is correct and you can start layering your domain resources on top.

Implementation Patterns

Tenant-scoped resources are the default, not the exception

Once ->tenant(Team::class) is set, every resource registered on that panel is tenant-scoped automatically via Filament's tenantOwnershipRelationshipName. Create new resources with php artisan make:filament-resource Invoice --tenant and add team_id to the table + a team() relationship. Keep genuinely global resources (e.g. a Country lookup) on a separate non-tenant panel rather than fighting the scoping.

Invitation → membership → role assignment

The typical workflow: an owner invites via the team members page, the package creates a pending invitation record, the invitee accepts and a pivot row is created. For day-to-day work, hook your own logic into the acceptance step (e.g. assign a default role, seed starter records) rather than overriding the page class:

// Observer / listener on membership creation
TeamMemberAdded::class => SeedTeamDefaults::class,

Roles: delegate to spatie/laravel-permission (or the package's built-in roles)

If the package exposes role selection, back it with policies per team rather than per user. A TeamPolicy that resolves $user->roleIn($team) keeps authorisation readable and consistent across resources, pages, and the invitation flow.

Localisation workflow (new in 1.2.0)

Ship the package's translations by publishing them once:

php artisan vendor:publish --tag="filament-teams-translations"
# → lang/vendor/filament-teams/{ar,az,de,...}/team.php

Then per-panel locale switching works out of the box:

->plugin(FilamentTeamsPlugin::make())
// users with app()->setLocale() / a locale switcher in the topbar

If your app supports fewer locales than the plugin, either publish and delete the unused folders or gate the switcher on config('app.supported_locales').

Customising without forking

Prefer: (a) config-level overrides, (b) ->resources([...]) on the plugin to swap in your own subclassed resource/page, (c) a published view override in resources/views/vendor/filament-teams/. Subclassing beats editing vendor files and survives composer update.

Pair it with a policy + test suite

Use Pest/Livewire tests with actingAs($user) plus Filament::setTenant($team) so tenant-scoped assertions are realistic. Test the invite-accept path and the "user from team A cannot see team B's records" assertion explicitly — that's the failure mode that matters.

Gotchas and Tips

Locale folders are region-variant where you may not expect it. zh_CN and bare pt (not pt_BR) are what shipped in 1.2.0. If your app uses pt_BR or zh_TW, Laravel's fallback locale resolution will miss and silently render the fallback language. Set 'fallback_locale' => 'en' explicitly and add your own pt_BR folder if needed.

Publishing translations does not auto-update. Publish only once, or re-publish with --force when upgrading and then re-apply your edits — vendor lang files take precedence over the package's, so a stale published copy will hide new 1.2.0 keys. If some team strings appear in English while the rest are translated, this is almost always the cause.

Clear caches after any config or plugin change. php artisan config:clear && php artisan filament:optimize-clear (or optimize:clear). Plugin registration inside a cached config is a classic "why is the team menu gone in production" bug.

Existing users don't get teams retroactively. The migration creates the pivot table; it does not backfill. Write a one-off command that creates a personal team per user and attaches them, or the tenant menu will be empty for your whole existing user base.

Tenant middleware order matters. If you get 404s on team-scoped pages, check that Filament\Http\Middleware\IdentifyTenant (or the package's equivalent) is in the panel's middleware stack and that your Team route key is the slug, not the id. Mixing the two produces URLs that resolve in one panel and 404 in another.

Invitations and queued mail. If ShouldQueue mail is enabled, invitation acceptance tokens can be consumed out of order during local testing with QUEUE_CONNECTION=sync toggled on/off. Keep sync while developing the flow, then verify with a real queue worker before shipping.

Don't edit vendor/ for role or field changes. The public extension points — config model bindings, plugin resource overrides, published views, and model observers — cover nearly everything. Anything that genuinely requires a fork is worth opening upstream, given this package has an active release cadence (1.1.0 → 1.2.0 was purely additive; no breaking changes to plan around).

Upgrading 1.1.0 → 1.2.0 is safe. The release is translations plus CI/dependency housekeeping only. No migrations, no signature changes, no deprecations — bump the constraint, run composer update, and re-publish translations only if you want the new locales.

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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core