Control Filament v3 Admin Columns and Actions with Policies
Use conditional table columns and Laravel policies to hide sensitive data and restrict actions in Filament v3 admin panels.
03 Oct 2025, 04:33 UTC

The problem: auto-generated CRUD shows too much
You run php artisan make:filament-resource Post --generate and your admin panel instantly gains list, create, edit, and view pages for the posts table. That scaffolding is useful, but it also exposes every column to every administrator. A junior staff member can see and toggle is_admin, stripe_customer_id, or internal flags you never meant to display. Because Filament v3 builds these pages from a Resource class, the fix belongs in that class rather than in a controller you do not have.
Scope the columns with conditional visibility
Filament v3 Resources expose a static table() method where you define columns such as TextColumn, ToggleColumn, and BadgeColumn. Each column accepts a visible() closure, so you can hide fields based on the current user or request context.
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Columns\ToggleColumn;
use Filament\Tables\Table;
public static function table(Table $table): Table
{
return $table
->columns([
TextColumn::make('title')
->searchable()
->sortable(),
TextColumn::make('author.name')
->label('Author'),
ToggleColumn::make('is_published')
->label('Published')
->visible(fn () => auth()->user()?->can('publish posts') ?? false),
TextColumn::make('internal_note')
->visible(fn () => auth()->user()?->can('view internal notes') ?? false),
])
->filters([
// ...
])
->actions([
// ...
]);
}
auth()->user() returns the currently authenticated user under the guard configured for your Filament panel. If no user is logged in, the closure receives null, so the null-safe ?-> operator prevents an error before Filament's authentication middleware runs.
Enforce actions with Laravel policies
Column visibility controls what people see. It does not stop someone from guessing an edit URL. Filament v3 works alongside Laravel policies: create a policy for your model, register it in AuthServiceProvider, then implement authorization methods on the Resource or its page classes.
Example policy:
namespace App\Policies;
use App\Models\Post;
use App\Models\User;
class PostPolicy
{
public function view(User $user, Post $post): bool
{
return $user->can('view posts');
}
public function edit(User $user, Post $post): bool
{
return $user->can('edit posts') || ($user->id === $post->user_id && $post->is_draft);
}
}
For list and create pages, add static methods to the Resource:
public static function canView(): bool
{
return auth()->user()?->can('view posts') ?? false;
}
public static function canCreate(): bool
{
return auth()->user()?->can('create posts') ?? false;
}
For record-level actions, implement the method on the page class, for example app/Filament/Resources/PostResource/Pages/EditPost.php:
public function canEdit(Post $record): bool
{
return auth()->user()?->can('edit', $record) ?? false;
}
Filament checks these methods before rendering pages. If canEdit() returns false, the edit action disappears from the table and direct navigation to the edit page is denied.
Worked example: register and verify
- Open a terminal in your Laravel project root. You need write access to
app/Filament/Resources/andapp/Policies/. - Confirm the installed major version:
composer show filament/filament. The output should show version3.x. Filament v2 used different method signatures, so do not copy v2 snippets into a v3 project. - Generate the resource:
php artisan make:filament-resource Post --generate. This createsapp/Filament/Resources/PostResource.phpand page classes underapp/Filament/Resources/PostResource/Pages/. If a class with that name already exists, Artisan may overwrite it or abort depending on your installer version. - Create
app/Policies/PostPolicy.phpand register it inApp\Providers\AuthServiceProviderinside the$policiesarray. - Edit
PostResource.php: add conditionalvisible()closures to sensitive columns and implementcanView()andcanCreate(). - Edit
EditPost.php: implementcanEdit(Post $record)using the policy. - Register the resource in your panel provider, typically
app/Providers/Filament/AdminPanelProvider.php, insidegetResources(). - Start the local server and navigate to
/admin/posts. Confirm the list page loads, theinternal_notecolumn is absent for users without theview internal notesability, and the edit action is missing for records the policy rejects.
Limitation: visibility is not validation
Hiding a column in the table or removing an edit button improves the interface, but it does not replace server-side validation. A determined caller can still send a request to Filament's underlying Livewire endpoints. Keep business rules in form rules, model events, or a service layer. Treat the admin panel as a convenient UI, not a security boundary.
Check your work
- Run
php artisan route:listand look for Filament resource routes under your admin prefix. - Log in as a user without the relevant policy ability and confirm sensitive columns and actions are not rendered.
- Attempt to hit the edit URL directly for a disallowed record and confirm Filament returns an authorization response rather than the form.
Filament v3 gives you declarative control over what each admin sees. Combine conditional table columns with Laravel policies, verify with a real non-privileged account, and keep domain validation where it belongs: in your models and form rules.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.