Skip to content

Architecture Overview

Filament CMS is built from focused packages that work together to provide a complete content management and visual editing experience.

graph TD
    subgraph "Host Application"
        A[Your Laravel App]
        B[Livewire Pages]
        C[Livewire Sections]
        D[Custom Content Blocks]
        E[Policies]
    end
    
    subgraph CorePackage["franc014/filament-cms-core"]
        F[FilamentCMSCorePlugin]
        G[PageResource]
        H[SectionResource]
        I[BlockCaster]
        J[BlockCollection]
        K[BlockData]
        L[Models]
        M[BlockRegistry]
        N[BlockSchemaExtractor]
    end
    
    subgraph LivewirePackage["franc014/filament-cms-livewire"]
        P1[Page Livewire Component]
        P2[SectionComponent]
        P3[autoHydrate]
        P4[Metatags Trait]
        P5[Blade Views]
    end
    
    subgraph VEPackage["franc014/ve-filament-cms-livewire"]
        R[VisualEditor]
        S[InlineEditForm]
        T[InteractsWithVisualEditing]
        U[CmsSourceMap]
        V[EditingMode]
    end
    
    A -->|registers| F
    A -.->|via service provider| R
    B -->|extends| P1
    C -->|extends| P2
    D -->|implements| F
    C -.->|opt-in| T
    T -->|reads| U
    I -->|casts to| J
    J -->|contains| K
    K -->|carries| U
    M -->|maps types| K
    N -->|extracts labels| K
    R -->|opens| S
    S -->|updates| L

The core package provides:

  • Pages, Sections, Posts, Menus, Categories, Components — Full CRUD via Filament resources
  • Content Builder — Block-based content editing using Filament’s Builder field
  • Media Management — Spatie Media Library integration for images and files
  • BlockCaster — Eloquent cast that transforms JSON to BlockCollection
  • BlockCollection — Typed container for resolved blocks
  • BlockData — Abstract base class for individual block data
  • GenericBlockData — Default concrete implementation
  • BlockRegistry — Maps block types to DTO classes
  • BlockSchemaExtractor — Auto-extracts field labels from ContentBlock schemas
  • ContentBlock — Interface for defining content blocks
  • PageStatus, SectionStatus — Enums for content state

The Livewire Package (filament-cms-livewire)

Section titled “The Livewire Package (filament-cms-livewire)”

The Livewire package provides:

  • Page — Abstract Livewire component for frontend pages
  • SectionComponent — Abstract base for all frontend sections
  • autoHydrate() — Automatic property mapping from BlockData to component properties
  • Metatags — Trait for injecting SEO meta tags from CMS page data
  • partials/page — Renders sections dynamically via @livewire()
  • partials/head — Renders metatags, OG tags, Twitter cards

The Visual Editing Package (ve-filament-cms-livewire)

Section titled “The Visual Editing Package (ve-filament-cms-livewire)”

The visual editing package provides:

  • VisualEditor — Floating UI that toggles editing mode and manages the slide-over panel
  • InlineEditForm — Dynamic form inside the panel that loads and saves section content
  • InteractsWithVisualEditing — Opt-in trait providing renderField(), renderImageField(), renderRepeaterContainer(), and refreshFromCMS()
  • CmsSourceMap — Immutable value object tracing HTML elements back to database records
  • EditingMode — Session-based state manager for editing mode
  • ImageUploadHandler — Moves uploaded images from temp to permanent storage
  • RepeaterSchemaBuilder — Dynamically builds repeater form schemas from block definitions
  • InitializeEditingMode — Middleware ensuring editing state is available
sequenceDiagram
    participant DB as Database
    participant BC as BlockCaster
    participant BCol as BlockCollection
    participant SC as SectionComponent
    participant AH as autoHydrate()
    participant VE as VisualEditor
    
    DB->>BC: Read content JSON
    BC->>BCol: Decode + wrap in BlockData
    BCol->>SC: mount(BlockCollection)
    SC->>AH: Map properties
    AH->>SC: $this->label = 'Hello'
    SC->>VE: Render with data-cms-source
    VE->>SC: contentUpdated event
    SC->>DB: Section::resolve()
    DB->>BC: Re-read content
    BC->>SC: Re-hydrate
graph TD
    Page["Page (slug: 'about')"] -->|page_section pivot| Section1["Section (slug: 'hero')"]
    Page -->|page_section pivot| Section2["Section (slug: 'mission')"]
    Page -->|page_section pivot| Section3["Section (slug: 'team')"]
    
    Section1 -->|BlockCaster| Block1["BlockCollection: type='hero'"]
    Section2 -->|BlockCaster| Block2["BlockCollection: type='mission'"]
    Section3 -->|BlockCaster| Block3["BlockCollection: type='team'"]
    
    Block1 -->|autoHydrate| Comp1["App\\Livewire\\Hero"]
    Block2 -->|autoHydrate| Comp2["App\\Livewire\\Mission"]
    Block3 -->|autoHydrate| Comp3["App\\Livewire\\Team"]

The core package is frontend-agnostic — it provides the admin dashboard and data layer, but knows nothing about how content is rendered on the frontend. This means:

  • Livewire projects use filament-cms-core + filament-cms-livewire
  • Inertia projects (coming soon) will use filament-cms-core + filament-cms-inertia
  • API/headless projects can use filament-cms-core alone and consume content via Eloquent or API resources

The core package handles:

  • Database schema
  • Filament resources and forms
  • Content resolution (BlockCaster, BlockCollection)
  • Media management

The frontend packages handle:

  • Page routing and rendering
  • Section component lifecycle (autoHydrate)
  • Blade views and layouts
  • Visual editing integration
  1. Separation of Concerns — Content structure (blocks) is separate from presentation (Livewire components)
  2. Content Provenance — Every rendered element knows its database origin via source maps
  3. Package Modularity — Core, frontend, and visual editing are independent packages
  4. Convention over Configuration — Section slugs match component class names, block types match Block::make() names
  5. Auto-Hydration — Property mapping happens automatically via reflection, eliminating boilerplate
  6. Schema-Extracted Labels — Field labels are read from ContentBlock schemas, not manually specified