Filament v4 Unified Schema API: Forms &amp; Infolists | Mohamed Said       [Skip to content](#main)  [ ![](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png) Mohamed SaidLaravel Backend Engineer ](https://www.msaied.com/public) - [Home](https://www.msaied.com/public)
- [Projects](https://www.msaied.com/public/projects)
- [Articles](https://www.msaied.com/public/articles)
- [Certificates](https://www.msaied.com/public/certificates)
- [About](https://www.msaied.com/public#about)

           [  Contact](https://www.msaied.com/public#contact) Menu 

Menu
----

Close 

 - [HomeStart here](https://www.msaied.com/public)
- [ProjectsCase studies](https://www.msaied.com/public/projects)
- [ArticlesEngineering notes](https://www.msaied.com/public/articles)
- [CertificatesCredentials](https://www.msaied.com/public/certificates)
- [AboutHow I work](https://www.msaied.com/public#about)
- [ContactGet in touch](https://www.msaied.com/public#contact)

  [Start a conversation](https://www.msaied.com/public#contact) [WhatsApp](https://wa.me/201094619204) [Email](mailto:hello@msaied.com) 

 1. [Home](https://www.msaied.com/public)
2. /
3. [Articles](https://www.msaied.com/public/articles)
4. /
5. Filament v4 Schema-Based Forms, Infolists, and the Unified Schema API

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

 Filament v4 replaces scattered form and infolist definitions with a single Schema API. Learn how unified schemas reduce duplication, how field resolution works under the hood, and how to migrate real resource classes cleanly.

 ![](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp) [Mohamed Said](https://www.msaied.com/public#person) Published 14 Jul 2026 · Updated 14 Jul 2026 · 3 min read

ShareCopy linkCopied

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

  On this page +1. [Why Filament v4 Introduced a Unified Schema API](#why-filament-v4-introduced-a-unified-schema-api)
2. [The Core Concept: Schema as a First-Class Object](#the-core-concept-codeschemacode-as-a-first-class-object)
3. [Field Resolution and State Hydration](#field-resolution-and-state-hydration)
4. [Practical Migration Pattern for Existing Resources](#practical-migration-pattern-for-existing-resources)
5. [Key Takeaways](#key-takeaways)

 Why Filament v4 Introduced a Unified Schema API
-----------------------------------------------

In Filament v3, `form(Form $form)` and `infolist(Infolist $infolist)` lived in completely separate methods with separate component trees. Sharing layout logic between them meant either duplicating field arrays or reaching for abstract helper methods that felt bolted on.

Filament v4 solves this with the **Schema API**: a single component tree that both the form renderer and the infolist renderer consume. Fields declare how they behave in each context, and the framework resolves the correct representation at render time.

---

The Core Concept: `Schema` as a First-Class Object
--------------------------------------------------

Instead of returning a configured `Form` or `Infolist`, your resource now returns a `Schema`:

```php
use Filament\Schema\Schema;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Select;
use Filament\Infolists\Components\TextEntry;

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

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

```

The `form()` and `infolist()` methods on the resource can now delegate to `schema()`, or you can override them individually when the display context genuinely differs.

```php
public static function form(Form $form): Form
{
    return $form->schema(static::schema(new Schema($form->getLivewire()))->getComponents());
}

public static function infolist(Infolist $infolist): Infolist
{
    return $infolist->schema([
        TextEntry::make('name'),
        TextEntry::make('status')
            ->badge()
            ->color(fn (Status $state) => $state->color()),
    ]);
}

```

When the infolist needs richer display logic (badges, icons, formatted values), you override it explicitly. When it doesn't, the shared schema is enough.

---

Field Resolution and State Hydration
------------------------------------

Under the hood, `Schema` components implement `HasState`. During form hydration, each component calls `$this->getState()` which reads from the Livewire component's `data` array. During infolist rendering, the same component tree reads from the bound `$record` model.

This dual-context resolution is why a `TextInput` can render as an `` in a form and as plain text in an infolist without you writing two components.

```php
// A custom field that behaves correctly in both contexts
class MoneyInput extends Field
{
    protected function setUp(): void
    {
        parent::setUp();

        $this->formatStateUsing(fn ($state) => $state ? number_format($state / 100, 2) : null);
        $this->dehydrateStateUsing(fn ($state) => (int) (floatval(str_replace(',', '', $state)) * 100));
    }
}

```

`formatStateUsing` runs in both form and infolist contexts. `dehydrateStateUsing` only fires when the form is submitted — the infolist never calls it.

---

Practical Migration Pattern for Existing Resources
--------------------------------------------------

The safest migration path is incremental:

1. **Extract shared layout** into a static `baseSchema()` method returning a plain array.
2. **Keep `form()` and `infolist()` separate** until you've verified parity.
3. **Collapse to `schema()`** once the infolist no longer needs custom entries.

```php
private static function baseSchema(): array
{
    return [
        TextInput::make('title')->required(),
        TextInput::make('slug')->unique(ignoreRecord: true),
    ];
}

public static function form(Form $form): Form
{
    return $form->schema([
        ...static::baseSchema(),
        FileUpload::make('cover_image'),
    ]);
}

public static function infolist(Infolist $infolist): Infolist
{
    return $infolist->schema([
        TextEntry::make('title'),
        TextEntry::make('slug'),
        ImageEntry::make('cover_image'),
    ]);
}

```

This pattern keeps diffs reviewable and avoids a big-bang rewrite.

---

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

- The unified `Schema` API eliminates the primary source of duplication between forms and infolists in Filament v4.
- `formatStateUsing` and `dehydrateStateUsing` are context-aware; only dehydration is skipped in infolist rendering.
- Custom fields built on `Field` work in both contexts without modification if they respect the state lifecycle.
- Incremental migration via a shared `baseSchema()` array is safer than rewriting resources all at once.
- Override `form()` or `infolist()` explicitly when display requirements genuinely diverge — the unified API is a tool, not a mandate.

- [filament](https://www.msaied.com/public/articles?search=filament)
- [laravel](https://www.msaied.com/public/articles?search=laravel)
- [filament-v4](https://www.msaied.com/public/articles?search=filament-v4)
- [admin-panel](https://www.msaied.com/public/articles?search=admin-panel)

 Frequently asked questions 
---------------------------

  Can I still define separate form() and infolist() methods in Filament v4?Yes. The unified Schema API is additive. You can define a shared schema() method and delegate from form() and infolist(), or keep them fully separate when the display contexts differ significantly.

   Does a custom Field component need changes to work in both form and infolist contexts?Not if it follows the standard state lifecycle. formatStateUsing runs in both contexts; dehydrateStateUsing is only called on form submission. Fields that respect these hooks work in infolists without modification.

   How does Filament v4 know whether to render a TextInput as an input or as plain text?The Schema renderer checks the rendering context — form or infolist — and calls the appropriate view. Each component ships with both a form view and an infolist view; the framework selects the correct one based on which container is active.

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

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

[About](https://www.msaied.com/public#about) [GitHub ↗](https://github.com/EG-Mohamed) [LinkedIn ↗](https://www.linkedin.com/in/msaiedm/) [WhatsApp ↗](https://wa.me/201094619204) [Email Address ↗](mailto:hello@msaied.com) [My CV ↗](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)  

   [Previous articleLaravel Quota: Enforce Usage Budgets for Calendar Periods in Laravel](https://www.msaied.com/public/articles/laravel-quota-enforce-usage-budgets-for-calendar-periods-in-laravel) [Next articleFrankenPHP, OPcache JIT, and Preloading: Squeezing Real Throughput from Laravel](https://www.msaied.com/public/articles/frankenphp-opcache-jit-and-preloading-squeezing-real-throughput-from-laravel-2)  

   On this page
-------------

1. [Why Filament v4 Introduced a Unified Schema API](#why-filament-v4-introduced-a-unified-schema-api)
2. [The Core Concept: Schema as a First-Class Object](#the-core-concept-codeschemacode-as-a-first-class-object)
3. [Field Resolution and State Hydration](#field-resolution-and-state-hydration)
4. [Practical Migration Pattern for Existing Resources](#practical-migration-pattern-for-existing-resources)
5. [Key Takeaways](#key-takeaways)

 ###  Have a technical challenge?

 Tell me what you’re building. I reply within two working days.

[Start a conversation](https://www.msaied.com/public#contact) 

   Related articles
-----------------

 [ ![](https://cdn.msaied.com/740/cce86edc21eddcbdd2f2454fadaf9c70.png)  · 3 min read### The Pipeline Pattern in Laravel: Custom Pipelines Beyond Middleware

5 Oct 2026 ](https://www.msaied.com/public/articles/the-pipeline-pattern-in-laravel-custom-pipelines-beyond-middleware-1) [ ![](https://cdn.msaied.com/739/2d6897fdcdcf090613f96f72a64b8a78.png)  · 4 min read### MySQL Full-Text Search in Laravel: Indexes, Relevance Scoring, and Boolean Mode

4 Oct 2026 ](https://www.msaied.com/public/articles/mysql-full-text-search-in-laravel-indexes-relevance-scoring-and-boolean-mode) [ ![](https://cdn.msaied.com/738/073696a3fefe18bec825beec5ac658f5.png)  · 4 min read### Laravel Queue Rate-Limited Middleware: Throttling Jobs Without Losing Work

4 Oct 2026 ](https://www.msaied.com/public/articles/laravel-queue-rate-limited-middleware-throttling-jobs-without-losing-work) 

  Have a technical challenge?
----------------------------

Tell me what you’re building. I reply within two working days.

 [Discuss your project ↗](https://www.msaied.com/public#contact) 

  © 2026 Mohamed Said · Built with Laravel, meant to last.Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

 - [Home](https://www.msaied.com/public)
- [Articles](https://www.msaied.com/public/articles)
- [Certificates](https://www.msaied.com/public/certificates)
- [GitHub](https://github.com/EG-Mohamed)
- [LinkedIn](https://www.linkedin.com/in/msaiedm/)
- [WhatsApp](https://wa.me/201094619204)
- [Email Address](mailto:hello@msaied.com)
- [My CV](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
- [Sitemap](https://www.msaied.com/public/sitemap.xml)
