atannex / desk-editor
A premium, first-class rich text editor for Laravel: Blade & Livewire components, uploads, mentions, math, code highlighting, tables, callouts, server-side sanitisation and a polished light/dark theme built with Tailwind CSS.
Requires
- php: ^8.2
- illuminate/console: ^12.0|^13.0
- illuminate/contracts: ^12.0|^13.0
- illuminate/filesystem: ^12.0|^13.0
- illuminate/http: ^12.0|^13.0
- illuminate/routing: ^12.0|^13.0
- illuminate/support: ^12.0|^13.0
- illuminate/validation: ^12.0|^13.0
- illuminate/view: ^12.0|^13.0
- symfony/html-sanitizer: ^7.1|^8.0
Requires (Dev)
- filament/filament: ^4.0
- larastan/larastan: ^3.0
- laravel/pint: ^1.27
- laravel/reverb: ^1.5
- orchestra/testbench: ^10.0|^11.0
- phpunit/phpunit: ^11.0|^12.0
Suggests
- livewire/livewire: Bind the editor with wire:model (^3.0|^4.0)
Provides
None
Conflicts
None
Replaces
None
README
atannex/desk-editor is a rich text editor and publishing platform for Laravel, built with TipTap 3 and ProseMirror.
It combines a configurable editor, editorial CMS, media library, collaboration tools, and a public blog. The compiled assets are included, so no frontend build step is required in the consuming Laravel application.
<x-desk-editor name="body" :value="$post->body" />
Features
- Rich editing: headings, formatting, lists, tables, columns, callouts, code blocks, math, diagrams, charts, embeds, and Markdown shortcuts.
- Media: image uploads and editing, galleries, audio/video, attachments, responsive image variants, and a searchable media library.
- Editorial tools: inline comments, tracked suggestions, drafts, find and replace, outline, preview, and export.
- Collaboration: live co-editing, presence, coloured cursors, and document-level authorization.
- CMS: articles, authors, categories, tags, translations, revision history, review workflow, edit locks, and scheduled publishing.
- Public blog: article and taxonomy pages, reader comments, RSS/Atom, sitemap, SEO metadata, and analytics.
- Integration: Blade, Livewire, Filament, and a JavaScript API.
- Accessibility and responsive UI: keyboard navigation, reduced-motion support, and mobile-friendly controls.
- Localization: English, French, Spanish, German, Brazilian Portuguese, Arabic, and Simplified Chinese.
Screenshots
Installation
composer require atannex/desk-editor php artisan desk-editor:install --migrate
The installer publishes the configuration, links public storage, and runs the package migrations.
Optional installer flags:
php artisan desk-editor:install --assets --views --lang --force
Add Laravel's scheduler to your deployment. Scheduled publishing runs every minute, and media pruning runs daily.
Use the editor
Add the component to a Blade form:
<form method="POST" action="{{ route('posts.store') }}"> @csrf <x-desk-editor name="body" :value="old('body', $post->body)" toolbar="standard" placeholder="Tell your story..." theme="auto" accent="#e11d48" min-height="24rem" :max-characters="20000" :max-words="3000" format="html" /> <button type="submit">Save</button> </form>
The editor supports html, json, and markdown formats. The toolbar accepts a preset, a comma-separated list, or an array. Use | to start a toolbar group and -> to push items to the right.
The first editor on a page injects its stylesheet and script automatically. To manage assets yourself, set inject_assets to false and include @deskEditorStyles and @deskEditorScripts.
Livewire
<x-desk-editor wire:model="content" />
The component binds the hidden input and keeps the editor itself under wire:ignore.
Comments, drafts, and collaboration
Enable document-specific features with the corresponding component attributes:
<x-desk-editor name="body" :comments="'post:'.$post->id" :collaboration="'post:'.$post->id" :autosave="['key' => 'post-'.$post->id, 'server' => true]" />
Server drafts are stored per user and can be restored across devices. Live collaboration requires Laravel broadcasting and a configured broadcaster.
Render saved content
Use the content component to render editor output for readers:
<x-desk-editor::content :html="$post->body" />
Content is sanitised before rendering. Editorial-only markup is removed, media receives responsive sources and dimensions, and supported features such as diagrams, charts, code highlighting, math, collapsibles, and image lightboxes are enhanced in the browser.
Store and validate content
Use the package cast to sanitise content when it is written:
use Atannex\DeskEditor\Casts\AsDeskContent; protected function casts(): array { return [ 'body' => AsDeskContent::class, ]; }
The cast provides helpers for common content tasks:
$post->body->words(); $post->body->readingTime(); $post->body->excerpt(160); $post->body->outline(); $post->body->images(); $post->body->mentions();
Validate content with DeskContentRule:
DeskContentRule::make() ->required() ->minWords(50) ->maxCharacters(20000) ->maxImages(10);
Validation measures visible text, so empty HTML elements do not count as content.
CMS and public blog
The article manager is available at /desk/articles. It includes:
- Draft, review, scheduled, published, and archived states
- Authoring, SEO fields, categories, tags, and translations
- Reviewer approval and change requests
- Revision comparisons and restore
- Edit locks and conflict detection
- Scheduled publishing and activity history
Enable the public blog with:
DESK_BLOG_ENABLED=true
The default URL prefix is /blog. The blog includes article and category pages, search, related posts, threaded reader comments, RSS/Atom feeds, a sitemap, structured metadata, and analytics.
Media processing
Uploads are recorded in the media library at /desk/media.
- Images can be converted into responsive WebP variants.
- Video and audio metadata and posters are generated with ffprobe.
- Video transcoding uses ffmpeg and can run through queued jobs.
- Unused media can be reviewed with a dry run:
php artisan desk-editor:prune-media --dry-run
SVG uploads are disabled by default. Increase PHP's upload_max_filesize and post_max_size when handling large media files.
Filament
Use the package field in a Filament form:
use Atannex\DeskEditor\Filament\DeskEditor; DeskEditor::make('body') ->toolbar('standard') ->maxWords(3000) ->document(fn ($record) => $record ? 'post:'.$record->id : null) ->columnSpanFull();
The field stores sanitised HTML. Laravel Nova is not supported.
JavaScript API
Get an editor instance and interact with it programmatically:
const desk = DeskEditor.get(document.querySelector('.desk-editor')); await desk.ready; desk.getHTML(); desk.getMarkdown(); desk.setContent('# Hello', { format: 'markdown' }); desk.toggleSuggestionMode(); desk.toggleComments(); desk.toggleFocusMode(); desk.on('change', ({ html, words }) => { // Handle editor changes. });
The package also emits DOM events, including desk-editor:ready, desk-editor:change, desk-editor:upload, and desk-editor:peers. Editors added dynamically are initialised automatically. To disable automatic initialisation, set window.DeskEditorAutoInit = false and call DeskEditor.init(container).
Configuration and security
theme="auto"follows the page theme and operating-system preference;lightanddarkare also supported.- Use
accentto customise the editor's accent colour. - The editor uses an HTML allow-list sanitiser.
- Iframes are restricted to HTTPS allow-listed hosts and sandboxed.
- Link previews validate destinations and redirects to protect against SSRF.
- Uploads are MIME-checked, stored under random names, and scoped to their owners.
- CMS routes are protected by authorization gates; configure the package gates to match your application's roles and permissions.
Development
Regenerate the README screenshots from a running workbench with node scripts/screenshots.mjs (environment in workbench/collab.env.sh).
composer install npm install npm run build composer test npm test npm run test:e2e composer analyse
The repository includes a Testbench workbench application for trying the editor, CMS, and blog locally.
License
MIT © Atannex.
Bundled TipTap, ProseMirror, Yjs, KaTeX, Mermaid, Chart.js, highlight.js, and Lucide icons are distributed under their respective MIT, ISC, or Apache licenses.












