Filament v4 Unified Schema API: Forms &amp; Infolists | Mohamed Said        [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://msaied.com) [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles  ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

       [  ](https://github.com/EG-Mohamed)       

 [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

  [ home ](https://msaied.com)    [ articles ](https://msaied.com/articles)    Filament v4 Schema-Based Forms, Infolists, and the Unified Schema API        On this page       1. [  Why the Old Approach Felt Redundant ](#why-the-old-approach-felt-redundant)
2. [  The Unified Schema API in Practice ](#the-unified-schema-api-in-practice)
3. [  Opting Into Context-Specific Behaviour ](#opting-into-context-specific-behaviour)
4. [  Reusable Schema Fragments ](#reusable-schema-fragments)
5. [  Table Columns Are Still Separate ](#table-columns-are-still-separate)
6. [  Testing the New Schema ](#testing-the-new-schema)
7. [  Key Takeaways ](#key-takeaways)

  ![Filament v4 Schema-Based Forms, Infolists, and the Unified Schema API](https://cdn.msaied.com/297/6eb3a7aaf7148fd21116eea870bd004e.png)

  #filament   #laravel   #filament-v4   #php  

 Filament v4 Schema-Based Forms, Infolists, and the Unified Schema API 
=======================================================================

     26 Jun 2026      3 min read    ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)  Mohamed Said  

       Table of contents

1. [  01   Why the Old Approach Felt Redundant  ](#why-the-old-approach-felt-redundant)
2. [  02   The Unified Schema API in Practice  ](#the-unified-schema-api-in-practice)
3. [  03   Opting Into Context-Specific Behaviour  ](#opting-into-context-specific-behaviour)
4. [  04   Reusable Schema Fragments  ](#reusable-schema-fragments)
5. [  05   Table Columns Are Still Separate  ](#table-columns-are-still-separate)
6. [  06   Testing the New Schema  ](#testing-the-new-schema)
7. [  07   Key Takeaways  ](#key-takeaways)

 Why the Old Approach Felt Redundant
-----------------------------------

In Filament v3, a typical resource forced you to define `form()` and `infolist()` separately. If your `OrderResource` displayed twelve fields, you wrote those fields twice — once as `TextInput` components, once as `TextEntry` components. The logic was identical; only the component class differed. Senior engineers reached for shared methods or traits to paper over the duplication, but it was always a workaround.

Filament v4 addresses this at the framework level with the **unified Schema API**.

The Unified Schema API in Practice
----------------------------------

The core idea: a `Schema` is a context-aware tree of components. The same schema definition renders as an editable form inside `CreateRecord` or `EditRecord`, and as a read-only infolist inside `ViewRecord`. Components resolve their own rendering based on the active context.

```php
use Filament\Schema\Schema;
use Filament\Schema\Components\TextInput;
use Filament\Schema\Components\Select;
use Filament\Schema\Components\Section;

public function schema(Schema $schema): Schema
{
    return $schema->components([
        Section::make('Customer')
            ->schema([
                TextInput::make('name')
                    ->required()
                    ->maxLength(255),

                Select::make('status')
                    ->options(OrderStatus::class)
                    ->required(),
            ]),
    ]);
}

```

You define `schema()` once on the resource. Filament resolves `TextInput` to an editable input on the form page and to a read-only text entry on the view page — no duplication, no trait gymnastics.

### Opting Into Context-Specific Behaviour

Sometimes a field genuinely needs different behaviour per context. The API provides `->visibleOn()` and `->hiddenOn()` helpers, plus the lower-level `->when()` callback that receives the active context string (`'form'`, `'infolist'`):

```php
TextInput::make('internal_notes')
    ->hiddenOn('infolist'),

Select::make('assigned_to')
    ->options(fn () => User::pluck('name', 'id'))
    ->when(
        fn (string $context) => $context === 'form',
        fn (Select $field) => $field->searchable()->preload()
    ),

```

This keeps the single-schema principle intact while giving you an escape hatch.

### Reusable Schema Fragments

Because a schema is just a PHP value, you can extract fragments into dedicated classes and compose them:

```php
class AddressSchema
{
    public static function make(): array
    {
        return [
            TextInput::make('street')->required(),
            TextInput::make('city')->required(),
            TextInput::make('postcode')->required(),
        ];
    }
}

// Inside your resource:
Section::make('Shipping Address')
    ->schema(AddressSchema::make()),

```

This is the pattern that replaces the old `getFormSchema()` / `getInfolistSchema()` split. One fragment, used everywhere.

### Table Columns Are Still Separate

It is worth being explicit: the unified Schema API covers **forms and infolists only**. Table column definitions remain in `table()` and have not been merged. This is intentional — table columns carry sorting, searching, and bulk-action concerns that do not map cleanly onto a field/entry duality.

### Testing the New Schema

Pest assertions against Filament v4 schemas use the same `livewire()` helper, but the component class changes:

```php
use function Pest\Livewire\livewire;

it('saves an order', function () {
    livewire(\App\Filament\Resources\OrderResource\Pages\CreateOrder::class)
        ->fillForm([
            'name' => 'Acme Corp',
            'status' => 'pending',
        ])
        ->call('create')
        ->assertHasNoFormErrors();
});

```

The assertion surface is unchanged; only the underlying schema resolution differs.

Key Takeaways
-------------

- **One `schema()` method** replaces separate `form()` and `infolist()` definitions on a resource.
- Components are **context-aware** — they render as inputs or entries depending on the active page.
- Use `->when()` or `->hiddenOn()` for the rare cases where context-specific behaviour is genuinely needed.
- Extract reusable fragments into plain PHP classes; they compose cleanly into any schema.
- Table columns remain separate — the unification is scoped to forms and infolists.
- Existing Pest assertions continue to work; no test-layer rewrite is required.

 Found this useful?

          [  ](https://twitter.com/intent/tweet?url=https%3A%2F%2Fmsaied.com%2Farticles%2Ffilament-v4-schema-based-forms-infolists-and-the-unified-schema-api-1&text=Filament+v4+Schema-Based+Forms%2C+Infolists%2C+and+the+Unified+Schema+API) [  ](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fmsaied.com%2Farticles%2Ffilament-v4-schema-based-forms-infolists-and-the-unified-schema-api-1) 

 Frequently Asked Questions 
----------------------------

  3 questions  

     Q01  Do I have to rewrite all my v3 resources to use the unified Schema API when upgrading to v4?        Not immediately. Filament v4 provides compatibility shims so existing `form()` and `infolist()` definitions continue to work during migration. You can migrate resources incrementally, replacing both methods with a single `schema()` method at your own pace. 

      Q02  Can a single schema component render completely differently in form vs infolist context?        Yes. Each component resolves its own view based on the active context string. You can also use the `-&gt;when()` callback to apply arbitrary configuration — different options, validation rules, or visibility — depending on whether the schema is rendering as a form or an infolist. 

      Q03  Does the unified Schema API affect how Filament handles validation?        Validation rules are only evaluated in form context. When the same schema renders as an infolist, validation is skipped entirely. You do not need to guard your `-&gt;required()` or `-&gt;rules()` calls — Filament handles the context check internally. 

  Continue reading

 More Articles 
---------------

 [ View all    ](https://msaied.com/articles) 

 [ ![Filament v4 Migrating from v3: Breaking Changes and Refactor Patterns](https://cdn.msaied.com/700/85cddaf32751d756924869323a845563.png) filament laravel upgrade 

### Filament v4 Migrating from v3: Breaking Changes and Refactor Patterns

A practical, opinionated guide to the most impactful breaking changes when upgrading Filament v3 to v4, with c...

  ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)  Mohamed Said 

 25 Sep 2026     4 min read  

  Read    

 ](https://msaied.com/articles/filament-v4-migrating-from-v3-breaking-changes-and-refactor-patterns-1) [ ![Livewire v3 Islands, Lazy Components, and Deferred Loading in Practice](https://cdn.msaied.com/699/2667012bbe680cb54d99e4596e396547.png) livewire laravel performance 

### Livewire v3 Islands, Lazy Components, and Deferred Loading in Practice

Lazy components and deferred loading in Livewire v3 let you ship fast initial pages and hydrate expensive UI o...

  ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)  Mohamed Said 

 25 Sep 2026     1 min read  

  Read    

 ](https://msaied.com/articles/livewire-v3-islands-lazy-components-and-deferred-loading-in-practice-4) [ ![Auto-Load Generated Columns After Save with Laravel's #[Refreshes] Attribute](https://cdn.msaied.com/697/2450936023128760d64547a61ee24087.png) Laravel Eloquent Generated Columns 

### Auto-Load Generated Columns After Save with Laravel's #\[Refreshes\] Attribute

Laravel 13.33 introduced the #\[Refreshes\] PHP attribute for Eloquent models. Declare which database-computed c...

  ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)  Mohamed Said 

 23 Sep 2026     4 min read  

  Read    

 ](https://msaied.com/articles/auto-load-generated-columns-after-save-with-laravels-refreshes-attribute) 

   [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://msaied.com)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

Explore

- [Home](https://msaied.com)
- [Projects](https://msaied.com/projects)
- [Articles](https://msaied.com/articles)
- [Certificates](https://msaied.com/certificates)
- [Contact](https://msaied.com#contact-section)

Connect

- [   hello@msaied.com ](mailto:hello@msaied.com)
- [   +20 109 461 9204 ](tel:+201094619204)

© 2026 Mohamed Said. All rights reserved.

 [  ](https://github.com/EG-Mohamed) [  ](https://www.linkedin.com/in/msaiedm/) [  ](https://wa.me/201094619204) [  ](mailto:hello@msaied.com) [  ](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
