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 Php Laravel Package

ueberdosis/tiptap-php

Convert Tiptap content between HTML and JSON in PHP. Set content, get documents, JSON, or HTML, sanitize and modify content, and extend it with custom nodes and marks. A PHP port of the Tiptap editor API.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

What the package is: a server-side PHP implementation of the Tiptap/ProseMirror document model — it parses HTML into Tiptap-compatible JSON, serializes JSON back to HTML, flattens to plain text, sanitizes, and allows tree traversal/mutation via descendants(). It is a content-transformation library, not a framework component.

Architecture fit: Strong where the application has a Tiptap editor on the front end and needs PHP-side handling of the same document format. The natural seams are: (1) render-on-write — convert stored JSON to HTML at publish/save time so public pages and emails don't depend on a PHP renderer per request; (2) sanitize-on-write — enforce an allowlist at the persistence boundary as defense-in-depth alongside client-side sanitization; (3) derive read models — getText() for search indices, excerpts, notification previews, and RSS; (4) content ops — batch migrations and attribute normalization over existing documents. It is a poor fit as a general-purpose HTML sanitizer for untrusted third-party HTML, and a poor fit if the HTML→JSON direction is expected to be byte-faithful.

Integration feasibility: Low friction. Composer install, no Laravel service provider required, no database or queue coupling, MIT license, no runtime services for the default path. The API deliberately mirrors the JavaScript package (new Editor([...])->setContent()->getHTML()), so developers already fluent in Tiptap need minimal translation. Extension configuration (StarterKit, per-node options, HTMLAttributes) maps onto the JS mental model, which reduces design ambiguity in code review.

Technical risk — the dominant one is schema parity. The README itself states the implementation "doesn't fully adhere to the ProseMirror schema" (e.g. marks in CodeBlock). Every custom node or mark in the JS editor must be reimplemented in PHP (Node/Mark subclasses with parseHTML, renderHTML, priorities). Any drift produces silent divergence: content authored in the editor that the PHP side does not recognize can be dropped or flattened during a sanitize-then-persist cycle. This is a data-loss risk, not a rendering bug, and it must be treated as such in the design. Priority ordering ($priority, default 100) also becomes an application-level concern once custom nodes overlap with built-ins (TaskItem vs ListItem is the documented precedent).

Secondary risks:

  • Sanitization is not a silver bullet. sanitize() normalizes against the loaded extension set — it removes what the schema does not know about, which is exactly the failure mode above if the extension set is incomplete. Treat it as schema conformance, not as a security boundary by itself.
  • Recent release churn is security-adjacent. 2.2.0 "hardened rendering and merging of attributes," 2.1.1 fixed a TypeError on non-string link hrefs, 2.1.2 fixed non-scalar attribute crashes. These are correctness/robustness fixes on the serialization path — good signs of maintenance, but evidence that attribute handling has been a live edge. Pin versions and re-run a parity corpus on upgrade.
  • Node.js dependency paths. CodeBlockShiki shells out to Shiki via spatie/shiki-php and requires the shiki npm package. That converts a pure-PHP library into something with a Node runtime on the server. Avoid unless there is a hard requirement; CodeBlockHighlight (highlight.php) is the in-process alternative.
  • Round-trip fidelity. HTML→JSON is a normalization, not a preservation. Never let a PHP-side parse overwrite the canonical stored document; write HTML/text derivatives and keep JSON as source of truth.

Key questions for the team:

  1. Which direction(s) do we actually need — JSON→HTML, HTML→JSON, text extraction, sanitize, or mutation? Direction determines most of the risk.
  2. What is our canonical storage format, and are we prepared to guarantee JSON as source of truth with HTML as a derived artifact?
  3. Do we have custom Tiptap nodes/marks today? If yes, who owns the JS↔PHP parity contract, and where is it tested?
  4. Is server-side rendering required at request time, or can it be precomputed at publish?
  5. What is our XSS posture today, and is this package additive hardening or a replacement for an existing sanitizer (e.g. HTMLPurifier)? Replacement is the wrong call.
  6. Are we willing to pin the package and gate upgrades on a regression corpus?
  7. Do we need Node.js in production for Shiki? If not, is that dependency excluded by policy?

Integration Approach

Stack fit: Clean. Plain PHP + Composer, MIT, no Laravel coupling and no global state — easy to wrap in a small application service (e.g. ContentRenderer) and bind via the container so the editor configuration lives in one place (config/tiptap.php) rather than being reconstructed at call sites. Keep it behind that seam so the package can be swapped or upgraded without touching controllers, Blade templates, or jobs.

Suggested placement:

  • App\Support\Content\TiptapRenderer — single extension registry, one place where StarterKit and any custom nodes are declared.
  • A RenderContentHtml action invoked from model observers/jobs at publish time, persisting content_html and content_text.
  • A SanitizeContent action at the persistence boundary for editor saves.
  • Store the raw Tiptap JSON untouched in a dedicated column; derived columns are regenerable.

Migration path: Incremental and reversible. Start as a read-only derivative generator against an existing corpus — no writes, no schema changes, no user-visible behavior. Compare output against the current renderer for a sample of real documents and quantify divergence before committing to anything. Only after parity is acceptable do you move sanitize-on-write and any mutation workflows into the critical path.

Compatibility considerations:

  • Pin the package to a specific minor (^2.2) and record the resolved version; attribute-handling behavior has changed across 2.1.x→2.2.0.
  • Version-lock the pairing of JS Tiptap editor version and PHP package version in documentation — the two evolve independently and the PHP side lags.
  • Verify PHP version support against the runtime matrix and check the CI badge/tests workflow for the supported matrix rather than assuming.
  • Confirm the extension set covers every node/mark the editor can produce today, including Details (added 2.1.0), Image width/height attributes (2.1.0), and link mark edge cases. Anything not covered is a candidate for silent drop.
  • Audit the "0 dependents" signal — treat the star count and download badge as ecosystem evidence, but independently verify by searching Packagist/enterprise usage rather than relying on the dependents metric.

Sequencing:

  1. Spike (days): render and sanitize a representative sample of production documents; diff JSON→HTML→JSON round trips and record every lossy case.
  2. Parity harness: build a fixture corpus of real documents (not synthetic) and a PHPUnit test asserting PHP output matches expected HTML for each. This harness becomes the upgrade gate and the onboarding artifact.
  3. Extension audit: enumerate editor extensions, mark each as supported / needs custom PHP class / deliberately out of scope. Assign owners for custom nodes.
  4. Read path first: wire HTML + plain-text generation at write time behind a feature flag; ship to a small slice of content.
  5. Write path second: enable sanitize-on-persist only after (2) proves no legitimate content is dropped. Log every removal with the node type so losses are observable, not silent.
  6. Cleanup: remove legacy renderers, document the parity contract, and add an upgrade runbook.

Operational Impact

Maintenance: Low ongoing cost, but not zero. Single-vendor stewardship (ueberdosis, the company behind Tiptap) is strategically favorable — this package exists to support their editor product, so it is unlikely to be abandoned — but it is a companion library, not the flagship, so PHP-side features will lag the JS feature set and requests for parity may be deprioritized. Plan for the possibility of contributing upstream patches for custom-node gaps. MIT license removes legal friction; the bus factor is the vendor's priorities, not a legal or licensing risk.

Support: Community-driven via GitHub issues and Discord; no commercial SLA implied by the MIT package. Internal ownership matters more than vendor support: name one engineer as the parity owner so that editor-configuration changes trigger a PHP-side review. Budget a small recurring allowance for upstream fixes, particularly around attribute serialization where recent history shows activity.

Scaling: CPU-bound DOM parsing and serialization in PHP, roughly linear in document size, with per-document cost driven by extension count and node depth. This scales acceptably for write-time rendering and poorly for read-time rendering of high-traffic pages. Design accordingly: render once at publish, cache the HTML in a column or object store, and never invoke the editor on a hot read path. For backfills over large corpora, run through queued jobs with bounded concurrency, chunked by document, and expect to throttle — this is a batch CPU workload, not an I/O workload. The Shiki path multiplies cost per code block by spawning a Node process; if used, restrict it to publish-time and batch it, or exclude it entirely.

Failure modes to instrument:

  • Silent content loss — an unrecognized node/mark dropped during sanitize-and-persist. Mitigation: never overwrite canonical JSON with a PHP-rendered parse; log removed node types with content ID; alert on nonzero removal rates after deploy.
  • Unhandled type errors on malformed attributes — the class of bug fixed in 2.1.1/2.1.2. Mitigation: pin versions, wrap rendering in try/catch at the job level, and fail closed to "render unprocessed / mark for review" rather than writing empty HTML.
  • XSS via rendered HTML — if the extension set permits attributes (hrefs, styles) that the front end assumed were filtered, server output may reintroduce them. Mitigation: keep existing output escaping in templates, treat this package as conformance not sole sanitization, and add a regression test with known payloads (javascript: hrefs, event-handler attributes, nested script).
  • Version drift after an editor upgrade — new JS node type shipped, PHP side unaware, published HTML loses that block. Mitigation: parity harness run in CI on every editor-version bump; make the JS upgrade PR require a green parity run.
  • Node/runtime absence — if Shiki is enabled and Node is missing or the npm package is not installed in the deploy image, publish jobs fail. Mitigation: prefer CodeBlockHighlight, or gate Shiki behind a health check and fall back to plain code blocks.
  • Memory pressure on very large documents — deep or pathological trees under concurrent queue workers. Mitigation: per-document size guard and a documented ceiling.

Ramp-up: Estimated 2–4 days for a senior Laravel engineer to be productive and 1–2 weeks to be genuinely safe, with most of that time spent on the parity harness rather than the library itself. The API mirrors the JS package, so the conceptual load is low for anyone who has touched Tiptap; the real learning is the extension model (parseHTML/renderHTML/getAttrs/priority semantics) and the discipline of keeping JS and PHP extension sets in lockstep. Two artifacts accelerate onboarding more than any documentation: the fixture corpus with expected outputs, and a one-page "how to add a custom node to both sides" runbook.

Bottom line for a TPM: inexpensive, low-coupling, high-leverage for the specific job of rendering and indexing Tiptap content server-side. The opportunity is real (content-heavy Laravel apps routinely need it), but the score/opportunity asymmetry in the assessment reflects that the library is small-surface and single-maintainer — the risk is not adoption cost, it is parity drift and silent data loss. Fund the parity harness and the write-time rendering design before funding the integration.

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.
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log