PHPackages                             refineddigital/cms - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. refineddigital/cms

ActiveLibrary[Framework](/categories/framework)

refineddigital/cms
==================

Laravel CMS Core

v1.36.4(6d ago)01.1k17MITPHPPHP ^8.4CI failing

Since Aug 15Pushed 6d ago1 watchersCompare

[ Source](https://github.com/refined-digital/refined-cms-core)[ Packagist](https://packagist.org/packages/refineddigital/cms)[ RSS](/packages/refineddigital-cms/feed)WikiDiscussions master Synced today

READMEChangelogDependencies (62)Versions (381)Used By (17)

Refined CMS
===========

[](#refined-cms)

Laravel CMS core package (`refineddigital/cms`).

---

Form Builder
------------

[](#form-builder)

Admin create/edit forms are defined on each model. Instead of hand-writing deeply nested arrays, models expose a **fluent, Filament-style schema** built from typed field and layout classes.

A model defines its form by implementing `formSchema()` and returning an array of `Tab`s:

```
use RefinedDigital\CMS\Modules\Core\Forms\Tab;
use RefinedDigital\CMS\Modules\Core\Forms\Block;
use RefinedDigital\CMS\Modules\Core\Forms\Row;
use RefinedDigital\CMS\Modules\Core\Forms\Fields\TextInput;
use RefinedDigital\CMS\Modules\Core\Forms\Fields\Select;

class UserGroup extends CoreModel
{
    public function formSchema(): array
    {
        return [
            Tab::make('Content')->schema([
                Row::make([
                    Select::make('active', 'Active')->required()->options([1 => 'Yes', 0 => 'No']),
                    TextInput::make('name', 'Name')->required(),
                ]),
            ]),
        ];
    }
}
```

> The legacy `public $formFields = [...]` array (and the `formFields()` method form) still works — `formSchema()` simply takes precedence when present. Convert at your own pace, or use the converter command below.

### Layout

[](#layout)

Forms nest **Tab → (Section | Block) → Row → Field**. You only use the layers you need.

ClassPurposeFactory`Tab`A top-level tab in the editor`Tab::make('Details')``Section`A column within a tab (`left` / `right` / `bottom`)`Section::left()`, `Section::right()`, `Section::bottom()``Block`A titled card of fields`Block::make('Profile')``Row`Fields rendered side-by-side`Row::make([...])`Each layout container takes its children via `->schema([...])` (except `Row`, which takes its fields directly: `Row::make([...])`).

**Rows are how you put fields side-by-side.** Fields in the same `Row` share a line; each `Row` is a new line. A bare field passed where a row is expected is placed on its own row automatically.

#### A tab can hold one of three things

[](#a-tab-can-hold-one-of-three-things)

```
// 1. left / right / bottom sections (for split layouts like Tags)
Tab::make('Content')->schema([
    Section::left()->schema([ Block::make('Content')->schema([...]) ]),
    Section::right()->schema([ Block::make('Image')->schema([...]) ]),
]),

// 2. blocks directly (titled cards stacked down the tab)
Tab::make('User Details')->schema([
    Block::make('Profile')->schema([...]),
    Block::make('Password')->schema([...]),
]),

// 3. rows / fields directly (a single implicit block)
Tab::make('Content')->schema([
    Row::make([ TextInput::make('name')->required() ]),
]),
```

> Don't mix sections, blocks, and rows at the same level inside one tab — pick one shape.

### Fields

[](#fields)

All fields share a common fluent API and compile to the renderer's field definition.

ClassRenders as`Field`generic — set any type with `->type('...')``TextInput`text input (plus `->email()`, `->url()`, `->number()`)`Textarea`textarea`Select`dropdown (`->options([...])`)`RichEditor`rich text editor`Image`image picker`FileUpload`file picker`Password`password input```
TextInput::make('first_name', 'First Name')->required();
TextInput::make('email', 'Email')->email()->required()->note('Used for login');
Select::make('active', 'Active')->required()->options([1 => 'Yes', 0 => 'No']);
RichEditor::make('content');
Image::make('image')->hideLabel();
```

`make($name, $label = null)` — the second argument is the label. If omitted, a label is derived from the field name (`first_name` → `First Name`).

#### Field methods

[](#field-methods)

MethodEffect`->label(string)`set the field label`->type(string)`set the field type (on the generic `Field`) — e.g. a custom `userLevels` type`->required(bool = true)`mark required`->hideLabel(bool = true)`render without a visible label`->options(array)`options for selects`->note(string)`help text shown below the field (HTML allowed)`->preNote(string)`help text shown above the field`->attrs(array)`extra HTML/Vue attributes, e.g. `['v-model' => 'content.name', '@keyup' => 'updateSlug']``->extra(string, mixed)`set any other renderer key not covered above#### Custom field types

[](#custom-field-types)

The renderer supports CMS-specific types (`userLevels`, `userGroups`, `tagType`, …) that map to blade partials under `core::form.elements.*`. Use the generic `Field`with `->type()`:

```
Field::make('user_level_id', 'User Level')->type('userLevels')->required();
Field::make('groups', 'User Group')->type('userGroups');
```

### Full example (split layout with sections)

[](#full-example-split-layout-with-sections)

```
public function formSchema(): array
{
    return [
        Tab::make('Content')->schema([
            Section::left()->schema([
                Block::make('Content')->schema([
                    Row::make([
                        TextInput::make('name', 'Name')->required(),
                        Field::make('type', 'Type')->type('tagType')->required(),
                    ]),
                    RichEditor::make('content', 'Content'),
                ]),
            ]),
            Section::right()->schema([
                Block::make('Image')->schema([
                    Image::make('image', 'Image')->hideLabel(),
                ]),
            ]),
        ]),
    ];
}
```

### Converting a legacy model

[](#converting-a-legacy-model)

A console command rewrites a model's legacy `$formFields` array (or `formFields()`method) into the fluent `formSchema()`:

```
# print the generated formSchema() + the imports it needs
php artisan refinedCMS:convert-form-schema "App\RefinedCMS\Blog\Models\Post"

# or write it straight into the model file
php artisan refinedCMS:convert-form-schema "App\RefinedCMS\Blog\Models\Post" --write
```

Pass the fully-qualified model class name. Without `--write` it prints the code for you to paste; with `--write` it inserts the imports and replaces the legacy form fields in place. Review the result and run your tests.

> New modules generated with `php artisan make:module` already scaffold a `formSchema()` using this builder.

###  Health Score

63

—

FairBetter than 99% of packages

Maintenance99

Actively maintained with recent releases

Popularity17

Limited adoption so far

Community19

Small or concentrated contributor base

Maturity100

Battle-tested with a long release history

 Bus Factor1

Top contributor holds 98.7% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~7 days

Recently: every ~2 days

Total

375

Last Release

6d ago

PHP version history (5 changes)1.0.1PHP ^7.1.3

v1.10.0PHP ^8.0

v1.15.0PHP ^8.2

v1.18.0PHP ^8.3

v1.34.2PHP ^8.4

### Community

Maintainers

![](https://www.gravatar.com/avatar/68146e3ae06bc083751b69fac40d60298fea43b296b304b2b14ee17b8f0b6025?d=identicon)[refineddigital](/maintainers/refineddigital)

---

Top Contributors

[![nzmattman](https://avatars.githubusercontent.com/u/6423794?v=4)](https://github.com/nzmattman "nzmattman (771 commits)")[![matthias-batch](https://avatars.githubusercontent.com/u/143045623?v=4)](https://github.com/matthias-batch "matthias-batch (10 commits)")

### Embed Badge

![Health badge](/badges/refineddigital-cms/health.svg)

```
[![Health](https://phpackages.com/badges/refineddigital-cms/health.svg)](https://phpackages.com/packages/refineddigital-cms)
```

###  Alternatives

[unopim/unopim

UnoPim Laravel PIM

10.5k2.4k](/packages/unopim-unopim)[grumpydictator/firefly-iii

Firefly III: a personal finances manager.

23.9k69.5k](/packages/grumpydictator-firefly-iii)[bagisto/bagisto

Bagisto Laravel E-Commerce

27.6k172.1k9](/packages/bagisto-bagisto)[typicms/base

A modular multilingual CMS built with Laravel, enabling developers to manage structured content like pages, news, events, and more.

1.6k20.4k](/packages/typicms-base)[fleetbase/core-api

Core Framework and Resources for Fleetbase API

1235.9k20](/packages/fleetbase-core-api)[nasirkhan/laravel-starter

A CMS like modular Laravel starter project.

1.4k2.7k](/packages/nasirkhan-laravel-starter)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
