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

Tiptap Editor Bundle Laravel Package

nowo-tech/tiptap-editor-bundle

A Symfony bundle that integrates the Tiptap rich text editor into your forms and templates, giving you a clean, extensible WYSIWYG experience with sensible defaults, easy configuration, and no front-end boilerplate.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Symfony bundle, not a Laravel package — that shapes everything below. It ships a Symfony Form type that renders a Tiptap (ProseMirror) editor on top of a plain <textarea>, so the submitted value is HTML that lands in whatever column your entity maps. If you are working in Laravel, the honest first step is to decide whether you are adopting Symfony's Form component (you are not, in a Laravel app) or only borrowing the frontend: the compiled editor lives in src/Resources/public/ as a Vite IIFE bundle, and that single file is the only piece that is portable to a Laravel/Blade context.

For a Symfony app, the minimal path:

  1. composer require nowo-tech/tiptap-editor-bundle.
  2. Register it in config/bundles.php if Flex did not do it (the class lives at the package root, following the NowoTechnologies\TiptapEditorBundle\... namespace convention).
  3. Open src/DependencyInjection/Configuration.php before writing any YAML — it is the authoritative list of options, and it is short. Do the same for src/Form/ to get the real form type class name and its configureOptions() signature; do not guess it from the README.
  4. Make sure the editor assets actually reach the page. The bundle exposes a prebuilt tiptap-editor.js; check src/Resources/views/ for the Twig template that includes it and confirm whether it is pulled in automatically by the form theme or whether you must add it to your base layout.

First use case: take an existing TextareaType field on a CMS-ish entity (a Post::$body or Page::$content mapped as Types::TEXT) and swap the form type. Keep the column nullable, keep your existing validators, and verify with curl/browser devtools that the POST body contains the same HTML the editor showed. That single round trip validates the whole integration: asset loading, form rendering, textarea sync, and persistence.

Implementation Patterns

Entity and storage. Treat the field as an opaque HTML string:

#[ORM\Column(type: Types::TEXT, nullable: true)]
private ?string $body = null;

No JSON column, no ProseMirror doc serialization on your side — the bundle's contract is "HTML in the textarea." That is convenient for migrations and for reading the value in contexts that never load JS (exports, emails, search indexing), but it pushes all sanitization responsibility onto you.

Sanitization belongs on output, not only on input. Store what the editor produced, then sanitize at render time with an allow-list HTML sanitizer, and additionally sanitize on save if untrusted roles can author content. In Twig, {{ post.body|raw }} is the only way to render it, so keep |raw confined to a single, obviously-named sanitized filter/macro rather than sprinkling it through templates.

Form wiring. Instantiate the bundle's form type wherever you previously had TextareaType. Pass toolbar/editor options through the type's own options array (read configureOptions() first — it is the public extension point and the thing most likely to be BC-stable). For an app with several content shapes ("article", "comment", "short bio"), either define one form type per shape that pre-sets the options, or drive them from a single bundle config profile; the former is easier to test and avoids global config drift.

Testing. A TypeTestCase asserting the rendered widget and the presence of the bundle's asset include is cheap and catches 90% of breakage on upgrade. Add one functional test that POSTs HTML containing a link and an emphasis mark and asserts it round-trips.

Turbo / Live Components / partial re-renders. The editor binds to a textarea at page load. Any framework that swaps that DOM node without a full reload will leave an orphaned ProseMirror instance. Plan for a small stimulus/JS hook that re-initializes after the swap, and give each textarea a unique id so multiple editors on one page do not collide.

Version discipline. Zero stars and zero dependents means you are the QA. Pin an exact version in composer.json, and read CHANGELOG.md plus UPGRADING.md on every bump — the bundle's own contributor rules promise BC preservation for DI extension, compiler pass wiring, and form type options, which tells you those are the surfaces to diff.

Gotchas and Tips

  • Laravel mismatch. The Symfony Form dependency means this will not install usefully into a Laravel app. If the goal is "Tiptap in Laravel," integrate Tiptap directly (Livewire/Alpine + a textarea) and skip the bundle entirely; the ProseMirror-to-HTML contract is easy to reproduce in ~20 lines.
  • <p></p> is not empty. An emptied Tiptap editor emits an empty paragraph, so NotBlank on the raw string will pass on visually blank content. Add a normalizing callback (strip tags, trim, treat &nbsp; as blank) before validation.
  • Silent asset 404. The most common failure mode is a rendered textarea with no editor. Check the network tab for the tiptap-editor.js request first, then the console. With AssetMapper you may need to map src/Resources/public/ explicitly; with Webpack Encore, copy or reference the bundle path from your layout. Do not edit anything under src/Resources/public/build.
  • HTML length and DB limits. A rich text field inflates fast. VARCHAR(255) will truncate; use TEXT/LONGTEXT and, if you validate length, validate the stripped text, not the markup.
  • Validation of markup. Length/Regex constraints run against raw HTML and produce confusing failures. Normalize (sanitize + strip tags) in a PRE_SUBMIT/POST_SUBMIT listener or a custom constraint before measuring.
  • CSP and inline styles. ProseMirror itself is DOM-based, but check your Content-Security-Policy if the editor initially renders unstyled or a style-src violation appears — rich text builds commonly inject inline styles.
  • Multiple instances. If you render the same form type twice on a page (e.g., inline edit rows), confirm each gets a distinct id; duplicate ids break label association and can make the JS attach to the wrong node.
  • Where to extend safely. The three supported seams are the form type's options, the Twig template blocks, and the bundle configuration. Overriding a service id or the compiled JS means a manual merge on every release — prefer wrapping the form type in your own ...Type class that delegates.
  • Debugging tip. When the value posts empty, temporarily disable the JS and confirm the plain textarea still submits. That isolates "editor never initialized" from "form/persistence is broken" in one step.
  • Future-dated release. The listed last release (2026-10-25) is ahead of the current date, so pin by the actual tag in Packagist rather than trusting the metadata, and verify the tag builds against your Symfony version (the bundle targets PHP ≥8.1 and Symfony 6/7/8).
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