Skip to content

Plugins Usage Guide

This guide explains how to use Filament Tenancy plugins to configure central administration panels (landlord) and tenant panels.

Filament Tenancy provides two main plugins to integrate multi-tenancy with Filament:

  • TenancyLandlordPlugin: For the central administration panel (admin/landlord)
  • TenancyTenantPlugin: For tenant panels

These plugins automatically configure the necessary middlewares and access restrictions to ensure security and isolation between tenants.

Before using the plugins, make sure you have:

  1. ✅ Filament installed (composer require filament/filament:"^4.0")
  2. ✅ Run php artisan filament-tenancy:install
  3. ✅ Have at least one Filament panel created

This guide will show you step by step how to configure both panels (Admin and Tenant) with Filament Tenancy plugins.

Create or modify the file app/Providers/Filament/AdminPanelProvider.php:

<?php
namespace App\Providers\Filament;
use AngelitoSystems\FilamentTenancy\FilamentPlugins\TenancyLandlordPlugin;
use Filament\Http\Middleware\Authenticate;
use Filament\Http\Middleware\DisableBladeIconComponents;
use Filament\Http\Middleware\DispatchServingFilamentEvent;
use Filament\Pages;
use Filament\Panel;
use Filament\PanelProvider;
use Filament\Widgets;
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
use Illuminate\Cookie\Middleware\EncryptCookies;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
use Illuminate\Routing\Middleware\SubstituteBindings;
use Illuminate\Session\Middleware\AuthenticateSession;
use Illuminate\Session\Middleware\StartSession;
use Illuminate\View\Middleware\ShareErrorsFromSession;
class AdminPanelProvider extends PanelProvider
{
public function panel(Panel $panel): Panel
{
return $panel
->id('admin') // 👈 Panel ID (must match landlord_panel_id in config)
->path('admin') // 👈 Panel path (e.g., https://app.example.com/admin)
->login()
->colors([
'primary' => \Filament\Support\Colors\Color::Blue,
])
->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources')
->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages')
->pages([
Pages\Dashboard::class,
])
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets')
->widgets([
Widgets\AccountWidget::class,
])
->middleware([
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartSession::class,
AuthenticateSession::class,
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
SubstituteBindings::class,
DisableBladeIconComponents::class,
DispatchServingFilamentEvent::class,
])
->authMiddleware([
Authenticate::class,
])
->plugin(TenancyLandlordPlugin::make()); // 👈 Landlord plugin (IMPORTANT)
}
}

Key Points:

  • The id('admin') must match landlord_panel_id in config/filament-tenancy.php (default is admin)
  • The plugin TenancyLandlordPlugin::make() is added at the end with ->plugin()
  • This panel will only be accessible from central domains (e.g., app.example.com/admin)

Create the file app/Providers/Filament/TenantPanelProvider.php:

<?php
namespace App\Providers\Filament;
use AngelitoSystems\FilamentTenancy\FilamentPlugins\TenancyTenantPlugin;
use Filament\Http\Middleware\Authenticate;
use Filament\Http\Middleware\DisableBladeIconComponents;
use Filament\Http\Middleware\DispatchServingFilamentEvent;
use Filament\Pages;
use Filament\Panel;
use Filament\PanelProvider;
use Filament\Widgets;
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
use Illuminate\Cookie\Middleware\EncryptCookies;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
use Illuminate\Routing\Middleware\SubstituteBindings;
use Illuminate\Session\Middleware\AuthenticateSession;
use Illuminate\Session\Middleware\StartSession;
use Illuminate\View\Middleware\ShareErrorsFromSession;
class TenantPanelProvider extends PanelProvider
{
public function panel(Panel $panel): Panel
{
return $panel
->id('tenant') // 👈 Panel ID (must match tenant_panel_id in config)
->path('admin') // 👈 Panel path (e.g., https://tenant1.example.com/admin)
->login()
->colors([
'primary' => \Filament\Support\Colors\Color::Green,
])
->discoverResources(in: app_path('Filament/Tenant/Resources'), for: 'App\\Filament\\Tenant\\Resources')
->discoverPages(in: app_path('Filament/Tenant/Pages'), for: 'App\\Filament\\Tenant\\Pages')
->pages([
Pages\Dashboard::class,
])
->discoverWidgets(in: app_path('Filament/Tenant/Widgets'), for: 'App\\Filament\\Tenant\\Widgets')
->widgets([
Widgets\AccountWidget::class,
])
->middleware([
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartSession::class,
AuthenticateSession::class,
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
SubstituteBindings::class,
DisableBladeIconComponents::class,
DispatchServingFilamentEvent::class,
])
->authMiddleware([
Authenticate::class,
])
->plugin(TenancyTenantPlugin::make()); // 👈 Tenant plugin (IMPORTANT)
}
}

Key Points:

  • The id('tenant') must match tenant_panel_id in config/filament-tenancy.php (default is tenant)
  • The plugin TenancyTenantPlugin::make() is added at the end with ->plugin()
  • This panel will only be accessible from tenant domains (e.g., tenant1.example.com/admin)
  • Note the discovery paths: Filament/Tenant/Resources to keep resources separated

The landlord plugin is used for the central administration panel where all tenants are managed.

  • ✅ Access only from central domains without active tenant
  • PreventTenantAccess middleware to block access from tenant context
  • ✅ Automatic connection to central database (landlord)
  • ✅ Automatic registration of TenantResource to manage tenants

The tenant plugin is used for individual tenant panels.

  • ✅ Access only when there’s an active and resolved tenant
  • PreventLandlordAccess middleware to block access without tenant
  • ✅ Automatic connection to tenant database
  • ✅ Dynamic branding based on tenant name

Error: “Access denied: Admin panel cannot be accessed from tenant context”

Section titled “Error: “Access denied: Admin panel cannot be accessed from tenant context””

Cause: You’re trying to access the admin panel from a tenant domain.

Solution:

  • Access the admin panel from a central domain (e.g., app.example.com/admin)
  • Or configure the tenant panel if you want to access from the tenant domain

Error: “Access denied: Tenant panel requires an active tenant context”

Section titled “Error: “Access denied: Tenant panel requires an active tenant context””

Cause: You’re trying to access the tenant panel from a central domain without tenant.

Solution:

  • Access the tenant panel from a tenant domain (e.g., tenant1.example.com/admin)
  • Make sure the tenant exists and is active in the database
  1. Resource Separation: Keep landlord and tenant resources in separate directories:

    • app/Filament/Resources/ → Admin panel resources
    • app/Filament/Tenant/Resources/ → Tenant panel resources
  2. Models with Traits: Use the correct traits in your models:

    • BelongsToTenant for tenant models
    • UsesLandlordConnection for central models
  3. Domain Configuration: Configure central domains correctly:

    'central_domains' => [
    'app.example.com',
    'admin.example.com',
    env('APP_DOMAIN', 'localhost'),
    ],