tonysm/rich-text-laravel
Rich Text Laravel brings Action Text-style rich content to Laravel. It integrates the Trix editor, stores HTML with attachments, and provides helpers for sanitizing, rendering, and managing rich text fields on Eloquent models.
Architecture fit
tonysm/rich-text-laravel is a Laravel-native package that ports the Rails Action Text pattern into Eloquent/Blade idioms. It introduces a dedicated rich_texts table (HTML body + polymorphic record relation), a RichText model, a HasRichText trait for attaching rich bodies to any Eloquent model, a Blade <x-trix-input> component wrapping the Trix editor, and a renderer/sanitizer pipeline for output. For applications already built on server-rendered Blade (or Livewire/Alpine) with a classic relational schema, this is a strong architectural fit: content is stored as HTML, ownership is modeled relationally, and rendering stays inside Laravel's view layer. It fits poorly in API-only or headless architectures where the frontend is a separate SPA — there the package's Blade/Trix assumptions become friction and a headless editor (TipTap, Lexical, ProseMirror) paired with a JSON document store is usually a better shape.
Integration feasibility
Feasibility is high for a Laravel team. The install path is conventional: Composer require, publish config, run migrations for rich_texts (and attachment tables), add the trait to target models, invoke the Blade component in forms, and expose the sanitized body in views. No queue, no external service, no proprietary SaaS dependency is required — Trix assets can be published or imported via your existing asset pipeline. The main integration effort is not wiring the package but defining content policy: which models get rich bodies, what HTML is allowed, how attachments are stored and authorized, and how the rendered output is cached or escaped. A one-to-two-day proof of concept on a single model is realistic; a production rollout across many models and legacy content is a multi-sprint effort.
Technical risk
{!! !!} bypass is a stored-XSS vector. This must be treated as a security-reviewed component, not a convenience.rich_texts polymorphic design is sound but opinionated; retrofitting existing body/description columns requires a backfill and a dual-read/dual-write transition.Key questions
Stack fit Best fit: Laravel 10+/PHP 8.1+, Blade views, Livewire or Alpine for interactivity, MySQL/PostgreSQL/SQLite, and standard Laravel filesystem disks for attachments. Also a reasonable fit for Inertia apps that still render forms server-side. Poor fit for API-first/JSON-document architectures, mobile clients needing structured content, or headless CMS patterns where the editor must be swapped independently of the backend.
Migration path
HasRichText to a pilot model; define accessor/renderer conventions so no view ever emits unsanitized HTML.Compatibility
Validate explicitly against: supported Laravel major(s) and PHP versions, the DB drivers in use (polymorphic morphs behavior and JSON/index support differ), your asset bundler (Vite vs. mix vs. CDN), Livewire version if used (Trix + Livewire requires careful state sync and wire:ignore handling), and any existing HTML sanitizer or CSP middleware that could conflict with Trix's inline styles and iframe-free output. Confirm the editor's browser support matrix meets your baseline.
Sequencing Security and content policy must land before UI rollout: define the sanitizer allowlist, output-escaping convention, and CSP rules first, then wire the editor. Attachments should follow sanitization but precede broad rollout, since retrofitting storage paths and authorization after content exists is costly. Treat the pilot → backfill → flag-gated rollout → decommission sequence as the critical path, and do not parallelize the sanitizer policy decision with editor UX work — the former constrains the latter.
Maintenance Ongoing cost is modest but non-zero: periodic Composer updates, sanitizer allowlist reviews, Trix version bumps, and Laravel upgrade compatibility checks each major cycle. Because upstream is small, budget a recurring line item for "monitor and possibly patch" rather than assuming passive upkeep. Pin to exact versions, commit the lockfile, and run the package's tests in CI if available. Maintain an internal fork or patch set only if you hit a blocking bug — otherwise upstream contributions are cheaper than divergence.
Support Community support via GitHub issues and PRs; there is no commercial SLA. Internal ownership should be explicit: one engineer accountable for the editor integration and one for content security review. Document the sanitizer configuration as an owned artifact with a change log, because it is a security control, not a style preference.
Scaling The data model scales well for typical CMS/comment/description workloads — HTML bodies in a side table, indexed by polymorphic owner, avoids bloating primary tables. Watch three pressure points: (1) sanitization cost — rendering untrusted HTML on every request is CPU-work; cache rendered output or precompute sanitized bodies on write; (2) attachment volume — move to object storage with CDN delivery early rather than growing local disk; (3) search — HTML blobs are poor full-text targets; plan to derive plain-text excerpts or feed a search index at write time.
Failure modes
Ramp-up Low for Laravel developers: the package reads as idiomatic Laravel (trait, model, Blade component), so a mid-level engineer can be productive in a day. The real learning curve is organizational, not technical — everyone touching content must internalize the sanitize-on-output rule and attachment authorization model. Allocate a short internal enablement session covering the trait/component pattern, the sanitizer policy, and the "never emit raw HTML" rule; that hour prevents the most likely incident.
How can I help you explore Laravel packages today?