PHPackages                             yaroslawww/nova-flexible-content - 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. [Utility &amp; Helpers](/categories/utility)
4. /
5. yaroslawww/nova-flexible-content

Abandoned → [think.studio/nova-flexible-content](/?search=think.studio%2Fnova-flexible-content)Library[Utility &amp; Helpers](/categories/utility)

yaroslawww/nova-flexible-content
================================

Flexible Content &amp; Repeater Fields for Laravel Nova.

4.2.0(2y ago)0200MITPHPPHP ^8.1

Since Dec 6Pushed 2y agoCompare

[ Source](https://github.com/dev-think-one/nova-flexible-content)[ Packagist](https://packagist.org/packages/yaroslawww/nova-flexible-content)[ Docs](https://github.com/dev-think-one/nova-flexible-content)[ RSS](/packages/yaroslawww-nova-flexible-content/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (9)Versions (32)Used By (0)

Laravel Nova Flexible Content
=============================

[](#laravel-nova-flexible-content)

[![Packagist License](https://camo.githubusercontent.com/c254a311b3da6108576b6d66a42e4b202f1ce17076e49970bccf83b3f8fdae78/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f7468696e6b2e73747564696f2f6e6f76612d666c657869626c652d636f6e74656e743f636f6c6f723d253233346463373166)](https://camo.githubusercontent.com/c254a311b3da6108576b6d66a42e4b202f1ce17076e49970bccf83b3f8fdae78/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f7468696e6b2e73747564696f2f6e6f76612d666c657869626c652d636f6e74656e743f636f6c6f723d253233346463373166)[![Packagist Version](https://camo.githubusercontent.com/4eda81c1554b026c290522f54f992df81a13415fe7a7a4118c688ad375388e98/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f7468696e6b2e73747564696f2f6e6f76612d666c657869626c652d636f6e74656e74)](https://packagist.org/packages/think.studio/nova-flexible-content)[![Total Downloads](https://camo.githubusercontent.com/9d2c2c058df473adb4ca4e7386648ce39bfa9a99896a8bed1413f7e653350f9c/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f7468696e6b2e73747564696f2f6e6f76612d666c657869626c652d636f6e74656e74)](https://packagist.org/packages/think.studio/nova-flexible-content)[![Build Status](https://camo.githubusercontent.com/e72f253f975b8fdf34b765597e04175d09be322034722f536078b39b0707f47c/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f6465762d7468696e6b2d6f6e652f6e6f76612d666c657869626c652d636f6e74656e742f6261646765732f6275696c642e706e673f623d6d61696e)](https://scrutinizer-ci.com/g/dev-think-one/nova-flexible-content/build-status/main)[![Code Coverage](https://camo.githubusercontent.com/488791beb0d3de2d828348033d1840543dd54f11d9070fb09644cce329a3bb52/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f6465762d7468696e6b2d6f6e652f6e6f76612d666c657869626c652d636f6e74656e742f6261646765732f636f7665726167652e706e673f623d6d61696e)](https://scrutinizer-ci.com/g/dev-think-one/nova-flexible-content/?branch=main)[![Scrutinizer Code Quality](https://camo.githubusercontent.com/08ea4898acd7f82907074a03260411dbec366a48945017082b7b5995cc97f969/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f6465762d7468696e6b2d6f6e652f6e6f76612d666c657869626c652d636f6e74656e742f6261646765732f7175616c6974792d73636f72652e706e673f623d6d61696e)](https://scrutinizer-ci.com/g/dev-think-one/nova-flexible-content/?branch=main)

**This is fork from great package `whitecube/nova-flexible-content` but reworked without easy possibility to merge functionality to main package**

An easy &amp; complete Flexible Field for Laravel Nova, perfect for repeated and flexible field groups.

[![Laravel Nova Flexible Content in action](docs/assets/presentation.gif)](docs/assets/presentation.gif)

NovaPackageV1V1 V2V4V3 V4Installation
------------

[](#installation)

```
composer require think.studio/nova-flexible-content
# optional publish configs
php artisan vendor:publish --provider="NovaFlexibleContent\ServiceProvider" --tag="config"
```

Usage
-----

[](#usage)

A flexible field allows easy management of repeatable and orderable groups of fields. This package does not have constraints on which fields you are allowed to use within these groups. That means you can use all Laravel Nova field types, and also any community-made fields.

### Layouts

[](#layouts)

A layout represents a group of fields that can be repeated inside the Flexible field. You can add as many different layouts as you wish. If only one layout is defined the field will behave like a simple Repeater and by adding more layouts you'll obtain a Flexible Content.

#### Layout definition

[](#layout-definition)

```
namespace App\Nova\Flexible\Layouts;

use Laravel\Nova\Fields\Text;
use Laravel\Nova\Fields\Markdown;
use NovaFlexibleContent\Layouts\Layout;

class VideoLayout extends Layout
{

    // Optionally you limit count of this layout in flexible groups
    // protected int $limit = 3;

     protected function linksPreset()
    {
        return Preset::withLayouts([
            LinkLayout::class,
        ]);
    }

    /**
     * Get the fields displayed by the layout.
     */
    public function fields(): array
    {
        return [
            Text::make('Title', 'title')
                ->help('Optional'),
            FileForFlexible::make('Video', 'video')
                           ->prunable()
                           ->acceptedTypes('video/mp4')
                           ->deletable()
                           ->help('Aspect ratio 16:9. Please do not upload a large file.'),
            ImageForFlexible::make('Poster', 'poster')
                            ->prunable()
                            ->rules(['max:' . 1024 * 10])
                            ->deletable()
                            ->help('Aspect ratio 16:9.'),
            // Recursive flexible
            \NovaFlexibleContent\Flexible::make('Links', 'links')
                        ->preset($this->linksPreset())
                        ->layoutsMenuButton('Add link'),
        ];
    }
}
```

Then use this layout

```
\NovaFlexibleContent\Flexible::make('Content')
    ->useLayout(\App\Nova\Flexible\Layouts\VideoLayout::class)
    ->useLayout(\App\Nova\Flexible\Layouts\FooLayout::class)
    ->useLayout(\App\Nova\Flexible\Layouts\BarLayout::class);
```

[![Example of Flexible layout](docs/assets/example_layout.png)](docs/assets/example_layout.png)

#### Customizing display

[](#customizing-display)

You can change the display by call methods:

```
\NovaFlexibleContent\Flexible::make('Content')
    ->fullWidth()
    ->useSearchableLayoutsMenu()
    ->layoutsMenuButton('Add Video')
    ->limit(3)
    ->withGroupRemovingConfirmation('Are you sure?', 'Yes :)', 'Ahh, no');
```

[![Add something amazing](docs/assets/remove_confirmation.png)](docs/assets/remove_confirmation.png)

### Value resolver

[](#value-resolver)

By default, the field takes advantage of a **JSON column** on your model's table. In some cases, a JSON attribute is just not the way to go. For example, you could want to store the values in another table (meaning you'll be using the Flexible Content field instead of a traditional BelongsToMany or HasMany field). No worries, we've got you covered!

#### Resolver definition

[](#resolver-definition)

Each Resolver MUST implement the `NovaFlexibleContent\Value\Resolver` contract and therefore feature at least two methods: `set` and `get`.

```
namespace App\Nova\Flexible\Resolvers;

use NovaFlexibleContent\Layouts\Collections\GroupsCollection;
use NovaFlexibleContent\Layouts\Collections\LayoutsCollection;
use NovaFlexibleContent\Value\Resolver;

class WysiwygPageResolver implements Resolver
{
    public function get(mixed $resource, string $attribute, LayoutsCollection $groups): GroupsCollection
    {
        return new GroupsCollection();
    }

    public function set(mixed $resource, string $attribute, GroupsCollection $groups): string
    {
        return '';
    }
}
```

### Resolve the field

[](#resolve-the-field)

The `get` method used to resolve the field's content. It is responsible to retrieve the content from somewhere and return a collection of layouts instances (groups). For example, we could want to retrieve the values on a `blocks` table and transform them into Layout instance:

```
public function get(mixed $resource, string $attribute, LayoutsCollection $groups): GroupsCollection
{
    $blocks = $resource->blocks()->orderBy('order')->get();

    return $blocks->map(
        fn($block) => $layouts->find($block->name)
            ?->duplicate($block->id, ['value' => $block->value]);
    )->filter();
}
```

### Fill the field

[](#fill-the-field)

The `set` method is responsible for saving the Flexible's content. In our example, it should store the data in a `blocks` table:

```
public function set(mixed $resource, string $attribute, GroupsCollection $groups): string
{
    if($resource instanceof \Illuminate\Database\Eloquent\Model) {
        $resource::saved(function ($model) use ($groups) {
            // This is a quick & dirty example, syncing the models is probably a better idea.
            $model->blocks()->delete();
            $model->blocks()
                ->createMany($groups->map(function($group, $index) {
                    return [
                        'name' => $group->name(),
                        'value' => $group->toArray(),
                        'order' => $index
                    ];
                }));
        });
    }

    return '';
}
```

### Preset

[](#preset)

In addition to reusable Layout classes, you can go a step further and create `Preset` classes for your Flexible fields. These allow you to reuse your whole Flexible field anywhere you want. They also make it easier to make your Flexible fields dynamic, for example if you want to add Layouts conditionally. And last but not least, they also have the added benefit of cleaning up your Nova Resource classes, if your Flexible field has a lot of `useLayout` definitions.

#### Preset definition

[](#preset-definition)

```
namespace App\Nova\Flexible\Presets;

use NovaFlexibleContent\Layouts\Preset;

class WysiwygPagePreset extends Preset
{
    /**
     * @var array
     */
    protected array $usedLayouts = [
        \App\Nova\Flexible\Layouts\SimpleWysiwygLayout::class,
        \App\Nova\Flexible\Layouts\FooLayout::class,
    ];

    public function handle(\NovaFlexibleContent\Flexible $field)
    {
        parent::handle($field);

        $field->layoutsMenuButton('Add new block')
            ->setResolver(\App\Nova\Flexible\Resolvers\WysiwygPageResolver::class)
            ->help('Example help.');
    }
}
```

```
\NovaFlexibleContent\Flexible::make('Content')
    ->preset(\App\Nova\Flexible\Presets\WysiwygPagePreset::class);
```

### Display flexible content

[](#display-flexible-content)

The field stores its values as a single JSON string, meaning this string needs to be parsed before it can be used in your application.

```
namespace App;

use Illuminate\Database\Eloquent\Model;
use NovaFlexibleContent\Concerns\HasFlexible;

class Post extends Model
{
    use HasFlexible;

    // Collect basic `Layout` instances
    public function getCollectedFlexibleContentAttribute()
    {
        return $this->flexible('flexible-content');
    }

    // Cast to specified classes
    public function getCastedFlexibleContentAttribute()
    {
        return $this->flexible('flexible-content', [
            'wysiwyg' => \App\Nova\Flexible\Layouts\WysiwygLayout::class,
            'video' => \App\Nova\Flexible\Layouts\VideoLayout::class,
        ]);
    }
}
```

IDE Helper
----------

[](#ide-helper)

Create helper file for IDE your Flexible Layouts in application.

```
php artisan nova-flexible-content:ide-helper:layouts
# or
php artisan  nova-flexible-content:ide-helper:layouts --filename custom-file.php
```

Credits
-------

[](#credits)

- [Whitecube](https://whitecube.be/)
- [![Think Studio](https://camo.githubusercontent.com/8e541bece07d503c85a126b5294865faa00e27371048772f566a0cce8c01fd3a/68747470733a2f2f7961726f736c617777772e6769746875622e696f2f696d616765732f73706f6e736f72732f7061636b616765732f6c6f676f2d7468696e6b2d73747564696f2e706e67)](https://think.studio/)

###  Health Score

32

—

LowBetter than 72% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity10

Limited adoption so far

Community19

Small or concentrated contributor base

Maturity69

Established project with proven stability

 Bus Factor2

2 contributors hold 50%+ of commits

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 ~22 days

Recently: every ~11 days

Total

31

Last Release

953d ago

Major Versions

1.0.9 → 2.0.02021-12-28

2.1.1 → 3.0.02022-07-07

3.6.2 → 4.0.02023-08-06

PHP version history (2 changes)1.0.6PHP &gt;=8.0

3.0.0PHP ^8.1

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/23663794?v=4)[yaroslawww](/maintainers/yaroslawww)[@yaroslawww](https://github.com/yaroslawww)

---

Top Contributors

[![toonvandenbos](https://avatars.githubusercontent.com/u/5635557?v=4)](https://github.com/toonvandenbos "toonvandenbos (152 commits)")[![voidgraphics](https://avatars.githubusercontent.com/u/9298484?v=4)](https://github.com/voidgraphics "voidgraphics (94 commits)")[![yaroslawww](https://avatars.githubusercontent.com/u/23663794?v=4)](https://github.com/yaroslawww "yaroslawww (71 commits)")[![GarethSomers](https://avatars.githubusercontent.com/u/5942607?v=4)](https://github.com/GarethSomers "GarethSomers (19 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (17 commits)")[![Tarpsvo](https://avatars.githubusercontent.com/u/2018660?v=4)](https://github.com/Tarpsvo "Tarpsvo (11 commits)")[![chrisneal](https://avatars.githubusercontent.com/u/1110269?v=4)](https://github.com/chrisneal "chrisneal (6 commits)")[![harmenjanssen](https://avatars.githubusercontent.com/u/803176?v=4)](https://github.com/harmenjanssen "harmenjanssen (4 commits)")[![happyDemon](https://avatars.githubusercontent.com/u/38573?v=4)](https://github.com/happyDemon "happyDemon (4 commits)")[![slackernrrd](https://avatars.githubusercontent.com/u/1019496?v=4)](https://github.com/slackernrrd "slackernrrd (3 commits)")[![ngiraud](https://avatars.githubusercontent.com/u/12152071?v=4)](https://github.com/ngiraud "ngiraud (3 commits)")[![martijngastkemper](https://avatars.githubusercontent.com/u/250662?v=4)](https://github.com/martijngastkemper "martijngastkemper (3 commits)")[![NorthBlue333](https://avatars.githubusercontent.com/u/43409865?v=4)](https://github.com/NorthBlue333 "NorthBlue333 (2 commits)")[![jfeid](https://avatars.githubusercontent.com/u/1190470?v=4)](https://github.com/jfeid "jfeid (2 commits)")[![zareismail](https://avatars.githubusercontent.com/u/23401061?v=4)](https://github.com/zareismail "zareismail (2 commits)")[![kaareloun](https://avatars.githubusercontent.com/u/19284921?v=4)](https://github.com/kaareloun "kaareloun (2 commits)")[![LorenzoSapora](https://avatars.githubusercontent.com/u/25519274?v=4)](https://github.com/LorenzoSapora "LorenzoSapora (2 commits)")[![danieleugoletti](https://avatars.githubusercontent.com/u/115441?v=4)](https://github.com/danieleugoletti "danieleugoletti (2 commits)")[![m-triassi](https://avatars.githubusercontent.com/u/9440691?v=4)](https://github.com/m-triassi "m-triassi (2 commits)")[![bernhardh](https://avatars.githubusercontent.com/u/642292?v=4)](https://github.com/bernhardh "bernhardh (2 commits)")

---

Tags

laravelFlexiblenovaRepeater

###  Code Quality

TestsPHPUnit

Static AnalysisPsalm

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/yaroslawww-nova-flexible-content/health.svg)

```
[![Health](https://phpackages.com/badges/yaroslawww-nova-flexible-content/health.svg)](https://phpackages.com/packages/yaroslawww-nova-flexible-content)
```

###  Alternatives

[dillingham/nova-attach-many

Attach Many Nova field

2712.0M2](/packages/dillingham-nova-attach-many)[sbine/route-viewer

A Laravel Nova tool to view your registered routes.

57215.9k](/packages/sbine-route-viewer)[outl1ne/nova-simple-repeatable

A Laravel Nova simple repeatable rows field.

74356.3k](/packages/outl1ne-nova-simple-repeatable)[markwalet/nova-modal-response

A Laravel Nova asset for Modal responses on an action.

14720.0k](/packages/markwalet-nova-modal-response)[optimistdigital/nova-simple-repeatable

A Laravel Nova simple repeatable rows field.

74151.2k](/packages/optimistdigital-nova-simple-repeatable)[stepanenko3/nova-cards

A Laravel Nova info cards.

33143.0k](/packages/stepanenko3-nova-cards)

PHPackages © 2026

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