Kustomisasi Halaman Login di Filament 4
Kalau kamu udah pernah pakai FilamentPHP, pasti tahu dong tampilan login bawaannya itu simple banget bersih sih, tapi kadang terlalu standar. Nah, gimana kalau kita pengen ubah tampilannya biar lebih kita banget? Misalnya tambahin logo, ubah warna, atau bahkan bikin layout yang lebih keren.
Di artikel ini, kita bahas cara Kustomisasi Halaman Login di Filament 4 dengan gaya santai tapi tetap ngoding.
1. Bikin Komponen Livewire
Pertama, kita bikin komponen baru untuk halaman login kita:
php artisan make:livewire AdminLoginPerintah ini bakal bikin dua file:
app/Livewire/AdminLogin.php ← logic dan autentikasi
resources/views/livewire/admin-login.blade.php ← tampilan login
2. Buat Layout "Guest"
Kita butuh layout khusus untuk halaman login (yang di luar dashboard admin Filament).
Buat file baru:
resources/views/components/layouts/guest.blade.phpIsinya seperti ini:
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ $title ?? config('app.name', 'Laravel') }}</title>
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />
<!-- Styles -->
@filamentStyles
@vite('resources/css/app.css')
</head>
<body class="font-sans antialiased">
{{ $slot }}
@filamentScripts
@vite('resources/js/app.js')
</body>
</html>Penjelasan singkat:
@filamentStylesdan@filamentScripts= otomatis load semua CSS & JS Filament.@vite('resources/css/app.css')= load Tailwind dari proyek kamu.{{ $slot }}= tempat isi konten login kamu bakal ditampilkan.
3. Buat Logika Login di Livewire
Sekarang buka file:
app/Livewire/AdminLogin.phpIsi seperti ini:
<?php
namespace App\Livewire;
use Filament\Facades\Filament;
use Livewire\Component;
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
use DanHarrin\LivewireRateLimiting\Exceptions\TooManyRequestsException;
class AdminLogin extends Component
{
use WithRateLimiting;
public $email = '';
public $password = '';
public bool $remember = false;
public function mount()
{
// Kalau user udah login, langsung lempar ke dashboard
if (Filament::auth()->check()) {
return redirect()->intended(Filament::getUrl());
}
}
public function authenticate()
{
// Batasin percobaan login biar gak brute force
try {
$this->rateLimit(5);
} catch (TooManyRequestsException $exception) {
$this->addError('email', __('Terlalu banyak percobaan login. Coba lagi dalam :seconds detik.', [
'seconds' => $exception->secondsUntilAvailable,
]));
return;
}
// Validasi manual
$this->validate([
'email' => 'required|email',
'password' => 'required|string|min:4',
]);
// Coba login via Filament
if (!Filament::auth()->attempt([
'email' => $this->email,
'password' => $this->password,
], $this->remember)) {
$this->addError('email', 'Email atau password salah.');
return;
}
session()->regenerate();
return redirect()->intended(Filament::getUrl());
}
public function render(): \Illuminate\Contracts\View\View
{
return view('livewire.admin-login')->layout('components.layouts.guest');
}
}Penjelasan detail:
| Bagian | Fungsi |
|---|---|
use InteractsWithForms; | Supaya bisa pakai sistem form dari Filament. |
use WithRateLimiting; | Batasin jumlah percobaan login (contoh: 5x per menit). |
mount() | Cek kalau user udah login → redirect langsung ke dashboard. |
authenticate() | Proses login: validasi, rate-limit, autentikasi, dan redirect. |
Filament::auth()->attempt() | Login pakai sistem auth-nya Filament. |
ValidationException | Biar kalau gagal, bisa munculin pesan error di form. |
4. Desain Tampilan Login
Sekarang buka:
resources/views/livewire/admin-login.blade.php<div class="min-h-screen flex items-center justify-center bg-gradient-to-br from-gray-800 via-gray-700 to-gray-900 relative overflow-hidden">
{{-- Background image --}}
<div class="absolute inset-0 bg-[url('https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=1920')] bg-cover bg-center opacity-30"></div>
<div class="absolute inset-0 bg-gradient-to-br from-gray-900/90 via-gray-800/80 to-gray-900/90"></div>
<div class="relative z-10 w-full max-w-6xl mx-4 flex">
{{-- Form --}}
<div class="w-full lg:w-1/2 bg-gray-800/90 backdrop-blur-lg border border-gray-700/50 rounded-3xl p-10 shadow-2xl">
{{-- Logo --}}
<div class="flex items-center gap-2 mb-8">
<div class="w-8 h-8 bg-blue-500 rounded-full"></div>
<span class="text-white font-semibold text-lg">{{ config('app.name','Semangatin') }}</span>
</div>
{{-- Title --}}
<div class="mb-8">
<p class="text-gray-400 text-sm mb-2 uppercase tracking-wider">WELCOME BACK</p>
<h2 class="text-4xl font-bold text-white mb-3">
Sign in to your account<span class="text-blue-500">.</span>
</h2>
</div>
{{-- Form Input --}}
<form wire:submit="authenticate" class="space-y-5">
{{-- Email --}}
<div>
<label for="email" class="block text-sm text-gray-300 mb-2">Email</label>
<input type="email" id="email" wire:model="email"
placeholder="[email protected]"
class="w-full px-4 py-3 bg-gray-700/50 border border-gray-600/50 rounded-lg text-white placeholder-gray-400 focus:ring-2 focus:ring-blue-500/50 focus:border-transparent transition-all duration-300 text-sm @error('email') border-red-500 @enderror"
required autofocus />
@error('email')
<p class="mt-1 text-sm text-red-500">{{ $message }}</p>
@enderror
</div>
{{-- Password --}}
<div>
<label for="password" class="block text-sm text-gray-300 mb-2">Password</label>
<input type="password" id="password" wire:model="password"
placeholder="••••••••"
class="w-full px-4 py-3 bg-gray-700/50 border border-gray-600/50 rounded-lg text-white placeholder-gray-400 focus:ring-2 focus:ring-blue-500/50 focus:border-transparent transition-all duration-300 text-sm @error('password') border-red-500 @enderror"
required autocomplete="current-password" />
@error('password')
<p class="mt-1 text-sm text-red-500">{{ $message }}</p>
@enderror
</div>
{{-- Forgot Password --}}
<div class="text-right">
<a href="#" class="text-sm text-blue-400 hover:text-blue-300 transition">Forgot password?</a>
</div>
{{-- Submit --}}
<button type="submit"
class="w-full py-3 px-6 bg-blue-500 hover:bg-blue-600 text-white font-semibold rounded-lg transition-all duration-300 shadow-lg shadow-blue-500/30 hover:scale-[1.02] active:scale-[0.98]"
wire:loading.attr="disabled">
<span wire:loading.remove>Sign in</span>
<span wire:loading>Signing in...</span>
</button>
</form>
</div>
{{-- Image Section --}}
<div class="hidden lg:block w-1/2 relative rounded-r-3xl overflow-hidden">
<div class="absolute inset-0 bg-gradient-to-br from-gray-700/50 to-gray-900/50"></div>
<img src="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=800"
alt="Background" class="w-full h-full object-cover">
</div>
</div>
</div>Penjelasan singkat:
| Elemen | Fungsi |
|---|---|
bg-gradient-to-br | Bikin gradasi background elegan dari abu tua ke hitam. |
backdrop-blur-lg | Efek blur transparan kayak kaca (glassmorphism). |
wire:model="data.email" | Data binding Livewire (langsung ke $data['email']). |
wire:submit="authenticate" | Jalankan fungsi authenticate() di PHP saat form dikirim. |
| Bagian kanan (gambar) | Estetika aja, biar login page nggak polos. |
5. Hubungkan ke Filament Panel
Sekarang, biar Filament tahu kalau login-nya pakai halaman custom ini,
buka file:
app/Providers/Filament/AdminPanelPanelProvider.phpUbah / tambahkan baris berikut:
use App\Livewire\AdminLogin;
class AdminPanelPanelProvider extends PanelProvider
{
public function panel(Panel $panel): Panel
{
return $panel
->default()
->id('admin-panel')
->path('admin') // path dashboard kamu
->login(AdminLogin::class) // pakai login custom
}
}

Jika muncul error seperti di atas, maka jalankan perintah berikut:
npm i
npm run buildHarusnya halaman login custom kamu langsung muncul — bukan login Filament bawaan lagi!
