Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Editorjs Laravel Package

alaminfirdows/laravel-editorjs

A Laravel package for integrating Editor.js, the block-style content editor. It helps you store, parse, and render Editor.js JSON output in Laravel apps, making block-based content easier to use in models, controllers, and Blade views.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish

composer require alaminfirdows/laravel-editorjs
php artisan vendor:publish --tag=editorjs-config
php artisan vendor:publish --tag=editorjs-assets

Verify the service provider is auto-discovered (it is, in 2.x) and that config/editorjs.php exists — that file is the single most important thing to read first, since it defines the tool set, CDN/local asset paths, and any renderer mappings.

Where to look first

  1. config/editorjs.php — the tools array is where every behavior decision lives.
  2. The published views under resources/views/vendor/editorjs — the Blade component that boots the JS.
  3. The EditorJs cast / renderer class if you need to understand how stored JSON becomes HTML.

First use case: a blog post body

// migration
$table->json('content')->nullable();
// model
protected function casts(): array
{
    return ['content' => 'array'];
}
{{-- form --}}
<x-editorjs name="content" :value="$post->content" />
{{-- show page — new in v2.4.0 --}}
@editorJsRender($post->content)

That's the whole loop: component writes JSON blocks into a hidden input, the cast stores it as an array, and the blade directive renders it back to HTML server-side. v2.4.0's @editorJsRender() is the headline ergonomic win — it removes the need to inject the renderer service by hand in every view.

Implementation Patterns

Rendering: directive vs. service

Prefer @editorJsRender($model->content) in views. Reach for the underlying renderer service when you need the HTML as a string — for emails, PDF generation, API resources, or search indexing:

$html = app(\AlaminFirdows\LaravelEditorJs\EditorJs::class)->render($post->content);

Because the directive outputs raw HTML (it must, to render markup), treat it exactly like {!! !!} and make sure the input is trusted or sanitized.

Cache the rendered HTML, not the JSON

Editor.js JSON is cheap to store but comparatively expensive to render on every request, especially with many blocks. A common pattern:

public function getContentHtmlAttribute(): string
{
    return Cache::rememberForever(
        "post:{$this->id}:html:{$this->updated_at->timestamp}",
        fn () => app(EditorJs::class)->render($this->content ?? [])
    );
}

Keying on updated_at gives you free invalidation. If you'd rather store it, add a content_html column and refresh it in a model observer on saved.

Custom tools

Every non-default block type needs two halves: a JS tool registration (client) and a renderer mapping (server). Add the JS to config/editorjs.php under tools and register the corresponding renderer so unknown blocks don't silently render as empty strings.

Warning tool (new in v2.4.0)

Editor.js's warning/callout block is now supported out of the box. If you maintain a custom warning renderer from before this release, remove it — leaving a duplicate key can shadow the bundled implementation and produce mismatched markup or styling between old and new content.

Livewire / Alpine

The component mounts on DOMContentLoaded. In Livewire, wrap the re-init in Livewire.hook('morph.updated', ...) or dispatch a browser event, otherwise a re-rendered form leaves you with a dead editor surface. Also give each instance a unique id — two editors on one page with the default ID will fight over the same mount point.

Validation

Editor.js output is a JSON array of blocks. Validate structurally rather than with json alone:

'content' => ['nullable', 'array'],
'content.*.type' => ['required', 'string'],
'content.*.data' => ['required', 'array'],

This stops a client from posting {"evil": "payload"} that your renderer might pass through unchecked.

Gotchas and Tips

Unknown block types render as nothing — silently. This is the number one debugging trap. If an author pastes content from another instance with a tool your config doesn't know about, the block vanishes from the output with no error. Add a dev-only assertion that logs unknown type values, and consider a fallback renderer that emits a visible placeholder in non-production environments.

Asset publishing after upgrade. v2.4.0 adds the warning tool and new Laravel/PHP targets. If you have published assets, re-publish (--force) or you'll be running an older JS bundle against a newer PHP renderer. Version skew between the two halves is the cause of most "renders fine locally, broken in prod" reports.

JSON column quirks. On MySQL the json column is fine; on SQLite it's text and returns a string. The array cast handles both, but raw DB::table() queries bypass the cast and hand you a string — always go through the model.

Empty and null content. @editorJsRender(null) and an empty block array should be guarded in your views. Wrap in @if(!empty($post->content)) rather than relying on the renderer to produce a sensible empty state.

XSS. The directive does not sanitize by default. If any content originates from untrusted users, pipe the output through an HTML sanitizer (e.g. mews/purifier) before echoing, and configure an allowlist that matches the tags your tools actually emit — including the warning block's markup.

Composer constraints on Laravel 12/13. v2.4.0 widens the framework constraint and adds PHP 8.4 support. If you're pinned via composer.lock behind an older Laravel, upgrading the package alone won't move the needle — run composer update alaminfirdows/laravel-editorjs -W and expect the framework to move with it.

Renderer extension point. The config's tool map is the intended extension hook; subclassing the renderer works but is fragile across minor releases. Prefer config + a small custom renderer class registered there.

Tests are now in the package (v2.4.0). Useful as executable documentation of expected block shapes. If you write custom tools, mirror the package's test fixtures rather than inventing your own JSON structure — it's the fastest way to catch a data-shape mismatch before your users do.

Debugging a blank editor. In order: (1) confirm the assets are published and the script tag is present in the rendered page, (2) check the browser console for a missing tool module rather than a PHP error, (3) verify the hidden input name matches your validation rule. Failures here are almost always client-side, and Laravel's logs will tell you nothing.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin