thesimons's avatar

Creating a custom Registration page in Filament 4

Hello,

is there any tutorial / how to about creating a custom Registration page in Filament 4?

All material that I've found is about Filament 3.

Thanks, Simon

3 likes
3 replies
cvairlis's avatar

This worked for me

DigitalArtisan's avatar

Here is a tutorial that I just created:

Tutorial: Creating a Custom Registration Page in Filament 4

This tutorial walks you through creating a custom registration page in Filament 4 with additional fields beyond the standard name, email, and password.

Overview

We'll create a custom registration page that extends Filament's built-in registration functionality to include:

  • Standard fields: Name, Email, Password, Password Confirmation
  • Custom fields: Phone Number, Company/Organization, Terms and Conditions acceptance

Prerequisites

  • Laravel 12.x
  • Filament 4.x installed
  • A Filament panel already configured

Step-by-Step Implementation

Step 1: Generate the Custom Registration Page

First, we need to create a custom registration page class. Filament doesn't have a specific command for auth pages, so we'll use the general page command and then customize it.

php artisan filament:make-page Auth/Register

This creates a basic page at app/Filament/Pages/Auth/Register.php. We'll modify it to extend Filament's base Register class.

Step 2: Customize the Registration Page

Edit app/Filament/Pages/Auth/Register.php to extend Filament's base Register class and add custom fields:

Key Points:

  • Extends Filament\Auth\Pages\Register to inherit all base functionality
  • Overrides form() to add custom fields alongside standard ones
  • getPhoneFormComponent(): Phone field with tel input type and unique validation
  • getCompanyFormComponent(): Optional company field
  • getTermsFormComponent(): Required checkbox with dehydrated(false) so it's not saved to database
  • mutateFormDataBeforeRegister(): Removes the terms field before saving (it's only for validation)
  • afterRegister(): Hook for post-registration logic (emails, role assignment, etc.)

Step 3: Enable Registration in Panel Provider

Update app/Providers/Filament/AdminPanelProvider.php to enable registration and point to your custom page:

Add the import:

use App\Filament\Pages\Auth\Register;

Update the panel configuration:

public function panel(Panel $panel): Panel
{
    return $panel
        ->default()
        ->id('admin')
        ->path('admin')
        ->login()
        ->registration(Register::class)  // Add this line
        ->colors([
            'primary' => Color::Amber,
        ])
        // ... rest of configuration
}

The ->registration(Register::class) method enables registration and tells Filament to use your custom Register page.

Step 4: Update the User Model

Add the new fields to the $fillable array in app/Models/User.php:

protected $fillable = [
    'name',
    'email',
    'password',
    'phone',      // Add this
    'company',    // Add this
];

Note: We don't add terms because it's only used for validation, not stored in the database.

Step 5: Create Database Migration

Create a migration to add the new columns to the users table:

php artisan make:migration add_phone_and_company_to_users_table --table=users

Edit the migration file:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('phone', 20)->nullable()->after('email');
            $table->string('company')->nullable()->after('phone');
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn(['phone', 'company']);
        });
    }
};

Key Points:

  • Both fields are nullable (optional)
  • Phone is limited to 20 characters
  • Company is a standard string field
  • The down() method properly reverses the migration

Step 6: Run the Migration

Execute the migration:

php artisan migrate

Understanding the Implementation

Form Components

  1. Phone Field:

    • Uses tel() input type for mobile keyboard optimization
    • Has unique() validation to prevent duplicate phone numbers
    • Maximum length of 20 characters
  2. Company Field:

    • Simple text input
    • Optional (no required() call)
    • Maximum length of 255 characters
  3. Terms Field:

    • Required checkbox using accepted() validation
    • dehydrated(false) prevents it from being saved to the database
    • Only used for validation purposes

Data Mutation

The mutateFormDataBeforeRegister() method is crucial for fields that shouldn't be saved. Since terms is only for validation, we remove it before the user is created.

Post-Registration Hooks

The afterRegister() method is called after a user is successfully registered. This is where you can:

  • Send welcome emails
  • Assign default roles
  • Create related records
  • Trigger notifications

Alternatively, you can listen to Filament's Registered event in your EventServiceProvider.

Testing

  1. Navigate to /admin/register (or your panel path + /register)
  2. Fill out the registration form with:
    • Name
    • Email (must be unique)
    • Phone (optional, but must be unique if provided)
    • Company (optional)
    • Password
    • Password confirmation
    • Terms acceptance (required)
  3. Submit the form
  4. Verify the user is created with all provided data
  5. Check that the terms field is NOT saved to the database

Customization Options

Adding More Fields

To add additional fields:

  1. Create a new component method (e.g., getAddressFormComponent())
  2. Add it to the form() method's components array
  3. Add the field to the User model's $fillable array
  4. Create a migration if it's a database field

Making Fields Required

Add ->required() to any field component:

protected function getCompanyFormComponent(): Component
{
    return TextInput::make('company')
        ->label('Company/Organization')
        ->required()  // Add this
        ->maxLength(255);
}

Custom Validation

Add validation rules to any field:

protected function getPhoneFormComponent(): Component
{
    return TextInput::make('phone')
        ->label('Phone Number')
        ->tel()
        ->maxLength(20)
        ->unique($this->getUserModel())
        ->rules(['regex:/^\+?[1-9]\d{1,14}$/']);  // E.164 format
}

Custom Post-Registration Logic

Implement the afterRegister() method with your custom logic:

protected function afterRegister(): void
{
    $user = $this->form->getRecord();
    
    // Send welcome email
    Mail::to($user->email)->send(new WelcomeMail($user));
    
    // Assign default role
    $user->assignRole('customer');
    
    // Create related profile
    $user->profile()->create([
        'phone' => $user->phone,
        'company' => $user->company,
    ]);
}

Troubleshooting

Registration Page Not Showing

  • Ensure ->registration(Register::class) is added to your panel provider
  • Clear config cache: php artisan config:clear
  • Clear route cache: php artisan route:clear

Fields Not Saving

  • Verify fields are in the User model's $fillable array
  • Check that the migration has been run
  • Ensure fields are not set to dehydrated(false) unless intentional

Validation Errors

  • Check that validation rules are correct
  • Ensure unique constraints match your database structure
  • Verify required fields have ->required() in the component

Summary

You've successfully created a custom Filament 4 registration page with:

  • ✅ Extended Filament's base Register class
  • ✅ Added custom form fields (phone, company, terms)
  • ✅ Configured the panel to use the custom registration page
  • ✅ Updated the User model to accept new fields
  • ✅ Created and ran database migrations
  • ✅ Implemented custom validation and post-registration logic

The registration page is now available at /admin/register and fully integrated with Filament's authentication system.

DigitalArtisan's avatar

For completeness, here is the tutorial for creating a user resource in Filament 4:

Tutorial: Creating a User Resource in Filament 4

This tutorial walks you through creating a comprehensive User Resource in Filament 4 to manage users in your admin panel, including viewing, creating, editing, and deleting users with custom fields.

Overview

We'll create a User Resource that provides a full CRUD (Create, Read, Update, Delete) interface for managing users, including:

  • A table view with searchable and sortable columns
  • A form with organized sections for user information and authentication
  • Proper password handling (required on create, optional on edit)
  • Custom fields (phone, company) from your registration form
  • Navigation integration

Prerequisites

  • Laravel 12.x
  • Filament 4.x installed
  • A User model with custom fields (phone, company) already migrated
  • A Filament panel already configured

Step-by-Step Implementation

Step 1: Generate the User Resource

Use Filament's artisan command to generate a complete resource with all necessary files:

php artisan filament:make-resource User --generate

This command will create:

  • app/Filament/Resources/Users/UserResource.php - Main resource class
  • app/Filament/Resources/Users/Tables/UsersTable.php - Table configuration
  • app/Filament/Resources/Users/Schemas/UserForm.php - Form configuration
  • app/Filament/Resources/Users/Pages/ListUsers.php - List page
  • app/Filament/Resources/Users/Pages/CreateUser.php - Create page
  • app/Filament/Resources/Users/Pages/EditUser.php - Edit page

The --generate flag automatically generates the table, form, and page classes.

Step 2: Configure the Main Resource Class

Edit app/Filament/Resources/Users/UserResource.php to customize navigation and labels:

Key Points:

  • $model - Links the resource to your User model
  • $navigationLabel - Label shown in the sidebar navigation
  • $modelLabel / $pluralModelLabel - Labels used in page titles and actions
  • $navigationIcon - Icon displayed in navigation (Heroicon::OutlinedUserGroup)
  • form() - Delegates form configuration to UserForm class
  • table() - Delegates table configuration to UsersTable class
  • getPages() - Defines the routes for list, create, and edit pages

Step 3: Configure the Table View

Edit app/Filament/Resources/Users/Tables/UsersTable.php to customize the columns displayed in the list view:

Column Features:

  • searchable() - Enables search functionality for the column
  • sortable() - Allows sorting by clicking the column header
  • copyable() - Adds a copy button to quickly copy the value
  • toggleable() - Allows users to show/hide the column
  • toggleable(isToggledHiddenByDefault: true) - Column is hidden by default
  • label() - Custom label for the column header
  • dateTime() - Formats the value as a date/time

Table Actions:

  • recordActions() - Actions available for each row (Edit button)
  • toolbarActions() - Bulk actions available in the toolbar (Delete multiple)

Step 4: Configure the Form

Edit app/Filament/Resources/Users/Schemas/UserForm.php to create an organized form with sections:

Key Features:

  1. Sections: Organize form fields into logical groups

    • Section::make('Title') - Creates a collapsible section
    • ->columns(2) - Displays fields in a 2-column layout
  2. Field Validation:

    • ->required() - Makes the field mandatory
    • ->email() - Validates email format
    • ->tel() - Optimizes input for telephone numbers
    • ->maxLength(255) - Sets maximum character length
    • ->unique(ignoreRecord: true) - Ensures uniqueness, ignoring current record on edit
  3. Password Handling:

    • ->required(fn ($livewire) => ...) - Required only when creating (not editing)
    • ->dehydrated(...) - Only saves password if provided (on edit) or always (on create)
    • ->dehydrateStateUsing(...) - Automatically hashes the password before saving
    • Dynamic label changes based on create vs edit context
  4. Important Note on Namespaces:

    • Section is in Filament\Schemas\Components\Section (not Forms namespace)
    • TextInput and DateTimePicker are in Filament\Forms\Components

Step 5: Customize Page Classes (Optional)

The page classes are automatically generated and work out of the box. You can customize them if needed:

CreateUser.php:

<?php

namespace App\Filament\Resources\Users\Pages;

use App\Filament\Resources\Users\UserResource;
use Filament\Resources\Pages\CreateRecord;

class CreateUser extends CreateRecord
{
    protected static string $resource = UserResource::class;
}

EditUser.php:

<?php

namespace App\Filament\Resources\Users\Pages;

use App\Filament\Resources\Users\UserResource;
use Filament\Actions\DeleteAction;
use Filament\Resources\Pages\EditRecord;

class EditUser extends EditRecord
{
    protected static string $resource = UserResource::class;

    protected function getHeaderActions(): array
    {
        return [
            DeleteAction::make(),
        ];
    }
}

The EditUser class includes a delete action in the header, allowing you to delete users from the edit page.

Understanding the Structure

Resource Organization

Filament 4 uses a modular structure for resources:

app/Filament/Resources/Users/
├── UserResource.php          # Main resource class
├── Tables/
│   └── UsersTable.php        # Table configuration
├── Schemas/
│   └── UserForm.php          # Form configuration
└── Pages/
    ├── ListUsers.php         # List page
    ├── CreateUser.php        # Create page
    └── EditUser.php          # Edit page

This separation allows for:

  • Better code organization
  • Reusability of table and form configurations
  • Easier maintenance and testing

Auto-Discovery

Filament automatically discovers resources in app/Filament/Resources and adds them to navigation. No manual registration needed!

Customization Options

Adding More Columns to Table

Add additional columns to display more information:

TextColumn::make('phone')
    ->label('Phone')
    ->searchable()
    ->toggleable()
    ->formatStateUsing(fn ($state) => $state ?: 'N/A'),  // Show "N/A" if empty

Adding Filters

Add filters to help users find specific records:

use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Filters\Filter;

// In UsersTable.php ->filters([])
->filters([
    SelectFilter::make('email_verified_at')
        ->label('Email Status')
        ->options([
            'verified' => 'Verified',
            'unverified' => 'Unverified',
        ])
        ->query(function ($query, $state) {
            if ($state['value'] === 'verified') {
                return $query->whereNotNull('email_verified_at');
            }
            if ($state['value'] === 'unverified') {
                return $query->whereNull('email_verified_at');
            }
        }),
])

Adding Bulk Actions

Add more bulk actions for managing multiple users:

use Filament\Actions\BulkAction;

->toolbarActions([
    BulkActionGroup::make([
        BulkAction::make('verify_emails')
            ->label('Verify Selected Emails')
            ->icon('heroicon-o-check-circle')
            ->action(function ($records) {
                $records->each(function ($user) {
                    $user->markEmailAsVerified();
                });
            })
            ->requiresConfirmation(),
        DeleteBulkAction::make(),
    ]),
])

Customizing Form Sections

Add more sections or reorganize fields:

Section::make('Contact Information')
    ->schema([
        TextInput::make('phone'),
        TextInput::make('company'),
    ])
    ->collapsible(),  // Makes section collapsible

Adding Custom Actions

Add custom actions to the table or form:

// In UsersTable.php
->recordActions([
    EditAction::make(),
    Action::make('impersonate')
        ->label('Impersonate')
        ->icon('heroicon-o-user')
        ->action(function ($record) {
            // Impersonation logic
        }),
])

Customizing Navigation

Control how the resource appears in navigation:

// In UserResource.php
protected static ?int $navigationSort = 1;  // Position in sidebar

protected static ?string $navigationGroup = 'User Management';  // Group resources

protected static bool $shouldRegisterNavigation = true;  // Show/hide in nav

Testing

  1. Navigate to /admin in your browser
  2. Look for "Users" in the sidebar navigation
  3. Click it to see the list of users
  4. Test the following:
    • List View: Verify all columns display correctly
    • Search: Try searching by name, email, phone, or company
    • Sorting: Click column headers to sort
    • Create: Click "New User" and fill out the form
    • Edit: Click the edit icon on a user row
    • Delete: Delete a user from the edit page
    • Password: Verify password is required on create, optional on edit

Troubleshooting

Resource Not Appearing in Navigation

  • Clear Filament cache: php artisan filament:optimize-clear
  • Clear config cache: php artisan config:clear
  • Verify the resource is in app/Filament/Resources directory
  • Check that $shouldRegisterNavigation is not set to false

Form Fields Not Saving

  • Verify fields are in the User model's $fillable array
  • Check that database columns exist (run migrations)
  • Ensure dehydrated() is not set to false unless intentional

Password Issues

  • Password field should use ->dehydrateStateUsing() to hash the password
  • On edit, password should only be dehydrated if a value is provided
  • Use ->required(fn ($livewire) => ...) to make it conditional

Namespace Errors

  • Section is in Filament\Schemas\Components\Section
  • Form components (TextInput, DateTimePicker) are in Filament\Forms\Components
  • Table columns are in Filament\Tables\Columns

Summary

You've successfully created a comprehensive User Resource in Filament 4 with:

  • ✅ Full CRUD functionality (Create, Read, Update, Delete)
  • ✅ Organized form with sections for user information and authentication
  • ✅ Searchable and sortable table columns
  • ✅ Custom fields (phone, company) integrated
  • ✅ Smart password handling (required on create, optional on edit)
  • ✅ Automatic password hashing
  • ✅ Navigation integration
  • ✅ Bulk actions for managing multiple users
  • ✅ Professional UI with toggleable columns

The User Resource is now available in your Filament admin panel and automatically appears in the navigation sidebar. Users registered through your custom registration page will be visible and manageable through this interface.

Please or to participate in this conversation.