Filament v3 Custom Table Columns Deep Dive | Mohamed Said        [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://msaied.com) [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles  ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

       [  ](https://github.com/EG-Mohamed)       

 [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

  [ home ](https://msaied.com)    [ articles ](https://msaied.com/articles)    Filament v3 Custom Table Columns: Rendering, State, and Performance at Scale        On this page       1. [  Why Custom Columns Deserve More Attention ](#why-custom-columns-deserve-more-attention)
2. [  Anatomy of a Custom Column ](#anatomy-of-a-custom-column)
3. [  Declaring Eager Load Relationships ](#declaring-eager-load-relationships)
4. [  Fluent Configuration Methods ](#fluent-configuration-methods)
5. [  Keeping Views Fast ](#keeping-views-fast)
6. [  Registering the Column for Auto-Discovery ](#registering-the-column-for-auto-discovery)
7. [  Takeaways ](#takeaways)

  ![Filament v3 Custom Table Columns: Rendering, State, and Performance at Scale](https://cdn.msaied.com/406/145404572d600dbdab49a13226b0537d.png)

  #filament   #laravel   #tables   #performance  

 Filament v3 Custom Table Columns: Rendering, State, and Performance at Scale 
==============================================================================

     10 Jul 2026      4 min read    ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said  

       Table of contents

1. [  01   Why Custom Columns Deserve More Attention  ](#why-custom-columns-deserve-more-attention)
2. [  02   Anatomy of a Custom Column  ](#anatomy-of-a-custom-column)
3. [  03   Declaring Eager Load Relationships  ](#declaring-eager-load-relationships)
4. [  04   Fluent Configuration Methods  ](#fluent-configuration-methods)
5. [  05   Keeping Views Fast  ](#keeping-views-fast)
6. [  06   Registering the Column for Auto-Discovery  ](#registering-the-column-for-auto-discovery)
7. [  07   Takeaways  ](#takeaways)

 Why Custom Columns Deserve More Attention
-----------------------------------------

Filament's built-in columns cover 80% of cases, but the remaining 20% — composite data, computed badges, relationship-derived icons — quickly becomes a mess of `->html()` hacks and anonymous closures scattered across your resource. A proper custom column class gives you a reusable, testable, IDE-friendly primitive.

This article focuses on the mechanics that matter in production: state resolution, eager loading declarations, and keeping Blade views lean.

---

Anatomy of a Custom Column
--------------------------

Every custom column extends `Filament\Tables\Columns\Column`. The minimum surface you need to understand:

- `getState()` — resolves the column's value from the record
- `getExtraAttributes()` — merges HTML attributes onto the cell
- The Blade view referenced by `$view`

```php
namespace App\Filament\Tables\Columns;

use Filament\Tables\Columns\Column;

class SubscriptionStatusColumn extends Column
{
    protected string $view = 'filament.tables.columns.subscription-status';

    public function getState(): mixed
    {
        $record = $this->getRecord();

        return [
            'label' => $record->subscription?->plan->name ?? 'Free',
            'active' => $record->subscription?->isActive() ?? false,
            'trial' => $record->subscription?->onTrial() ?? false,
        ];
    }
}

```

The Blade view receives `$getState` as a closure:

```blade
@php
    $state = $getState();
@endphp

 $state['active'] && !$state['trial'],
    'bg-yellow-100 text-yellow-800' => $state['trial'],
    'bg-gray-100 text-gray-500' => !$state['active'],
])>
    {{ $state['label'] }}

```

---

Declaring Eager Load Relationships
----------------------------------

This is where most custom column implementations fall apart. If your `getState()` touches a relationship, every row triggers a lazy load. Filament provides `->relationship()` on built-in columns, but for custom columns you must override `getRelationships()`:

```php
public function getRelationships(): array
{
    return ['subscription', 'subscription.plan'];
}

```

Filament's table builder calls `getRelationships()` on every column and merges the results into a single `with()` call before executing the query. Declare nested dot-notation paths exactly as you would in Eloquent.

If your column conditionally touches different relationships based on a configuration closure, resolve the closure inside `getRelationships()` before returning:

```php
public function getRelationships(): array
{
    $extra = value($this->extraRelationship);

    return array_filter([
        'subscription',
        'subscription.plan',
        $extra,
    ]);
}

```

---

Fluent Configuration Methods
----------------------------

Custom columns should feel native. Add fluent setters using the `Macroable`-style pattern Filament itself uses — store values in `$this->evaluate()`-compatible closures so they support both static values and record-aware closures:

```php
protected bool | Closure $showTrialBadge = true;

public function showTrialBadge(bool | Closure $show = true): static
{
    $this->showTrialBadge = $show;

    return $this;
}

public function isShowingTrialBadge(): bool
{
    return $this->evaluate($this->showTrialBadge);
}

```

Passing `$this->evaluate()` a closure automatically injects the current record, so callers can write:

```php
SubscriptionStatusColumn::make('subscription_status')
    ->showTrialBadge(fn ($record) => $record->created_at->isAfter(now()->subDays(30)))

```

---

Keeping Views Fast
------------------

Blame slow tables on views that call PHP methods per cell. Rules:

1. **Resolve once** — call `$getState()` once at the top of the view and destructure.
2. **No Eloquent in views** — all relationship data must come through `getState()`.
3. **Avoid `@livewire` inside column views** — each cell is already inside a Livewire component; nesting adds wire overhead.
4. **Cache computed values in `getState()`** if the column is used in sortable or searchable contexts where it may be called multiple times per request.

---

Registering the Column for Auto-Discovery
-----------------------------------------

If you ship this inside a package or a shared module, register it in a service provider so teams can use it without imports:

```php
use Filament\Support\Facades\FilamentAsset;

public function boot(): void
{
    FilamentAsset::register([
        // register any JS/CSS assets here if your column needs them
    ]);
}

```

For in-app columns, a simple `use` statement is sufficient — no registration needed.

---

Takeaways
---------

- Override `getRelationships()` to declare eager loads; skipping this causes N+1 at the column level.
- Store configurable options as `bool | Closure` and resolve via `$this->evaluate()` for record-aware flexibility.
- Keep Blade views dumb: resolve all state in `getState()`, destructure once at the top of the view.
- Fluent setters make custom columns feel native and keep resource files readable.
- Test `getState()` directly by instantiating the column, setting a mock record, and asserting the returned array.

 Found this useful?

          [  ](https://twitter.com/intent/tweet?url=https%3A%2F%2Fmsaied.com%2Farticles%2Ffilament-v3-custom-table-columns-rendering-state-and-performance-at-scale&text=Filament+v3+Custom+Table+Columns%3A+Rendering%2C+State%2C+and+Performance+at+Scale) [  ](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fmsaied.com%2Farticles%2Ffilament-v3-custom-table-columns-rendering-state-and-performance-at-scale) 

 Frequently Asked Questions 
----------------------------

  3 questions  

     Q01  Why does my custom column still trigger N+1 queries even after overriding getRelationships()?        Check that the relationship names returned exactly match the Eloquent relation method names on your model, including nested dot-notation paths. A typo silently skips the eager load. Also verify you are not calling additional relationships inside the Blade view itself. 

      Q02  Can I make a custom column sortable or searchable?        Yes. Call -&gt;sortable() or -&gt;searchable() as usual, but provide a custom sort or search query callback when the column state is computed rather than a direct database column: -&gt;sortable(query: fn ($query, $direction) =&gt; $query-&gt;orderBy('subscriptions.status', $direction)). 

      Q03  How do I write a Pest test for a custom column's getState() output?        Instantiate the column with ::make('name'), call -&gt;record($model) to inject a model, then assert the return value of getState(). No Livewire test harness is needed for pure state logic — only bring in livewire() helpers when testing the rendered table interaction. 

  Continue reading

 More Articles 
---------------

 [ View all    ](https://msaied.com/articles) 

 [ ![Laravel Enum Casts, Backed Enums, and Value Semantics in PHP 8.3](https://cdn.msaied.com/408/3534df365ffb2c8f6c430180bfaba8f1.png) laravel php8.3 enums 

### Laravel Enum Casts, Backed Enums, and Value Semantics in PHP 8.3

PHP 8.3 backed enums combined with Laravel's native enum casting unlock type-safe domain models. Learn how to...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 10 Jul 2026     1 min read  

  Read    

 ](https://msaied.com/articles/laravel-enum-casts-backed-enums-and-value-semantics-in-php-83) [ ![Laravel Queues: Reliable Job Retry Strategies with Exponential Backoff and Dead-Letter Handling](https://cdn.msaied.com/407/704b143d40e1a6ac5a2faa4efb900174.png) laravel queues reliability 

### Laravel Queues: Reliable Job Retry Strategies with Exponential Backoff and Dead-Letter Handling

Beyond basic retries: how to implement exponential backoff, custom retry delays, and dead-letter queue pattern...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 10 Jul 2026     4 min read  

  Read    

 ](https://msaied.com/articles/laravel-queues-reliable-job-retry-strategies-with-exponential-backoff-and-dead-letter-handling) [ ![Passwordless Sign-In with Fortify Two-Factor Support in Laravel](https://cdn.msaied.com/409/dfd6ae4c1a91c60ca1d81cd84452bbc1.png) passwordless authentication magic-link 

### Passwordless Sign-In with Fortify Two-Factor Support in Laravel

Email Magic Link for Laravel adds passwordless authentication via emailed links or one-time codes, scanner-saf...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 10 Jul 2026     3 min read  

  Read    

 ](https://msaied.com/articles/passwordless-sign-in-with-fortify-two-factor-support-in-laravel) 

   [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://msaied.com)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

Explore

- [Home](https://msaied.com)
- [Projects](https://msaied.com/projects)
- [Articles](https://msaied.com/articles)
- [Certificates](https://msaied.com/certificates)
- [Contact](https://msaied.com#contact-section)

Connect

- [   hello@msaied.com ](mailto:hello@msaied.com)
- [   +20 109 461 9204 ](tel:+201094619204)

© 2026 Mohamed Said. All rights reserved.

 [  ](https://github.com/EG-Mohamed) [  ](https://www.linkedin.com/in/msaiedm/) [  ](https://wa.me/201094619204) [  ](mailto:hello@msaied.com) [  ](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
