nasirkhan / laravel-jodit
Jodit WYSIWYG editor integration for Laravel — works with Blade, Blade Components, and Livewire
Requires
- php: ^8.2
- illuminate/filesystem: ^11.0|^12.0
- illuminate/http: ^11.0|^12.0
- illuminate/routing: ^11.0|^12.0
- illuminate/support: ^11.0|^12.0
- illuminate/view: ^11.0|^12.0
Requires (Dev)
- orchestra/testbench: ^9.0
- phpunit/phpunit: ^11.0
Suggests
- intervention/image-laravel: Required for image resize and crop operations (^1.5)
README
A Laravel package that integrates the Jodit WYSIWYG editor via a reusable Blade component. Works seamlessly in plain Blade templates, Blade view components, and Livewire components, with a built-in server-side file browser/uploader connector.
This package is used in Laravel Starter though it is framework-agnostic and can be dropped into any Laravel app.
Features
- One Blade component —
<x-jodit::editor name="content" />covers all use cases - Livewire-ready — pass
wire-modeland the editor syncs with your Livewire component - File browser + uploads — bundled connector controller; configurable storage disk and path
- CDN assets — loads Jodit CSS/JS from unpkg; no build step required
- Fully configurable — publish the config to override defaults, CDN URLs, middleware, etc.
Requirements
- PHP 8.2+
- Laravel 11/ 12/ 13
intervention/image-laravel ^1.5— only required for image resize and crop features
Installation
composer require nasirkhan/laravel-jodit
The service provider is auto-discovered. Optionally publish the config:
php artisan vendor:publish --tag=jodit-config
Usage
1. Ensure your layout has asset stacks
Your main layout must include the stacks that the component pushes assets into.
The default stack names are after-styles (CSS) and after-scripts (JS).
Add these to your layout if they are not already present:
{{-- In <head> --}} @stack('after-styles') {{-- Before </body> --}} @stack('after-scripts')
You can change the stack names in config/jodit.php.
2. Drop the component into any form
Plain Blade
<x-jodit::editor name="content" :value="old('content', $post->content ?? '')" />
With required + placeholder
<x-jodit::editor name="content" :value="old('content')" placeholder="Write your post here…" :required="true" />
Livewire — two-way sync
<x-jodit::editor name="content" :value="$content" wire-model="content" />
Inside a Livewire component the wrapper is automatically set to wire:ignore so Livewire's DOM diffing does not destroy the editor. Changes are flushed back to the Livewire component via the JavaScript API with a 300 ms debounce.
Disable file browser
<x-jodit::editor name="excerpt" :file-browser="false" />
Custom height and connector URL
<x-jodit::editor name="body" :height="600" connector-url="{{ route('admin.jodit.connector') }}" />
Component Props
| Prop | Type | Default | Description |
|---|---|---|---|
name |
string | — | <textarea name> / form field name (required) |
id |
string | jodit_{name} |
Custom HTML id for the textarea |
value |
string | '' |
Initial HTML content |
placeholder |
string | null |
Textarea placeholder |
class |
string | '' |
Extra CSS classes on the textarea |
height |
int | config default (400) | Editor height in pixels |
file-browser |
bool | true |
Enable Jodit file browser / uploader |
connector-url |
string | auto from config | Override the connector endpoint URL |
wire-model |
string | null |
Livewire model property to keep in sync |
required |
bool | false |
Add required attribute to the textarea |
buttons |
array|string | config default | Custom toolbar button list (see Buttons Reference) |
debounce |
int | 300 |
Livewire sync debounce in milliseconds |
Passing buttons
You can pass the buttons prop as a PHP array (:buttons=), a JSON string, or a PHP-style array string:
{{-- PHP array (recommended) --}} <x-jodit::editor name="content" :buttons="['bold', 'italic', 'underline', '|', 'link', 'image']" /> {{-- PHP-style array string (no colon prefix needed) --}} <x-jodit::editor name="content" buttons="['bold', 'italic', 'underline', '|', 'link', 'image']" /> {{-- JSON string --}} <x-jodit::editor name="content" buttons='["bold", "italic", "underline", "|", "link", "image"]' />
Configuration
// config/jodit.php (after publishing) return [ // Jodit CDN URLs — change the version or point to a custom build 'cdn_css' => 'https://unpkg.com/jodit@4.1.16/es2021/jodit.min.css', 'cdn_js' => 'https://unpkg.com/jodit@4.1.16/es2021/jodit.min.js', // Asset stacks used by the Blade component 'assets' => [ 'styles_stack' => 'after-styles', 'scripts_stack' => 'after-scripts', ], // Connector route settings 'route' => [ 'enabled' => true, 'prefix' => 'jodit', 'name' => 'jodit.connector', 'middleware' => ['web', 'auth'], ], // Storage 'disk' => 'public', 'base_path' => 'jodit', // Upload constraints 'max_file_size' => 10240, // kilobytes 'allowed_mimes' => 'jpeg,jpg,png,gif,webp,svg,pdf,doc,docx,xls,xlsx,zip,txt', // Default editor options (passed directly to Jodit) 'defaults' => [ 'height' => 400, 'toolbarSticky' => true, 'toolbarButtonSize' => 'large', 'showCharsCounter' => false, 'showWordsCounter' => false, 'showXPathInStatusbar' => false, 'defaultActionOnPaste' => 'insert_clear_html', ], // Default toolbar buttons 'buttons' => [ 'bold', 'italic', 'underline', 'strikethrough', '|', 'left', 'center', 'right', '|', 'ul', 'ol', '|', 'font', 'fontsize', 'paragraph', 'brush', '|', 'link', 'image', 'video', 'file', '|', 'undo', 'redo', ], ];
Registering the connector under a custom route
If you want the connector to live under your admin prefix with your own middleware, disable the package route and register it yourself:
// config/jodit.php 'route' => [ 'enabled' => false, ],
// routes/web.php use Nasirkhan\LaravelJodit\Http\Controllers\JoditConnectorController; Route::middleware(['web', 'auth', 'role:admin']) ->prefix('admin') ->group(function () { Route::any('jodit-connector', [JoditConnectorController::class, 'handle']) ->name('backend.jodit.connector'); });
Then tell the component which route to use:
<x-jodit::editor name="content" connector-url="{{ route('backend.jodit.connector') }}" />
Or set a global default in config/jodit.php:
'route' => [ 'enabled' => false, 'name' => 'backend.jodit.connector', // used by component when no connector-url prop ],
Buttons Reference
Use any of the names below in your buttons array. Use | as a visual separator between groups.
Text Formatting
| Name | Description |
|---|---|
bold |
Bold |
italic |
Italic |
underline |
Underline |
strikethrough |
Strikethrough |
superscript |
Superscript |
subscript |
Subscript |
eraser |
Clear formatting |
Alignment
| Name | Description |
|---|---|
left |
Align left |
center |
Align centre |
right |
Align right |
justify |
Justify |
Lists & Indentation
| Name | Description |
|---|---|
ul |
Unordered list |
ol |
Ordered list |
indent |
Increase indent |
outdent |
Decrease indent |
Block / Typography
| Name | Description |
|---|---|
paragraph |
Paragraph / Headings (H1–H6) |
font |
Font family |
fontsize |
Font size |
brush |
Text colour & background colour |
classSpan |
Apply CSS class to selection |
Insert
| Name | Description |
|---|---|
link |
Insert / edit hyperlink |
image |
Insert image |
video |
Insert video (embed) |
file |
Insert file link |
table |
Insert table |
hr |
Horizontal rule |
symbols |
Special characters |
Clipboard & History
| Name | Description |
|---|---|
undo |
Undo |
redo |
Redo |
cut |
Cut |
copy |
Copy |
paste |
Paste |
selectall |
Select all |
View / Utility
| Name | Description |
|---|---|
source |
Toggle HTML source view |
fullsize |
Toggle fullscreen |
preview |
Live preview |
print |
|
find |
Find & replace |
spellcheck |
Spell check |
speech |
Speech recognition |
Separators
| Name | Description |
|---|---|
| |
Vertical separator bar |
\n |
Line break (start a new toolbar row) |
Example — compact toolbar:
'buttons' => [ 'bold', 'italic', 'underline', 'strikethrough', 'eraser', '|', 'ul', 'ol', '|', 'paragraph', 'brush', '|', 'link', 'image', '|', 'undo', 'redo', ],
File Manager Backends
The file_manager.backend config key controls which file manager is wired up when file-browser="true" (the default).
builtin (default)
Uses the package's own connector controller. No extra packages required.
// config/jodit.php 'file_manager' => [ 'backend' => 'builtin', ],
unisharp — UniSharp Laravel FileManager
Requires unisharp/laravel-filemanager to be installed and its routes published.
composer require unisharp/laravel-filemanager php artisan vendor:publish --tag=lfm_public
// config/jodit.php 'file_manager' => [ 'backend' => 'unisharp', 'unisharp' => [ 'browse_url' => '/laravel-filemanager', 'upload_url' => '/laravel-filemanager/upload', 'type' => 'Images', // 'Images' | 'Files' 'window_size' => '900x600', ], ],
When this backend is active, a File Manager toolbar button replaces Jodit's native file browser. Clicking it opens the LFM popup; selecting a file inserts it into the editor automatically.
custom
Point the editor at any server-side connector that speaks Jodit's filebrowser protocol. Pass the URL via the component's connector-url prop, or set route.name in the config:
<x-jodit::editor name="content" connector-url="{{ route('my.connector') }}" />
License
MIT