PHPackages                             wezlo/filament-responsive-table - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. wezlo/filament-responsive-table

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

wezlo/filament-responsive-table
===============================

Render a Filament list table as stacked cards below a configurable Tailwind breakpoint.

1.0.0(1mo ago)048MITPHPPHP ^8.2

Since Apr 22Pushed 1mo agoCompare

[ Source](https://github.com/mustafakhaleddev/filament-responsive-table)[ Packagist](https://packagist.org/packages/wezlo/filament-responsive-table)[ RSS](/packages/wezlo-filament-responsive-table/feed)WikiDiscussions master Synced 1w ago

READMEChangelog (1)Dependencies (2)Versions (2)Used By (0)

Filament Responsive Table
=========================

[](#filament-responsive-table)

Render a Filament list table as stacked cards below a configurable Tailwind breakpoint. Above the breakpoint nothing changes — the native Filament table renders untouched. Below the breakpoint each row becomes a card with column labels, values, and the row's record actions in the footer.

The same `table()` definition drives both views — no duplicate column lists, no second source of truth.

Requirements
------------

[](#requirements)

- PHP 8.2+
- Laravel 11+ / 13
- Filament 4 or 5

Features
--------

[](#features)

- **Breakpoint-driven** — pick `sm`, `md`, `lg`, `xl`, or `2xl`; below it the table becomes cards, above it the native table renders
- **Zero duplication** — columns and record actions come straight from your existing `table()` method
- **Two-column label/value grid** inside each card, matching mobile UX expectations
- **Optional per-card title** resolved from the record
- **Optional bulk-selection checkbox** on each card so bulk actions still work on mobile
- **Optional record actions footer** — stack the row's actions at the bottom of each card
- **Column filtering** — `only()` / `except()` hide columns in cards without affecting the desktop table
- **Custom card Blade view** — opt out of the default template whenever you need it
- **Plugin-level defaults** — set a single default breakpoint for every responsive list page in a panel
- **Three-level configuration cascade** — page overrides plugin overrides config file
- **Dark mode support**

Installation
------------

[](#installation)

```
composer require wezlo/filament-responsive-table
```

Optionally register the plugin in your Panel Provider for global defaults:

```
use Wezlo\FilamentResponsiveTable\FilamentResponsiveTablePlugin;

->plugins([
    FilamentResponsiveTablePlugin::make()
        ->defaultBreakpoint('md'),
])
```

Optionally publish the config:

```
php artisan vendor:publish --tag=filament-responsive-table-config
```

### Theme Source (Tailwind v4)

[](#theme-source-tailwind-v4)

The package's Blade views use Tailwind utility classes. For Tailwind to detect them during your app's build, add the package's views as a `@source` in your Filament custom theme CSS file (usually `resources/css/filament/admin/theme.css`):

```
@import '../../../../vendor/filament/filament/resources/css/theme.css';

@source '../../../../vendor/wezlo/filament-responsive-table/resources/views/**/*';

@custom-variant dark (&:where(.dark, .dark *));
```

If you don't have a custom theme yet, create one:

```
php artisan make:filament-theme
```

Then rebuild assets:

```
npm run build
```

Quick Start
-----------

[](#quick-start)

Add the `HasResponsiveTable` trait to your resource's `ListRecords` page and declare a `$responsiveBreakpoint` property to pick the breakpoint:

```
use Filament\Resources\Pages\ListRecords;
use Wezlo\FilamentResponsiveTable\Concerns\HasResponsiveTable;

class ListUsers extends ListRecords
{
    use HasResponsiveTable;

    protected static string $resource = UserResource::class;

    public ?string $responsiveBreakpoint = 'md';
}
```

That's it. At viewports `< md` (768px) every row collapses into a card showing each column's label on the left and its rendered value on the right. Record actions stack in a footer below.

> The trait does not declare `$responsiveBreakpoint` itself (to avoid PHP trait/class property conflicts), so you declare it on your list page with whatever visibility and default you like — `public`, `protected`, `?string`, or a non-nullable `string` with a default value all work.

Configuration API
-----------------

[](#configuration-api)

For anything beyond the breakpoint shortcut, implement the `responsiveTable()` method. The configuration from the method wins over the `$responsiveBreakpoint` property.

```
use Wezlo\FilamentResponsiveTable\Concerns\HasResponsiveTable;
use Wezlo\FilamentResponsiveTable\ResponsiveTableConfiguration;

class ListUsers extends ListRecords
{
    use HasResponsiveTable;

    public function responsiveTable(ResponsiveTableConfiguration $config): ResponsiveTableConfiguration
    {
        return $config
            ->breakpoint('lg')
            ->except(['id', 'created_at'])
            ->cardTitle(fn ($record) => $record->name)
            ->showRecordActions()
            ->showBulkSelection();
    }
}
```

### Configuration Reference

[](#configuration-reference)

MethodSignatureDescription`breakpoint(string)``'sm' | 'md' | 'lg' | 'xl' | '2xl'`Below this Tailwind breakpoint, rows render as cards. Throws on unknown values.`only(array)``array`Keep only these column names in cards. Desktop table is untouched.`except(array)``array`Hide these column names from cards. Desktop table is untouched.`cardTitle(Closure)``fn (Model $record): string|Htmlable|null`Resolve a per-card header title from the record.`showRecordActions(bool)``bool` (default `true`)Render the table's record actions in each card's footer.`showBulkSelection(bool)``bool` (default `false`)Show the bulk-selection checkbox on each card.`cardView(string)``string`Override the default card Blade template.### Column Filtering

[](#column-filtering)

`only()` and `except()` match against `Column::getName()` — the first argument to `TextColumn::make('name')`, `IconColumn::make('status')`, etc. Nested relationship columns like `client.name` match by that exact name.

```
$config->only(['name', 'email', 'status']);
// or
$config->except(['id', 'created_at', 'updated_at', 'deleted_at']);
```

### Card Title

[](#card-title)

Without `cardTitle()`, the card has no header bar — it's just label/value rows and the action footer. Setting it renders a header strip with the title text (and the bulk checkbox if enabled).

```
$config->cardTitle(fn ($record) => "#{$record->invoice_number}");
$config->cardTitle(fn ($record) => new HtmlString("{$record->name}"));
```

### Custom Card View

[](#custom-card-view)

Point to your own Blade view if the default layout doesn't fit:

```
$config->cardView('users.mobile-card');
```

The view receives three variables:

VariableTypeDescription`$record``Model`The Eloquent record for this card`$columns``array`The visible card columns (after `only`/`except`)`$config``ResponsiveTableConfiguration`The resolved configurationYou can use `$this->getResponsiveTableRecordActions($record)` inside the view to get the cloned, record-bound, visibility-filtered actions array.

Plugin Configuration
--------------------

[](#plugin-configuration)

Register the plugin in your Panel Provider to set defaults for all responsive list pages in that panel:

```
use Wezlo\FilamentResponsiveTable\FilamentResponsiveTablePlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        ->plugins([
            FilamentResponsiveTablePlugin::make()
                ->defaultBreakpoint('md')
                ->defaultShowRecordActions(true)
                ->defaultShowBulkSelection(false),
        ]);
}
```

MethodTypeDefaultDescription`defaultBreakpoint(string)``string``null`Default breakpoint when no page sets one`defaultShowRecordActions(bool)``bool``null`Default visibility of the actions footer`defaultShowBulkSelection(bool)``bool``null`Default visibility of the bulk checkboxConfiguration Cascade
---------------------

[](#configuration-cascade)

Each setting resolves through a four-level cascade:

1. **`responsiveTable()` method** on the `ListRecords` page (highest priority)
2. **`$responsiveBreakpoint` property** on the `ListRecords` page (breakpoint only)
3. **Plugin defaults** on `FilamentResponsiveTablePlugin` in the Panel Provider
4. **Config file** — `config/filament-responsive-table.php` (lowest priority)

The method always wins over the property, which wins over plugin defaults, which win over the config file.

Default Config File
-------------------

[](#default-config-file)

```
// config/filament-responsive-table.php
return [
    'breakpoint' => 'md',
    'show_record_actions' => true,
    'show_bulk_selection' => false,
];
```

Full Example
------------

[](#full-example)

```
use Filament\Resources\Pages\ListRecords;
use Wezlo\FilamentResponsiveTable\Concerns\HasResponsiveTable;
use Wezlo\FilamentResponsiveTable\ResponsiveTableConfiguration;

class ListOrders extends ListRecords
{
    use HasResponsiveTable;

    protected static string $resource = OrderResource::class;

    public function responsiveTable(ResponsiveTableConfiguration $config): ResponsiveTableConfiguration
    {
        return $config
            ->breakpoint('lg')
            ->except(['id'])
            ->cardTitle(fn ($record) => "#{$record->number}")
            ->showRecordActions()
            ->showBulkSelection();
    }
}
```

The resource's `table()` method stays unchanged — columns, filters, search, header actions, record actions, and bulk actions all carry over to the card view automatically.

How It Works
------------

[](#how-it-works)

- The `HasResponsiveTable` trait overrides `content()` on the `ListRecords` page to render a single wrapper view that contains **both** the native Filament table (via `$this->getTable()->render()`) and a card stack generated from the same columns.
- The wrapper `` carries `data-breakpoint=""`. A small shipped stylesheet has static `@media` rules — at the configured breakpoint it hides the table and shows the cards, and vice-versa above it. Because the rules are static CSS (not Tailwind utilities), Tailwind's JIT scan isn't required for visibility toggling.
- Each card pulls its columns from `$this->getTable()->getVisibleColumns()`, then applies `only`/`except`. Columns are cloned per record (`$column->getClone()->record($record)`) so the same render pipeline used by the desktop table — badges, icons, date formatting, images — produces the card values.
- Record actions are cloned per record (`$action->getClone()->record($record)`) and filtered by `isHidden()`, mirroring the pattern in Filament's own table Blade view.
- The desktop table is Filament's native `Table::render()` output — search, filters, sorting, pagination, bulk actions, and row actions all work exactly as before.

CSS Classes
-----------

[](#css-classes)

All elements use `fi-responsive-table-*` prefixed classes for targeted styling:

ClassElement`fi-responsive-table`Root wrapper (carries `data-breakpoint`)`fi-responsive-table-desktop`Wraps Filament's native table`fi-responsive-table-cards`Wraps the card stack`fi-responsive-table-cards-list`Inner flex container for cards`fi-responsive-table-card`Individual card`fi-responsive-table-card-header`Card title + optional checkbox bar`fi-responsive-table-card-title`Title text`fi-responsive-table-card-checkbox`Bulk-selection checkbox`fi-responsive-table-card-body``` grid of label/value pairs`fi-responsive-table-card-field`One label/value pair (uses `display: contents`)`fi-responsive-table-card-field-label`Column label (``)`fi-responsive-table-card-field-value`Rendered column value (``)`fi-responsive-table-card-footer`Record-actions footerOverride any of these in your theme CSS to customize the card appearance.

License
-------

[](#license)

MIT

###  Health Score

40

—

FairBetter than 86% of packages

Maintenance90

Actively maintained with recent releases

Popularity10

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity46

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

49d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/25182746?v=4)[Mustafa Khaled](/maintainers/mustafakhaleddev)[@mustafakhaleddev](https://github.com/mustafakhaleddev)

---

Top Contributors

[![mustafakhaleddev](https://avatars.githubusercontent.com/u/25182746?v=4)](https://github.com/mustafakhaleddev "mustafakhaleddev (1 commits)")

---

Tags

laravelmobileresponsivetablefilamentcards

### Embed Badge

![Health badge](/badges/wezlo-filament-responsive-table/health.svg)

```
[![Health](https://phpackages.com/badges/wezlo-filament-responsive-table/health.svg)](https://phpackages.com/packages/wezlo-filament-responsive-table)
```

###  Alternatives

[ysfkaya/filament-phone-input

A phone input component for Laravel Filament

3131.2M25](/packages/ysfkaya-filament-phone-input)[rawilk/profile-filament-plugin

Profile &amp; MFA starter kit for filament.

3913.7k](/packages/rawilk-profile-filament-plugin)[dotswan/filament-map-picker

Easily pick and retrieve geo-coordinates using a map-based interface in your Filament applications.

127173.7k3](/packages/dotswan-filament-map-picker)[stephenjude/filament-feature-flags

Filament implementation of feature flags and segmentation with Laravel Pennant.

122157.7k1](/packages/stephenjude-filament-feature-flags)[creagia/filament-code-field

A Filamentphp input field to edit or view code data.

57301.3k3](/packages/creagia-filament-code-field)[jibaymcs/filament-tour

Bring the power of DriverJs to your Filament panels and start a tour !

12351.0k](/packages/jibaymcs-filament-tour)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
