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

Draftjs Bundle Laravel Package

m6web/draftjs-bundle

m6web/draftjs-bundle is a Symfony bundle for server-side rendering of Draft.js content. It converts Draft.js raw JSON into HTML, letting rich text created with the React editor be displayed in Twig templates without relying on client-side JavaScript.

View on GitHub
Deep Wiki
Context7

Getting Started

What it is: a Symfony bundle from M6Web that takes the raw JSON produced by a Draft.js editor (blocks + entityMap + inlineStyleRanges + entityRanges) and converts it to HTML server-side. You store editor JSON in your database and render it from Twig or PHP without shipping a JS renderer to every page.

Read this first: the package is archived and marked DEPRECATED (last release Feb 2017). Before writing any code, decide which of two situations you are in:

  • Already on it — you inherit a project that uses it. Fine: treat it as frozen, pin the version, and plan a migration.
  • Starting fresh — do not adopt it. Draft.js itself was deprecated by Meta in 2023 (in favour of Lexical), and this bundle is six-plus years older than that. Render on the client with draft-js-export-html / draft-convert, or store HTML produced by a sanitizer you control.

Minimal adoption steps (legacy project):

  1. composer require m6web/draftjs-bundle — expect to have to pin old Symfony (~2.7|~3.0) and PHP (>=5.5) constraints; check composer.json in the repo, not just Packagist.
  2. Register the bundle: new M6Web\Bundle\DraftjsBundle\M6WebDraftjsBundle() in AppKernel.php, or in config/bundles.php for Symfony 4+ flex apps.
  3. Import the bundle's service config if your app doesn't autoload it, then bin/console cache:clear.
  4. First place to look: the bundle's DependencyInjection/Configuration.php. That file is the whole public API surface — it lists the block types, inline styles and entity types you can map to HTML tags/classes. Read it before reading any other file.
  5. Second place: the Twig extension (Twig/Extension/DraftjsExtension.php or similar). It tells you the exact filter name to use — the naming has changed historically, so grep getFilters() rather than guessing between draftjs, draftjs_html and draftjs_render.

First use case — an article body:

// entity
$article->setContent($request->request->get('content')); // raw JSON string from the editor
{# template — filter name per step 5 #}
<div class="article-body">
    {{ article.content|draftjs }}
</div>

The output is an HTML string you must mark |raw, or pipe through the converter service in PHP and pass the string to the view.

Implementation Patterns

Store raw JSON, render on demand. Persist the editor's ContentState.toRaw() output in a JSON/TEXT column. Never store the bundle's HTML output as the source of truth — you will want to change markup, add classes, or migrate renderers later, and re-rendering from JSON is trivial while re-parsing HTML is not.

PHP service instead of Twig when you need the result elsewhere. Inject the bundle's converter/Draftjs service into a controller, an API serializer, or a console exporter. Typical integration points:

  • API responses: an accessor or serializer normalizer that adds content_html alongside content.
  • Search indexing / email rendering / PDF generation: convert once, cache, reuse.
  • Sitemap and RSS generation: same.

Cache aggressively. Conversion is a pure function of the JSON plus the config. Key a cache entry on sha1($rawContent . $configVersion) and invalidate on config change. Article pages that render the same body on every request should not re-parse the JSON.

Extend via custom decorators, not by editing vendor code. The bundle is built around a decorator concept: one decorator per block type / inline style / entity type, each responsible for producing the tags for its node. The day-to-day extension path is:

  1. Write a class implementing the bundle's decorator interface (it will receive the node and return an HTML fragment).
  2. Register it — either through the m6web_draftjs config tree under the relevant style/block/entity key, or as a tagged service (m6web_draftjs.decorator or equivalent tag — confirm the tag name in the bundle's Resources/config/services.yml).
  3. Clear the cache. Config-driven decorator maps are compiled into the container.

This is how you handle header-two → <h2 class="...">, custom block types from a Draft.js plugin, or atomic blocks wrapping embedded videos.

Handle the entity map explicitly. Links and images live in entityMap and are referenced by index from entityRanges. Decide early what your policy is for an entity key that doesn't exist (dangling reference after partial edits), an entity type you don't support (render text, drop the node?), and IMMUTABLE entities with missing data.href.

Validate the payload on write, not on read. Reject content that doesn't match the Draft.js raw schema at the controller/FormType level. The bundle was written assuming well-formed input from a known client; a malformed blocks array or a block with a null text will surface as a runtime error on a public page.

Sanitize the output. The converter emits whatever the entity data says. Never |raw untrusted content directly:

{{ article.content|draftjs|raw }}  {# only after HTMLPurifier or equivalent #}

Configure an HTMLPurifier pass (or html-sanitizer) that allows only the tags/attributes your decorators produce, and whitelist http/https/mailto in href/src.

Gotchas and Tips

It will not run on modern PHP. The codebase predates PHP 7.2 typed properties, void return types, and Symfony 4's container changes. Expect deprecation notices at minimum and fatal errors from removed functions at worst. Budget a morning for a php -l sweep and a Symfony version bump, or accept that you are freezing the app's runtime alongside the bundle.

Unknown block types fail silently or noisily. A block whose type has no registered decorator usually renders nothing — a whole section of an article disappears with no error. Add a catch-all decorator that emits a <p> (or logs) so content loss is visible.

unstyled is not "empty". Draft.js's default block type is literally the string unstyled; a paragraph of plain text uses it. If your config only maps header-one, blockquote, etc., every normal paragraph is dropped. Map unstyled first and verify with a fixture before touching anything else.

Nested lists don't exist in the raw format. Draft.js encodes ordered/unordered lists as flat blocks with depth in the block data (set by plugins) or as consecutive unordered-list-item blocks. Producing valid <ul><li> nesting requires your own depth-tracking decorator; the bundle will not invent it.

Offsets are UTF-16 code units, not bytes and not characters. inlineStyleRanges/entityRanges offsets assume JS string indexing. Emoji and other astral-plane characters will shift every inline style and entity after them by one position, producing mis-placed <strong> tags or a link that wraps the wrong words. If your content contains emoji, test with "Hello 😀 world" before going live.

Soft newlines. Shift+Enter inside a block produces \n inside block.text with no style range. If your decorators don't convert \n to <br>, paragraphs collapse onto one line.

Config is compiled. After changing any decorator mapping, run cache:clear in every environment, including prod. A stale var/cache with the old decorator map is the most common "my change did nothing" report.

Debugging recipe. Dump the raw JSON for the failing article, shrink it to two or three blocks, and feed that fixture through the converter in a unit test (or bin/console with a scratch command). Ninety percent of issues are: missing block-type decorator, dangling entity key, or UTF-16 offset drift.

Extension points beyond decorators are thin. There's no events system worth using. If you need different behaviour for the same content type, the pragmatic move is to wrap the converter in your own service (alias/decorates the bundle's converter) that pre-processes the raw JSON — normalising block types or splitting unsupported nodes — before handing it to the bundle.

Have an exit plan. Because the JSON format is stable and well documented, migrating off the bundle is mechanical: write a DraftJsonToHtml class using draft-convert's server-side equivalent in your language of choice, or a small recursive walker of your own, then swap the service alias. Keep the raw JSON in the database and the migration stays a one-day job whenever you decide to leave — which, given the package's status, you eventually will.

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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package