zeeshantariq/filament-sticky-columns

Sticky / frozen table columns for Filament v3, v4, and v5

Maintainers

Package info

github.com/zeeshantariq08/filament-sticky-columns

pkg:composer/zeeshantariq/filament-sticky-columns

Transparency log

Statistics

Installs: 3 945

Dependents: 0

Suggesters: 0

Stars: 15

Open Issues: 1

v1.2.0 2026-08-11 06:17 UTC

README

Latest Version on Packagist Total Downloads License Plumb score Scanned by Plumb

Sticky (frozen) table columns for Filament v3, v4, and v5.

Pin one or more columns to the left or right edge while the rest of the table scrolls horizontally β€” just like Excel or Google Sheets.

New in v1.2.0 β€” Stickyable columns (Filament v4 & v5)

Let end users choose which columns stay pinned, from a toolbar bookmark dropdown next to search:

  • Mark columns with ->stickyable()
  • Enable the manager with $table->stickyableColumns()
  • Add InteractsWithStickyableColumns on the List page
  • Selections persist in the session; Reset clears them
  • Forced ->sticky() columns stay locked (not listed in the dropdown)
  • No-op on Filament v3

See Stickyable columns below.

Features

  • πŸ“Œ Stick columns to the left or right
  • πŸ”– Stickyable columns (Filament v4 & v5) β€” let users pin columns from a toolbar dropdown
  • πŸŒ— Dark-mode aware β€” inherits Filament panel surface colours automatically
  • πŸ”’ Multiple sticky columns with auto-computed offsets
  • 🌊 Scroll shadow indicator so users know content is scrolled beneath
  • ⚑️ Works with Livewire v3 and v4 (Filament v3–v5)
  • 🧩 Use StickyColumn drop-in, or add stickiness to any column using ->sticky()
  • πŸ”§ Zero config required; publish only when you need to override defaults

Requirements

Dependency Version
PHP β‰₯ 8.1
Laravel β‰₯ 10
Filament ^3.0 || ^4.0 || ^5.0

Installation

Install from Packagist

composer require zeeshantariq/filament-sticky-columns

Recommended (Vite) β€” no filament:assets, no hard refresh

If your Filament panel uses a Vite theme (->viteTheme(...)), import the package CSS/JS into your theme build. This gives you automatic cache-busting (hashed filenames) and avoids running php artisan filament:assets.

Add to your Filament theme CSS (example: resources/css/filament/admin/theme.css):

@import "../../../../vendor/zeeshantariq/filament-sticky-columns/resources/css/filament-sticky-columns.css";

Import the JS in a file that is loaded by your panel (example: a Filament theme JS entry, or your panel JS bundle):

import '../../../../vendor/zeeshantariq/filament-sticky-columns/resources/js/filament-sticky-columns.js'

Then rebuild your assets:

npm run dev
# or
npm run build

Publish config

php artisan vendor:publish --tag="filament-sticky-columns-config"

Publish assets (Filament assets pipeline)

This package also registers JS/CSS via Filament assets.

Filament v3/v4 publish all registered assets with this command:

php artisan filament:assets

Cache busting (no hard refresh): asset filenames include your installed package version and the built files’ last-modified time, so after composer update or a fresh install, run php artisan filament:assets (or rely on filament:upgrade in your app’s Composer scripts) and do a normal page reload β€” the browser should request new CSS/JS URLs automatically.

Usage

Stickyable columns (Filament v4 & v5)

Give users a bookmark control in the table toolbar (next to search) to pin/unpin columns themselves.

1. Add the trait on the List page:

use ZeeshanTariq\FilamentStickyColumns\Concerns\InteractsWithStickyableColumns;

class ListOrders extends ListRecords
{
    use InteractsWithStickyableColumns;
}

2. Enable the manager and mark columns:

public function table(Table $table): Table
{
    return $table
        ->stickyableColumns()
        ->columns([
            TextColumn::make('code')->sticky(),          // always sticky (forced)
            TextColumn::make('name')->stickyable(),      // user can toggle
            TextColumn::make('email')->stickyable(),
            TextColumn::make('notes'),                   // never sticky
        ]);
}
API Role
$table->stickyableColumns() Shows the toolbar sticky manager
->stickyable() / ->stickyable(side: 'right') Column appears in the dropdown
->sticky() / ->stickyRight() Always sticky; not listed as toggleable
InteractsWithStickyableColumns Required on the List page for state + session

Hidden / toggleable-hidden columns are excluded from the dropdown automatically.

Option A β€” StickyColumn drop-in

StickyColumn is a drop-in replacement for Filament’s TextColumn. It is sticky left by default.

use Filament\Tables\Table;
use Filament\Tables\Columns\TextColumn;
use ZeeshanTariq\FilamentStickyColumns\Columns\StickyColumn;

public function table(Table $table): Table
{
    return $table->columns([

        // Sticky LEFT (default)
        StickyColumn::make('id')->sortable(),
        StickyColumn::make('name')->searchable(),

        // Regular scrollable columns
        TextColumn::make('email'),
        TextColumn::make('phone'),
        TextColumn::make('department'),

        // Sticky RIGHT
        StickyColumn::make('status')->right()->badge(),

    ]);
}

Option B β€” Sticky any column with ->sticky() / ->stickyRight()

This package registers macros on Filament\Tables\Columns\Column, so you can pin any column type without switching to StickyColumn.

use Filament\Tables\Table;
use Filament\Tables\Columns\IconColumn;
use Filament\Tables\Columns\ImageColumn;
use Filament\Tables\Columns\TextColumn;

public function table(Table $table): Table
{
    return $table->columns([
        TextColumn::make('id')->label('ID')->sticky(),
        ImageColumn::make('avatar')->circular()->sticky(),
        TextColumn::make('name')->searchable()->sticky(),

        TextColumn::make('email'),
        TextColumn::make('phone'),

        IconColumn::make('active')->boolean()->stickyRight(),
    ]);
}

Option C β€” Manual offsets

When auto-compute isn't accurate (e.g. beside a checkbox column):

StickyColumn::make('id')->sticky(offset: 0),    // starts at 0
StickyColumn::make('name')->sticky(offset: 60), // after a 60 px ID column

API reference

StickyColumn

StickyColumn::make('name')                 // sticky left
StickyColumn::make('name')->right()        // sticky right
StickyColumn::make('name')->sticky()       // sticky left (explicit)
StickyColumn::make('name')->stickyRight()  // sticky right
StickyColumn::make('name')->sticky(offset: 120)

Column macros

TextColumn::make('name')->sticky(condition: true, offset: null, zIndex: null)
TextColumn::make('actions')->stickyRight(condition: true, offset: null, zIndex: null)

// Filament v4 & v5 β€” user can toggle sticky from the toolbar
TextColumn::make('name')->stickyable()
TextColumn::make('actions')->stickyable(side: 'right')

$table->stickyableColumns()

Use InteractsWithStickyableColumns on the Livewire List page when enabling stickyableColumns().

Configuration

// config/filament-sticky-columns.php
return [
    'z_index'      => 10,                        // z-index for sticky cells
    'background'   => 'auto',                    // 'auto' = Filament surface colour
    'shadow'       => true,                      // directional scroll shadow
    'shadow_color' => 'rgba(0, 0, 0, 0.12)',     // shadow CSS colour
];

Manual refresh

If you dynamically add columns or swap table content outside of normal Livewire updates:

window.FilamentStickyColumns.refresh();

Troubleshooting

Sticky columns not applying

Checklist:

  • Make sure the assets are published:
php artisan filament:assets
  • Filament v4 note (important):

    • Filament v4 renders table cells using extraHeaderAttributes() (<th>) and extraCellAttributes() (<td>).
    • extraAttributes() is applied to inner column markup (e.g. TextColumn’s wrapper), which is not always enough for sticky positioning.
    • When debugging, ensure you can see data-sticky="left" / data-sticky="right" on the actual <th> / <td> elements in your table (not only on a nested <div>).
  • If you use the Filament assets pipeline, refresh once after publishing.

  • If you use Vite (recommended), you should not need hard refresh because Vite cache-busts.

  • Inspect the table DOM and confirm your sticky columns have data-sticky="left" / data-sticky="right" somewhere inside the cell.

Offsets look wrong

If you use selection/checkbox columns or very dynamic columns, auto offsets can be off. Use manual offsets:

TextColumn::make('id')->sticky(offset: 0)
TextColumn::make('name')->sticky(offset: 80)

Changelog

See CHANGELOG.md.

License

MIT β€” see LICENSE.md.