PHPackages                             awcodes/scribble - 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. awcodes/scribble

AbandonedArchivedLibrary[Utility &amp; Helpers](/categories/utility)

awcodes/scribble
================

A Rich Text Editor plugin for Filament Forms.

v0.8.1-alpha(1y ago)485.2k↓50%8[2 issues](https://github.com/awcodes/scribble/issues)[4 PRs](https://github.com/awcodes/scribble/pulls)MITPHPPHP ^8.1CI passing

Since Feb 14Pushed 5mo ago4 watchersCompare

[ Source](https://github.com/awcodes/scribble)[ Packagist](https://packagist.org/packages/awcodes/scribble)[ Docs](https://github.com/awcodes/scribble)[ GitHub Sponsors](https://github.com/awcodes)[ RSS](/packages/awcodes-scribble/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)Dependencies (11)Versions (35)Used By (0)

Scribble
========

[](#scribble)

[![Latest Version on Packagist](https://camo.githubusercontent.com/7d46829fa73fe87890314a5e1ea244e3bca0819e1beba7a1577b79ff4a09cad2/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6177636f6465732f7363726962626c652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/awcodes/scribble)[![Total Downloads](https://camo.githubusercontent.com/c9a2aad750edf840d1a2ba67268fddbf77891b59c5d6c8a368e3ca90337a74af/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6177636f6465732f7363726962626c652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/awcodes/scribble)

[![table repeater opengraph image](https://camo.githubusercontent.com/dcdb7726b416c030257c1b9bdd65446d5d68ed235424385dfa5fdddbab93711b/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f61772d636f6465732f696d6167652f75706c6f61642f775f313230302c665f6175746f2c715f6175746f2f706c7567696e732f7363726962626c652f6177636f6465732d7363726962626c652e6a7067)](https://camo.githubusercontent.com/dcdb7726b416c030257c1b9bdd65446d5d68ed235424385dfa5fdddbab93711b/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f61772d636f6465732f696d6167652f75706c6f61642f775f313230302c665f6175746f2c715f6175746f2f706c7567696e732f7363726962626c652f6177636f6465732d7363726962626c652e6a7067)

A Rich Text Editor plugin for Filament Forms.

Installation
------------

[](#installation)

Install the package via composer

```
composer require awcodes/scribble
```

### Publishing the config

[](#publishing-the-config)

```
php artisan vendor:publish --tag="scribble-config"
```

### Publishing the translations

[](#publishing-the-translations)

```
php artisan vendor:publish --tag="scribble-translations"
```

### Setting up the plugin's styling

[](#setting-up-the-plugins-styling)

Important

If you have not set up a custom theme and are using Filament Panels follow the instructions in the [Filament Docs](https://filamentphp.com/docs/3.x/panels/themes#creating-a-custom-theme) first. The following applies to both the Panels Package and the standalone Forms package.

Import the plugin's stylesheet (if not already included) into your theme's css file.

```
@import '/vendor/awcodes/scribble/resources/css/editor.css';
@import '/vendor/awcodes/scribble/resources/css/entry.css';
```

Add the plugin's views to your `tailwind.config.js` file.

```
content: [
    './vendor/awcodes/scribble/resources/**/*{.blade.php,.svelte}',
]
```

Install the dependency.

```
npm install @simonwep/pickr
```

Rebuild your custom theme.

```
npm run build
```

Preparing your model
--------------------

[](#preparing-your-model)

Scribble stores its content as JSON data in a single column on your model. So, it is vital that you cast the column to an array or json object in your model.

```
protected $casts = [
    'content' => 'array', // or 'json'
];
```

It is also recommended to make the column a`longText` type in your migration. However, this is not required and if you know you will not need a large amount of data you can use a `text` or `mediumText` type as well. Just be aware that the content can grow rather quickly.

```
$table->longText('content')->nullable();
```

Usage
-----

[](#usage)

### Form Component

[](#form-component)

```
use Awcodes\Scribble\ScribbleEditor;

public function form(Form $form): Form
{
    return $form
        ->schema([
            ScribbleEditor::make('content')
        ])
}
```

### Infolist Entry

[](#infolist-entry)

```
use Awcodes\Scribble\ScribbleEntry;

public function infolist(Infolist $infolist): Infolist
{
    return $infolist
        ->schema([
            ScribbleEntry::make('content')
        ]);
}
```

Global Configuration
--------------------

[](#global-configuration)

In the `boot` method of a ServiceProvider you can set the default configuration for all instances of the editor with the `configureUsing` method.

```
use Awcodes\Scribble\ScribbleEditor;
use Awcodes\Scribble\Profiles\MinimalProfile;

ScribbleEditor::configureUsing(function (ScribbleEditor $scribble) {
    $scribble
        ->renderToolbar()
        ->profile(MinimalProfile::class);
});
```

Editor Profiles
---------------

[](#editor-profiles)

Manually, creating menu configurations for each instance of the editor can be cumbersome. To alleviate this, you can create a profile class that defines the tools for the bubble, suggestion, and toolbar menus. You can then apply the profile to the editor using the `profile` method. You may use either the tool identifier or the tool's class name.

```
namespace App\ScribbleProfiles;

use Awcodes\Scribble\ScribbleProfile;use Awcodes\Scribble\Tools;

class Minimal extends ScribbleProfile
{
    public static function bubbleTools(): array
    {
        return [
            Tools\Paragraph::class,
            Tools\Bold::class,
            Tools\Italic::class,
            Tools\Link::class,
            Tools\BulletList::class,
            Tools\OrderedList::class,
        ];
    }

    public static function suggestionTools(): array
    {
        return [];
    }

    public static function toolbarTools(): array
    {
        return [
            'paragraph',
            'bold',
            'italic',
            'link',
            'bullet-list',
            'ordered-list',
        ];
    }
}
```

```
use App\ScribbleProfiles\Minimal;

Scribble::configureUsing('content')
    ->profile(Mimimal::class)
```

### Generating a Profile

[](#generating-a-profile)

You can scaffold out a new profile class using the `make:scribble-profile` command and following the prompts.

```
php artisan make:scribble-profile
```

Custom Editor Styles
--------------------

[](#custom-editor-styles)

Should you need to provide styles to the editor for custom blocks or tools, you can use the `customStyles` method to provide a path to a CSS file.

```
Scribble::make('content')
    ->customStyles('path/to/custom.css')
```

Extending the Editor
--------------------

[](#extending-the-editor)

### Custom Tools

[](#custom-tools)

#### Commands

[](#commands)

Command tools are used to insert content into the editor using Tiptap commands. The `Bold` and `Italic` tools are examples of this. This is also the default tool type.

```
use Awcodes\Scribble\ScribbleTool;

class Bold extends ScribbleTool
{
    protected function setUp(): void
    {
        $this
            ->icon('scribble-bold')
            ->label('Bold')
            ->extension('bold')
            ->active(extension: 'bold')
            ->commands([
                $this->makeCommand(command: 'toggleBold'),
                // or
                ['command' => 'toggleBold', 'arguments' => null],
            ]);
    }
}
```

#### Static Blocks

[](#static-blocks)

Static Blocks are a tool type that can be used to insert a static blade view into the editor. These are useful for inserting placeholder content that can be rendered out to a different view in your HTML. For instance, a block that represents a list of FAQs that when rendered on the front-end will display a list of FAQs from the database.

`$editorView` is optional but can be useful in the case that you need to provide a custom editor view for the block. And a different rendering view for the output.

```
use Awcodes\Scribble\ScribbleTool;
use Awcodes\Scribble\Enums\ToolType;

class FaqsList extends ScribbleTool
{
    protected function setUp(): void
    {
        $this
            ->icon('heroicon-o-question-mark-circle')
            ->label('FAQs List')
            ->type(ToolType::StaticBlock)
            ->editorView('scribble-tools.faqs-list-editor')
            ->renderedView('scribble-tools.faqs-list');
    }
}
```

```
{{-- scribble.static-block-editor --}}

    This is a placeholder. FAQ list will be rendered on output.

{{-- scribble.static-block --}}

    @foreach ($faqs as $faq)

            {{ $faq->question }}
            {{ $faq->answer }}

    @endforeach

```

#### Blocks

[](#blocks)

Blocks are a tool type that interact with the editor's content through a modal form and a blade view. They can be used to insert custom content into the editor.

`$editorView` is optional but can be useful in the case that you need to provide a custom editor view for the block. And a different rendering view for the output.

##### Tool class

[](#tool-class)

```
use Awcodes\Scribble\ScribbleTool;
use Awcodes\Scribble\Enums\Alignment;
use Awcodes\Scribble\Enums\SlideDirection;
use Awcodes\Scribble\Enums\ToolType;
use Filament\Support\Enums\MaxWidth;

class Notice extends ScribbleTool
{
    protected function setUp(): void
    {
        $this
            ->icon('heroicon-o-exclamation-triangle')
            ->label('Notice')
            ->type(ToolType::Block)
            ->optionsModal(NoticeForm::class)
            ->renderedView('scribble-tools.notice');
    }
}
```

##### Modal Form

[](#modal-form)

```
use Awcodes\Scribble\Livewire\ScribbleModal;
use Awcodes\Scribble\Profiles\MinimalProfile;
use Awcodes\Scribble\ScribbleEditor;
use Filament\Forms\Components\Radio;

class NoticeForm extends ScribbleModal
{
    public ?string $header = 'Notice';

    // this should match the identifier in the tool class
    public ?string $identifier = 'notice';

    public function mount(): void
    {
        $this->form->fill([
            'color' => $this->data['color'] ?? 'info',
            'body' => $this->data['body'] ?? null,
        ]);
    }

    public function getFormFields(): array
    {
        return [
            Radio::make('color')
                ->inline()
                ->inlineLabel(false)
                ->options([
                    'info' => 'Info',
                    'success' => 'Success',
                    'warning' => 'Warning',
                    'danger' => 'Danger',
                ]),
            ScribbleEditor::make('body')
                ->profile(MinimalProfile::class)
                ->columnSpanFull(),
        ];
    }
}
```

##### Blade View

[](#blade-view)

```
 'bg-success-200 text-success-900 border-success-600',
        'danger' => 'bg-danger-200 text-danger-900 border-danger-600',
        'warning' => 'bg-warning-200 text-warning-900 border-warning-600',
        default => 'bg-info-200 text-info-900 border-info-600',
      }
    ])
>
    @php
        $icon = match($color) {
            'success' => 'heroicon-o-check-circle',
            'danger' => 'heroicon-o-exclamation-circle',
            'warning' => 'heroicon-o-exclamation-triangle',
            default => 'heroicon-o-information-circle',
        };
    @endphp

    @svg($icon, 'h-6 w-6')

    {!! scribble($body)->toHtml() !!}

```

#### Modals

[](#modals)

Modals are a tool type that interact with the editor's content through a modal form and use Tiptap commands to insert content into the editor. The `Media` and `Grid` tools are examples of this.

##### Tool class

[](#tool-class-1)

```
use Awcodes\Scribble\ScribbleTool;
use Awcodes\Scribble\Enums\ToolType;
use App\Path\To\MediaForm;

class Media extends ScribbleTool
{
    protected function setUp(): void
    {
        $this
            ->icon('heroicon-o-photograph')
            ->label('Media')
            ->type(ToolType::Modal)
            ->commands([
                $this->makeCommand(command: 'setMedia'),
            ])
            ->optionsModal(MediaForm::class);
    }
}
```

##### Modal Form

[](#modal-form-1)

```
use Awcodes\Scribble\Livewire\ScribbleModal;
use Awcodes\Scribble\Profiles\MinimalProfile;
use Awcodes\Scribble\ScribbleEditor;
use Filament\Forms\Components\Radio;

class MediaForm extends ScribbleModal
{
    public ?string $header = 'Media';

    // this should match the identifier in the tool class
    public ?string $identifier = 'media';

    public function mount(): void
    {
        $this->form->fill([
            //
        ]);
    }

    public function getFormFields(): array
    {
        return [
            //
        ];
    }
}
```

#### Events

[](#events)

You may also create tools that emit events when they are clicked. This can be useful for triggering actions in your application when a tool is clicked.

##### Tool class

[](#tool-class-2)

```
use Awcodes\Scribble\Enums\ToolType;
use Awcodes\Scribble\ScribbleTool;

class OpenRandomModal extends ScribbleTool
{
    protected function setUp(): void
    {
        $this
            ->icon('scribble-open')
            ->label('Open Random Modal')
            ->type(ToolType::Event)
            ->commands([
                $this->makeCommand(command: 'setDataFromEvent'),
            ])
            ->event(
                name: 'open-modal',
                data: [
                    'id' => 'random-modal',
                    'title' => 'Random Modal',
                ],
            );
    }
}
```

### Generating a Tool

[](#generating-a-tool)

You can scaffold out a new tool class using the `make:scribble-tool` command and following the prompts.

```
php artisan make:scribble-tool
```

Custom Tiptap Extensions
------------------------

[](#custom-tiptap-extensions)

You can also provide custom Tiptap extensions or other Tiptap native extensions to the editor. This can be useful for adding custom marks, nodes, or other extensions to the editor.

### Javascript

[](#javascript)

```
import {Highlight} from "@tiptap/extension-highlight";
import MyCustomExtension from "./MyCustomExtension";

window.scribbleExtensions = {
    highlight: Highlight,
    myCustomExtension: MyCustomExtension,
};
```

Next you will need to load your js file in your layout or view before Filament's scripts. This can be done in a way you see fit for you application.

For example, with a Filament Panel you could do something like the following:

```
use Filament\View\PanelsRenderHook;

public function panel(Panel $panel): Panel
{
    return $panel
        ->renderHook(
            name: PanelsRenderHook::STYLES_AFTER,
            hook: fn (): string => Blade::render('@vite("resources/js/scribble/extensions.js")')
        );
}
```

### PHP Parser

[](#php-parser)

In order for the content to be able to be converted to HTML, you will need to provide a PHP parser for the extension. See the [Tiptap PHP](https://github.com/ueberdosis/tiptap-php) package for more information on how to create a parser for a Tiptap extension or using an included one in their package.

### Tool

[](#tool)

Next you will need a make a tool for the extension.

```
use Awcodes\Scribble\ScribbleTool;
use Tiptap\Marks\Highlight as TiptapHighlight;

class Highlight extends ScribbleTool
{
    protected function setUp(): void
    {
        $this
            ->icon('icon-highlight')
            ->label('Highlight')
            ->commands([
                $this->makeCommand(command: 'toggleHighlight'),
            ])
            ->converterExtensions(new TiptapHighlight());
    }
}
```

Now you can register the tool and PHP parser with the plugin in a ServiceProvider's `register` method.

```
use Awcodes\Scribble\ScribbleManager;
use App\ScribbleTools\Highlight;
use Tiptap\Marks\Highlight as TiptapHighlight;

public function register(): void
{
    app(ScribbleManager::class)
        ->registerTools([
            Highlight::make(),
        ]);
}
```

Converting output
-----------------

[](#converting-output)

### With the Converter Utility class

[](#with-the-converter-utility-class)

```
use Awcodes\Scribble\Utils\Converter;

Converter::from($content)->toHtml();
Converter::from($content)->toJson();
Converter::from($content)->toText();
Converter::from($content)->toMarkdown();
Converter::from($content)->toTOC(); // Table of Contents
```

### With the helper function

[](#with-the-helper-function)

```
{!! scribble($content)->toHtml() !!}
{!! scribble($content)->toJson() !!}
{!! scribble($content)->toText() !!}
{!! scribble($content)->toMarkdown() !!}
{!! scribble($content)->toTOC() !!}
```

### Table of Contents

[](#table-of-contents)

```
use Awcodes\Scribble\Utils\Converter;

// HTML output with headings linked and wrapped in anchor tags
Converter::from($content)
    ->toHtml(
        toc: true,
        maxDepth: 3,
        wrapHeadings: true
    );

// Structured list of heading links
Converter::from($content)->toTOC();
```

### MergeTags Replacement

[](#mergetags-replacement)

If you are using Merge tags and outputting the content as HTML you can use the `mergeTagsMap` method to replace the merge tags with the appropriate values.

```
{!!
    scribble($content)->mergeTagsMap([
        'brand_phone' => '1-800-555-1234',
        'brand_email' => 'test@example.com',
    ])->toHtml()
!!}
```

Faker Utility
-------------

[](#faker-utility)

```
use Awcodes\Scribble\Utils\Faker;

Faker::make()
    ->heading(int | string | null $level = 2)
    ->emptyParagraph()
    ->paragraphs(int $count = 1, bool $withRandomLinks = false)
    ->unorderedList(int $count = 1)
    ->orderedList(int $count = 1)
    ->image(?int $width = 640, ?int $height = 480)
    ->link()
    ->details(bool $open = false)
    ->code(?string $className = null)
    ->blockquote()
    ->hr()
    ->br()
    ->grid(array $cols = [1, 1, 1])
    ->toJson();
```

Testing
-------

[](#testing)

```
composer test
```

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING](.github/CONTRIBUTING.md) for details.

Security Vulnerabilities
------------------------

[](#security-vulnerabilities)

Please review [our security policy](../../security/policy) on how to report security vulnerabilities.

Credits
-------

[](#credits)

- [Adam Weston](https://github.com/awcodes)
- [All Contributors](../../contributors)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

41

—

FairBetter than 89% of packages

Maintenance56

Moderate activity, may be stable

Popularity32

Limited adoption so far

Community18

Small or concentrated contributor base

Maturity48

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 80.6% 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 ~27 days

Total

30

Last Release

599d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/3596800?v=4)[Adam Weston](/maintainers/awcodes)[@awcodes](https://github.com/awcodes)

---

Top Contributors

[![awcodes](https://avatars.githubusercontent.com/u/3596800?v=4)](https://github.com/awcodes "awcodes (145 commits)")[![atmonshi](https://avatars.githubusercontent.com/u/1952412?v=4)](https://github.com/atmonshi "atmonshi (11 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (9 commits)")[![Log1x](https://avatars.githubusercontent.com/u/5745907?v=4)](https://github.com/Log1x "Log1x (8 commits)")[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (6 commits)")[![curder](https://avatars.githubusercontent.com/u/8327004?v=4)](https://github.com/curder "curder (1 commits)")

---

Tags

laravelawcodesscribble

###  Code Quality

TestsPest

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/awcodes-scribble/health.svg)

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

###  Alternatives

[awcodes/filament-table-repeater

A modified version of the Filament Forms Repeater to display it as a table.

262815.1k5](/packages/awcodes-filament-table-repeater)[codewithdennis/filament-select-tree

The multi-level select field enables you to make single selections from a predefined list of options that are organized into multiple levels or depths.

320392.1k17](/packages/codewithdennis-filament-select-tree)[awcodes/filament-badgeable-column

Filament Tables column to append and prepend badges.

142419.3k3](/packages/awcodes-filament-badgeable-column)[ralphjsmit/laravel-filament-components

A collection of reusable components for Filament.

10972.2k2](/packages/ralphjsmit-laravel-filament-components)[schmeits/filament-character-counter

This is a Filament character counter TextField and Textarea form field for Filament v4 and v5

33184.7k6](/packages/schmeits-filament-character-counter)[defstudio/filament-searchable-input

A searchable autocomplete input for Filament forms

3212.4k](/packages/defstudio-filament-searchable-input)

PHPackages © 2026

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