September 27, 2026
Laravel Filament.
Membangun admin panel dengan Filament 5: panel, resource CRUD, form, tabel, action, widget dashboard, otorisasi, dan kapan lebih baik membuat admin sendiri.
Filament adalah kumpulan komponen full-stack untuk Laravel yang dibangun di atas Livewire, Alpine.js, dan Tailwind CSS. Paling terkenal sebagai admin panel builder: dengan beberapa perintah Artisan, kamu mendapat halaman login, dashboard, dan CRUD lengkap (tabel dengan search, filter, sort, pagination, plus form dengan validasi) β semuanya ditulis dalam PHP, tanpa menulis JavaScript.
Versi: Filament 5 (Januari 2026) secara fitur identik dengan Filament 4; perbedaannya adalah dukungan Livewire 4. Kedua versi masih dirawat. Sintaks di catatan ini berlaku untuk v4 dan v5. Tutorial lama (v2/v3) memakai struktur yang berbeda β perhatikan versinya saat mencari contoh.
1. Instalasi
Syarat: PHP 8.2+, Laravel 11.28+ (Laravel 13 butuh PHP 8.3+), Tailwind CSS 4.1+ jika kamu membuat tema kustom.
composer require filament/filament:"^5.0"
php artisan filament:install --panels # membuat app/Providers/Filament/AdminPanelProvider.php
php artisan make:filament-user # buat akun admin
Buka http://localhost:8000/admin.
Panel dikonfigurasi di AdminPanelProvider:
use Filament\Panel;
use Filament\Support\Colors\Color;
public function panel(Panel $panel): Panel
{
return $panel
->default()
->id('admin')
->path('admin')
->login()
->colors(['primary' => Color::Amber])
->discoverResources(in: app_path('Filament/Resources'), for: 'App\Filament\Resources')
->discoverPages(in: app_path('Filament/Pages'), for: 'App\Filament\Pages')
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\Filament\Widgets');
}
Satu aplikasi bisa punya beberapa panel (misal /admin untuk staf, /app untuk pelanggan): php artisan make:filament-panel app.
2. Resource: CRUD dari Model
php artisan make:filament-resource Product --generate
| Opsi | Fungsi |
|---|---|
--generate | Menebak field form & kolom tabel dari struktur tabel database |
--view | Tambah halaman View (read-only) |
--soft-deletes | Dukungan restore / force delete |
--simple | CRUD dalam modal di satu halaman βManageβ (cocok untuk data kecil) |
Struktur yang dihasilkan (v4/v5):
app/Filament/Resources/Products/
βββ ProductResource.php
βββ Pages/
β βββ CreateProduct.php
β βββ EditProduct.php
β βββ ListProducts.php
βββ Schemas/
β βββ ProductForm.php
βββ Tables/
βββ ProductsTable.php
ProductResource hanya mendelegasikan:
use Filament\Schemas\Schema;
use Filament\Tables\Table;
public static function form(Schema $schema): Schema
{
return ProductForm::configure($schema);
}
public static function table(Table $table): Table
{
return ProductsTable::configure($table);
}
3. Form
<?php
namespace App\Filament\Resources\Products\Schemas;
use Filament\Forms\Components\FileUpload;
use Filament\Forms\Components\RichEditor;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Schema;
use Illuminate\Support\Str;
class ProductForm
{
public static function configure(Schema $schema): Schema
{
return $schema->components([
Section::make('Informasi Produk')
->columns(2)
->schema([
TextInput::make('name')
->required()
->maxLength(255)
->live(onBlur: true)
->afterStateUpdated(fn ($state, $set) => $set('slug', Str::slug($state))),
TextInput::make('slug')
->required()
->unique(ignoreRecord: true),
Select::make('category_id')
->relationship('category', 'name')
->searchable()
->preload()
->required(),
TextInput::make('price')
->numeric()
->prefix('Rp')
->minValue(0)
->required(),
Toggle::make('is_active')->default(true),
]),
RichEditor::make('description')->columnSpanFull(),
FileUpload::make('image')->image()->directory('products'),
]);
}
}
Validasi ditulis sebagai method di field (required(), unique(), maxLength()), dan Filament menyimpan data ke model secara otomatis β pastikan kolom-kolomnya fillable (lihat Eloquent ORM
).
4. Tabel
<?php
namespace App\Filament\Resources\Products\Tables;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Tables\Columns\IconColumn;
use Filament\Tables\Columns\ImageColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Filters\TernaryFilter;
use Filament\Tables\Table;
class ProductsTable
{
public static function configure(Table $table): Table
{
return $table
->columns([
ImageColumn::make('image'),
TextColumn::make('name')->searchable()->sortable(),
TextColumn::make('category.name')->label('Kategori')->sortable(),
TextColumn::make('price')->money('IDR')->sortable(),
IconColumn::make('is_active')->boolean(),
TextColumn::make('created_at')->since()->toggleable(isToggledHiddenByDefault: true),
])
->defaultSort('created_at', 'desc')
->filters([
SelectFilter::make('category')->relationship('category', 'name'),
TernaryFilter::make('is_active'),
])
->recordActions([
EditAction::make(),
])
->toolbarActions([
BulkActionGroup::make([
DeleteBulkAction::make(),
]),
]);
}
}
Relasi
di kolom (category.name) otomatis di-eager-load, jadi aman dari N+1 untuk kasus umum.
5. Action
Action adalah tombol yang menjalankan sesuatu, opsional dengan modal konfirmasi atau form. Sejak v4, semua action memakai namespace yang sama: Filament\Actions\....
use Filament\Actions\Action;
use Filament\Forms\Components\TextInput;
use Filament\Notifications\Notification;
Action::make('adjustStock')
->label('Atur Stok')
->icon('heroicon-o-archive-box')
->schema([
TextInput::make('qty')->numeric()->required(),
])
->action(function (array $data, Product $record): void {
$record->increment('stock', $data['qty']);
Notification::make()->title('Stok diperbarui')->success()->send();
})
->requiresConfirmation()
->visible(fn (Product $record) => $record->is_active);
Pasang di recordActions([...]) pada tabel, atau di header halaman lewat method getHeaderActions() pada page class.
6. Widget Dashboard
php artisan make:filament-widget StatsOverview --stats-overview
php artisan make:filament-widget OrdersChart --chart
use App\Models\Order;
use App\Models\Product;
use Filament\Widgets\StatsOverviewWidget;
use Filament\Widgets\StatsOverviewWidget\Stat;
class StatsOverview extends StatsOverviewWidget
{
protected function getStats(): array
{
return [
Stat::make('Produk aktif', Product::where('is_active', true)->count()),
Stat::make('Order hari ini', Order::whereDate('created_at', today())->count())
->description('Termasuk yang belum dibayar')
->color('success'),
Stat::make('Omzet bulan ini', 'Rp ' . number_format(
Order::whereMonth('created_at', now()->month)->sum('total'), 0, ',', '.'
)),
];
}
}
Widget yang ditaruh di app/Filament/Widgets otomatis tampil di dashboard.
7. Otorisasi & Akses Panel
- Siapa yang boleh masuk panel. Di environment
production, Filament menolak semua user (403) sampai modelUsermengimplementasikanFilamentUser:
use Filament\Models\Contracts\FilamentUser;
use Filament\Panel;
class User extends Authenticatable implements FilamentUser
{
public function canAccessPanel(Panel $panel): bool
{
return $this->is_admin && $this->hasVerifiedEmail();
}
}
- Apa yang boleh dilakukan di resource. Filament mengikuti Laravel Policy (
viewAny,create,update,delete,deleteAny, dst.):
php artisan make:policy ProductPolicy --model=Product
Untuk role & permission yang lebih kompleks, kombinasikan dengan spatie/laravel-permission atau plugin seperti Filament Shield.
8. Filament vs Admin Custom
| Pertimbangan | Filament | Admin custom (Blade / Inertia + React) |
|---|---|---|
| Kecepatan membuat CRUD | Sangat cepat (menit per resource) | Lambat, semuanya ditulis manual |
| Konsistensi UI | Otomatis konsisten | Tergantung disiplin tim |
| Desain unik / brand kuat | Terbatas pada tema & komponen Filament | Bebas penuh |
| Interaksi sangat kustom (drag-drop kompleks, editor visual) | Bisa lewat custom page/Livewire, tapi melawan arus | Lebih natural |
| Stack frontend | Livewire (PHP) β tidak memakai React | Satu stack dengan frontend publik |
| Cocok untuk | Back-office, CMS, data master, laporan internal | Admin yang juga jadi produk utama untuk user |
Contoh nyata: Watch Store memakai admin panel Blade buatan sendiri; untuk kebutuhan CRUD produk, order, dan laporan standar seperti itu, Filament bisa memangkas waktu pengerjaan secara signifikan. Sebaliknya, aplikasi yang βadminβ-nya adalah produk itu sendiri β misal dashboard pemilik kos di Kos Management App β lebih cocok dengan UI custom.
9. Pitfalls
- 403 di produksi padahal di lokal bisa login β belum mengimplementasikan
FilamentUser::canAccessPanel(). - Mengikuti tutorial v3 β namespace berbeda (
Filament\Tables\Actions\EditActiondi v3 vsFilament\Actions\EditActiondi v4/v5,Form $formvsSchema $schema). Gunakan upgrade script resmi saat migrasi. - Data tidak tersimpan β kolom tidak ada di
$fillable/#[Fillable]. - Upload file tidak tampil β belum
php artisan storage:linkatauAPP_URLsalah. - Tabel lambat pada data besar β tambahkan index DB pada kolom yang
searchable()/sortable(), dan hindari kolom hasil komputasi berat per baris. - Setelah upgrade, asset/tampilan rusak β jalankan
php artisan filament:upgrade(biasanya sudah ada di scriptpost-autoload-dumpcomposer). - Menjadikan Filament untuk halaman publik yang butuh desain khusus β biasanya lebih repot daripada membuat halaman Laravel + React biasa.
Referensi: Filament Docs 5.x , Resources , Tables , Actions , Widgets , Introducing Filament v5 .

Hey! I’m Fanny, the software engineer tending to this digital garden. You can read more about me, or subscribe by email.