chengkangzai/filament-shot

Render Filament v4/v5 UI components (Forms, Tables, Infolists, Stats Widgets) as PNG images programmatically.

Maintainers

Package info

github.com/chengkangzai/filament-shot

Language:HTML

pkg:composer/chengkangzai/filament-shot

Fund package maintenance!

CCK

Statistics

Installs: 182

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 7

v0.8.0 2026-03-21 19:09 UTC

This package is auto-updated.

Last update: 2026-03-21 20:34:37 UTC


README

Latest Version on Packagist GitHub Tests Action Status Total Downloads

Render Filament v4/v5 UI components — Forms, Tables, Infolists, and Stats Widgets — as PNG screenshots programmatically. Define your components using familiar Filament classes and get pixel-perfect images without spinning up a browser manually.

Examples

These screenshots are generated by CI using the actual rendering pipeline to verify quality.

Forms

Light Dark
Form Light Form Dark
Section Layout Grid Layout
Form Section Form Grid
Additional Field Types
Form Fields
Tabs Wizard
Form Tabs Form Wizard
Color Picker
Form Color Picker

Tables

Basic With Badges
Table Basic Table Badges
Styled (Weight + Mono Font) Dark Mode
Table Styled Table Dark
Icon Column (Boolean) Bulk Actions
Table Icon Column Table Bulk Actions
Reorderable Action Group
Table Reorderable Table Action Group

Infolist

Light Dark
Infolist Infolist Dark

Navigation

Sidebar
Navigation

Stats Widgets

Light Dark
Stats Stats Dark

Notifications

Success
Notification

How It Works

Filament Shot generates standalone HTML using its own Blade templates styled with Filament's CSS classes, then captures screenshots via Browsershot. This avoids Livewire context issues entirely — no running application or panel required.

Requirements

  • PHP 8.2+
  • Laravel 11+
  • Filament v4 or v5
  • Node.js 18+ and Puppeteer
  • A Chromium-based browser (Chrome, Chromium, etc.)

Installation

Install the package via Composer:

composer require chengkangzai/filament-shot

Install Puppeteer (required by Browsershot):

npm install puppeteer

Publish the config file (optional):

php artisan vendor:publish --tag="filament-shot-config"

Usage

Forms

Capture Filament form components as an image:

use CCK\FilamentShot\FilamentShot;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Toggle;

FilamentShot::form([
    TextInput::make('name')
        ->label('Full Name')
        ->placeholder('Enter your name'),
    TextInput::make('email')
        ->label('Email Address')
        ->placeholder('you@example.com'),
    Select::make('role')
        ->label('Role')
        ->options([
            'admin' => 'Administrator',
            'editor' => 'Editor',
            'viewer' => 'Viewer',
        ]),
    Toggle::make('active')
        ->label('Active'),
])
->state(['name' => 'Jane Doe', 'email' => 'jane@example.com'])
->save('form.png');

Supported field types: TextInput, Select, Textarea, Toggle, Checkbox, Radio, Placeholder, DatePicker, DateTimePicker, FileUpload, ColorPicker, TagsInput, KeyValue, RichEditor, MarkdownEditor, Repeater.

Layout components: Section, Grid, Fieldset, Tabs, Wizard.

Forms with Tabs

use CCK\FilamentShot\FilamentShot;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Schemas\Components\Tabs;
use Filament\Schemas\Components\Tabs\Tab;

FilamentShot::form([
    Tabs::make('Settings')
        ->tabs([
            Tab::make('General')
                ->icon('heroicon-o-cog-6-tooth')
                ->schema([
                    TextInput::make('site_name')->label('Site Name'),
                    TextInput::make('site_url')->label('Site URL'),
                ]),
            Tab::make('Notifications')
                ->icon('heroicon-o-bell')
                ->schema([
                    Toggle::make('email_notifications')->label('Email Notifications'),
                ]),
        ]),
])
->state(['site_name' => 'My App', 'site_url' => 'https://myapp.com'])
->save('form-tabs.png');

Use ->activeTab(2) to render a specific tab as active.

Forms with Wizard Steps

use CCK\FilamentShot\FilamentShot;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Schemas\Components\Wizard;
use Filament\Schemas\Components\Wizard\Step;

FilamentShot::form([
    Wizard::make([
        Step::make('Account')
            ->description('Your credentials')
            ->schema([
                TextInput::make('email')->label('Email'),
                TextInput::make('password')->label('Password'),
            ]),
        Step::make('Profile')
            ->description('Personal info')
            ->schema([
                TextInput::make('name')->label('Full Name'),
            ]),
        Step::make('Review')
            ->description('Confirm details')
            ->schema([
                Toggle::make('terms')->label('I accept the terms'),
            ]),
    ]),
])
->state(['email' => 'jane@example.com'])
->width(900)
->save('form-wizard.png');

Use ->startOnStep(2) to render a specific step as active (previous steps show as completed).

Forms with Layout Components

use CCK\FilamentShot\FilamentShot;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Schemas\Components\Section;

FilamentShot::form([
    Section::make('Personal Information')
        ->schema([
            TextInput::make('name')->label('Full Name'),
            TextInput::make('email')->label('Email'),
        ]),
    Section::make('Settings')
        ->schema([
            Toggle::make('active')->label('Active'),
        ]),
])
->state(['name' => 'Jane Doe', 'email' => 'jane@example.com', 'active' => true])
->save('form-with-sections.png');

Tables

use CCK\FilamentShot\FilamentShot;
use Filament\Tables\Columns\TextColumn;

FilamentShot::table()
    ->columns([
        TextColumn::make('name'),
        TextColumn::make('email'),
        TextColumn::make('role'),
    ])
    ->records([
        ['name' => 'Alice', 'email' => 'alice@example.com', 'role' => 'Admin'],
        ['name' => 'Bob', 'email' => 'bob@example.com', 'role' => 'Editor'],
        ['name' => 'Charlie', 'email' => 'charlie@example.com', 'role' => 'Viewer'],
    ])
    ->heading('Team Members')
    ->striped()
    ->save('table.png');

Tables with Action Groups (Dropdowns)

use CCK\FilamentShot\FilamentShot;
use Filament\Actions\Action;
use Filament\Actions\ActionGroup;
use Filament\Tables\Columns\TextColumn;

FilamentShot::table()
    ->columns([
        TextColumn::make('name'),
        TextColumn::make('email'),
    ])
    ->records([
        ['name' => 'Alice', 'email' => 'alice@example.com'],
        ['name' => 'Bob', 'email' => 'bob@example.com'],
    ])
    ->recordActions([
        Action::make('view')->label('View')->icon('heroicon-o-eye'),
        ActionGroup::make([
            Action::make('edit')->label('Edit')->icon('heroicon-o-pencil-square'),
            Action::make('delete')->label('Delete')->icon('heroicon-o-trash')->color('danger'),
        ]),
    ])
    ->save('table-action-group.png');

Reorderable Tables

FilamentShot::table()
    ->columns([
        TextColumn::make('name'),
        TextColumn::make('email'),
    ])
    ->records([
        ['name' => 'Alice', 'email' => 'alice@example.com'],
        ['name' => 'Bob', 'email' => 'bob@example.com'],
    ])
    ->reorderable()
    ->save('table-reorderable.png');

Tables with Bulk Actions

use CCK\FilamentShot\FilamentShot;
use Filament\Actions\BulkAction;
use Filament\Tables\Columns\TextColumn;

FilamentShot::table()
    ->columns([
        TextColumn::make('name'),
        TextColumn::make('email'),
        TextColumn::make('status')->badge(),
    ])
    ->records([
        ['name' => 'Alice', 'email' => 'alice@example.com', 'status' => 'Active'],
        ['name' => 'Bob', 'email' => 'bob@example.com', 'status' => 'Blocked'],
        ['name' => 'Charlie', 'email' => 'charlie@example.com', 'status' => 'Active'],
    ])
    ->bulkActions([
        BulkAction::make('change_status')
            ->label('Change Status')
            ->icon('heroicon-o-bell')
            ->color('success'),
        BulkAction::make('delete')
            ->label('Delete')
            ->icon('heroicon-o-trash')
            ->color('danger'),
    ])
    ->selectedRows([0, 2]) // indices of rows to show as checked
    ->save('table-bulk-actions.png');

Infolists

use CCK\FilamentShot\FilamentShot;
use Filament\Infolists\Components\TextEntry;

FilamentShot::infolist([
    TextEntry::make('name')->label('Name'),
    TextEntry::make('email')->label('Email'),
    TextEntry::make('joined')->label('Member Since'),
])
->state([
    'name' => 'Jane Doe',
    'email' => 'jane@example.com',
    'joined' => 'January 2024',
])
->save('infolist.png');

Navigation

use CCK\FilamentShot\FilamentShot;
use Filament\Navigation\NavigationGroup;
use Filament\Navigation\NavigationItem;

FilamentShot::navigation()
    ->items([
        NavigationItem::make('Dashboard')
            ->icon('heroicon-o-home'),
        NavigationGroup::make('Content')
            ->items([
                NavigationItem::make('Posts')
                    ->icon('heroicon-o-document-text')
                    ->isActiveWhen(fn () => true)
                    ->badge('24', 'success'),
                NavigationItem::make('Pages')
                    ->icon('heroicon-o-document'),
            ]),
        NavigationGroup::make('Settings')
            ->items([
                NavigationItem::make('General')
                    ->icon('heroicon-o-cog-6-tooth'),
            ]),
    ])
    ->heading('Admin Panel')
    ->width(320)
    ->save('navigation.png');

Stats Widgets

use CCK\FilamentShot\FilamentShot;
use Filament\Widgets\StatsOverviewWidget\Stat;

FilamentShot::stats([
    Stat::make('Total Users', '1,234')
        ->description('12% increase')
        ->descriptionIcon('heroicon-m-arrow-trending-up')
        ->color('success'),
    Stat::make('Revenue', '$56,789')
        ->description('8% increase')
        ->chart([7, 3, 4, 5, 6, 3, 5, 8]),
    Stat::make('Orders', '456')
        ->description('3% decrease')
        ->descriptionIcon('heroicon-m-arrow-trending-down')
        ->color('danger'),
])
->save('stats.png');

Notifications

use CCK\FilamentShot\FilamentShot;

FilamentShot::notification()
    ->title('Status Updated')
    ->body('The customer status has been changed to Active.')
    ->success()
    ->width(400)
    ->save('notification.png');

Output Methods

Every renderer supports multiple output methods:

$renderer = FilamentShot::form([...]);

// Save to disk
$renderer->save('/path/to/screenshot.png');

// Get base64-encoded PNG
$base64 = $renderer->toBase64();

// Get rendered HTML (useful for debugging)
$html = $renderer->toHtml();

// Get an HTTP response with the PNG
return $renderer->toResponse();

Customization

Viewport

Control the screenshot dimensions and resolution:

FilamentShot::form([...])
    ->width(1280)
    ->height(720)
    ->deviceScale(2)  // Retina / HiDPI
    ->save('screenshot.png');

Theme

Switch between light and dark mode, or set a custom primary color:

FilamentShot::form([...])
    ->darkMode()
    ->primaryColor('#3b82f6')
    ->save('dark-form.png');

3rd-Party Plugin Support

FilamentShot automatically includes CSS from any Filament plugin registered via FilamentAsset::register(). If the plugin's service provider is loaded, its styles will appear in your screenshots — no extra configuration needed.

use Ysfkaya\FilamentPhoneInput\Forms\PhoneInput;

FilamentShot::form([
    TextInput::make('name')->label('Name'),
    PhoneInput::make('phone')->label('Phone Number'),
])
    ->state(['name' => 'Jane Doe', 'phone' => '+60123456789'])
    ->width(600)
    ->save('form-with-phone.png');

If a plugin's CSS isn't auto-discovered (e.g. it doesn't use Filament's asset pipeline), you can inject it manually using ->css() or ->cssFile():

FilamentShot::form([
    TextInput::make('name')->label('Full Name'),
    TextInput::make('phone')->label('Phone Number')->prefix('+60'),
    TextInput::make('email')->label('Email'),
])
    ->state(['name' => 'Jane Doe', 'phone' => '12-345 6789', 'email' => 'jane@example.com'])
    ->css('
        /* Custom plugin styles injected directly */
        .my-plugin-input { border-color: #3b82f6; }
    ')
    ->cssFile('/path/to/vendor/my-plugin/dist/plugin.css')
    ->width(600)
    ->save('form-with-phone.png');

Form with custom CSS

Artisan Command

Capture screenshots from a config file:

php artisan filament-shot:capture --config=screenshots.php

The config file should return an array of screenshot definitions:

<?php

use Filament\Forms\Components\TextInput;

return [
    [
        'type' => 'form',
        'components' => [
            TextInput::make('name')->label('Name'),
            TextInput::make('email')->label('Email'),
        ],
        'output' => 'storage/screenshots/form.png',
        'width' => 800,
        'dark_mode' => false,
    ],
];

Supported types: form, table, infolist, stats.

Configuration

// config/filament-shot.php

return [

    // Default viewport dimensions for screenshots
    'viewport' => [
        'width' => 1024,
        'height' => 768,
        'device_scale_factor' => 2,
    ],

    // Default theme settings
    'theme' => [
        'dark_mode' => false,
        'primary_color' => '#6366f1',
    ],

    // Browsershot / Puppeteer configuration
    'browsershot' => [
        'node_binary' => null,       // Path to Node.js binary
        'npm_binary' => null,        // Path to npm binary
        'chrome_path' => null,       // Path to Chrome/Chromium binary
        'no_sandbox' => false,       // Set true for Docker/CI environments
        'timeout' => 60,             // Timeout in seconds
        'additional_options' => [],   // Extra Puppeteer launch options
    ],

    // CSS customization
    'css' => [
        'theme_path' => null,  // Override path to Filament theme CSS
        'extra' => '',         // Additional CSS to inject
    ],

];

Docker / CI Environments

If running in a Docker container or CI pipeline, you'll likely need to enable no_sandbox:

// config/filament-shot.php
'browsershot' => [
    'no_sandbox' => true,
],

Testing

composer test

Run only unit tests (no Chrome required):

vendor/bin/pest --exclude-group=integration

Run integration tests (requires Chrome + Puppeteer):

vendor/bin/pest --group=integration

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

License

The MIT License (MIT). Please see License File for more information.