Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filament Grid List Laravel Package

wezlo/filament-grid-list

Swap Filament resource tables for responsive grid/card lists without rewriting your resources. Define cards via sections, closures, or Blade views while keeping search, filters, sorting, pagination, bulk actions, and record actions working out of the box.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the package:
    composer require wezlo/filament-grid-list
    
  2. Add the trait to your ListRecords page:
    use Wezlo\FilamentGridList\Concerns\HasGridList;
    
    class ListProducts extends ListRecords
    {
        use HasGridList;
        // ...
    }
    
  3. Define gridList() in your page:
    public function gridList(GridListConfiguration $config): GridListConfiguration
    {
        return $config
            ->header(fn ($record) => $record->name)
            ->content(fn ($record) => $record->description);
    }
    
  4. Ensure Tailwind detects views (add to resources/css/filament/admin/theme.css):
    @source '../../../../vendor/wezlo/filament-grid-list/resources/views/**/*';
    
    Then rebuild assets (npm run build).

First Use Case

Replace a table view for a ProductResource with a grid of product cards showing:

  • Thumbnail image
  • Product name (header)
  • Short description (content)
  • Price (footer)
public function gridList(GridListConfiguration $config): GridListConfiguration
{
    return $config
        ->image(fn ($record) => $record->thumbnail_url)
        ->header(fn ($record) => $record->name)
        ->content(fn ($record) => Str::limit($record->description, 100))
        ->footer(fn ($record) => '$' . number_format($record->price, 2));
}

Implementation Patterns

1. Structured Sections Workflow

Use structured sections for reusable, maintainable card layouts:

public function gridList(GridListConfiguration $config): GridListConfiguration
{
    return $config
        ->image(fn ($record) => $record->image_url)
        ->header(fn ($record) => $record->title)
        ->badges(fn ($record) => [
            ['label' => $record->status->value, 'color' => $record->status->color],
        ])
        ->content(fn ($record) => view('components.product-summary', ['product' => $record]))
        ->footer(fn ($record) => $record->created_at->diffForHumans());
}

2. Closure-Based Customization

For dynamic HTML, use describeUsing():

$config->describeUsing(fn ($record) => new HtmlString(<<<HTML
    <div class="p-4">
        <h3 class="font-bold">{$record->name}</h3>
        <div class="flex items-center gap-2 mt-2">
            <span class="text-sm text-gray-500">{$record->category}</span>
            <span class="badge badge-{$record->priority}">{$record->priority}</span>
        </div>
    </div>
HTML));

3. Custom Blade Views

For complex layouts, create a dedicated Blade view (resources/views/products/grid-card.blade.php):

<div class="p-4">
    <img src="{{ $record->image_url }}" class="w-full h-32 object-cover rounded">
    <h3 class="mt-2 font-semibold">{{ $record->name }}</h3>
    <p class="text-sm text-gray-600">{{ Str::limit($record->description, 80) }}</p>
    <div class="mt-3 flex justify-between items-center">
        <span class="font-bold">${{ number_format($record->price) }}</span>
        <x-filament::badge color="info">{{ $record->stock }} in stock</x-filament::badge>
    </div>
</div>

Then configure:

$config->cardView('products.grid-card');

4. Responsive Grid Configuration

Adjust columns per breakpoint:

$config->gridColumns([
    'default' => 1,   // Mobile: 1 column
    'sm' => 2,        // Small: 2 columns
    'md' => 3,        // Medium: 3 columns
    'lg' => 4,        // Large: 4 columns
    'xl' => 6,        // Extra large: 6 columns
]);

5. Bulk Actions Integration

Ensure bulk actions work by:

  • Keeping the table() method intact (filters, search, bulk actions are reused).
  • Explicitly enabling selection:
    $config->selectable(); // Enable bulk selection
    

6. Pagination Control

Override default pagination:

$config->recordsPerPage(16)
       ->recordsPerPageOptions([8, 16, 32, 64]);

7. Plugin-Level Defaults

Set defaults for all grid lists in a panel:

FilamentGridListPlugin::make()
    ->gridColumns(['default' => 1, 'sm' => 2, 'md' => 3])
    ->gap(4)
    ->recordsPerPage(12)
    ->recordsPerPageOptions([12, 24, 48]);

8. Dark Mode Support

The package includes dark mode classes (dark:*). Ensure your custom Blade views use:

<img src="{{ $record->image_url }}" class="dark:brightness-50">

Gotchas and Tips

Pitfalls

  1. Missing Tailwind Classes

    • Issue: Cards render with broken styling (e.g., gaps, padding).
    • Fix: Add @source to your Filament theme CSS and rebuild assets (npm run build).
    • Debug: Check browser dev tools for missing Tailwind classes (e.g., gap-4 not applied).
  2. Bulk Actions Not Working

    • Issue: Bulk actions (e.g., delete, export) fail or don’t trigger.
    • Fix: Ensure:
      • The table() method defines bulk actions (e.g., ->actions([...])).
      • selectable() is called in gridList().
      • The table() method is not overridden to return null or an empty table.
  3. Pagination Conflicts

    • Issue: Pagination options or defaults don’t apply.
    • Fix: Pagination config in gridList() overrides the table() method. If using both, prioritize gridList():
      $config->recordsPerPage(20); // Overrides table() defaults
      
  4. Custom Blade Views Not Found

    • Issue: cardView() throws View [products.grid-card] not found.
    • Fix: Ensure the view exists in resources/views/ and the namespace is correct.
  5. Performance with Large Datasets

    • Issue: Slow rendering or layout shifts with many records.
    • Fix:
      • Avoid 'all' in recordsPerPageOptions() (default excludes it).
      • Use content-visibility: auto (enabled by default) for offscreen cards.
      • Lazy-load images:
        ->image(fn ($record) => $record->image_url, ['loading' => 'lazy'])
        
  6. Record URL Overrides

    • Issue: Cards don’t link to the correct URL.
    • Fix: Override recordUrl() if needed:
      $config->recordUrl(fn ($record) => route('custom.route', $record));
      
  7. Dark Mode Styling Issues

    • Issue: Custom Blade views look broken in dark mode.
    • Fix: Use dark: variants for colors/backgrounds:
      <div class="bg-white dark:bg-gray-800 p-4">
      

Debugging Tips

  1. Inspect the Grid Configuration Use dd($config) in gridList() to verify settings before rendering.

  2. Check Livewire State For bulk selection issues, inspect the filamentTable Alpine component in browser dev tools:

    • Look for toggleSelectedRecord() and isRecordSelected() methods.
  3. Verify Table Infrastructure Ensure getTableRecords(), getTableColumns(), and getTableFilters() are not overridden in a way that breaks the grid.

  4. Tailwind Debugging Add !important temporarily to test if a style is being overridden:

    .fi-grid-list-card { gap: 1rem !important; }
    

Extension Points

  1. Custom Card Components Extend the grid by creating reusable components for sections (e.g., ProductCardHeader, ProductCardFooter) and include them in describeUsing().

  2. Dynamic Grid Columns Use JavaScript to adjust columns based on user preference:

    document.addEventListener('filament-grid-ready', () => {
        const grid = document.querySelector('.fi-grid-list-content');
        grid.style.gridTemplateColumns = 'repeat(auto-fit, minmax(250px, 1fr))';
    });
    
  3. Conditional Sections Show/hide sections based on record

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor
spatie/laravel-javascript-views