kamikx/laravel-jodit

Jodit WYSIWYG editor integration for Laravel — works with Blade, Blade components, Livewire components and Filament forms

Maintainers

Package info

github.com/KamikX/laravel-jodit

pkg:composer/kamikx/laravel-jodit

Transparency log

Statistics

Installs: 16

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.0.2 2026-08-05 08:02 UTC

This package is auto-updated.

Last update: 2026-08-05 08:08:31 UTC


README

Laravel Jodit - A WYSIWYG editor package that works seamlessly in plain Blade templates, Blade view components, Livewire components and Filament forms

A Laravel package that integrates the Jodit WYSIWYG editor through a reusable Blade component. It works seamlessly in plain Blade templates, Blade view components, Livewire components and Filament forms. The package includes a built-in server-side file browser and uploader connector, along with secure per-editor storage scopes that isolate uploaded files and browsing access for each editor instance.

Preview

Jodit Editor in Laravel Filament form

Jodit Editor

Jodit Editor File Upload Options

Jodit Editor

Jodit Editor File Browser

Jodit Editor

Jodit Editor Image Editor

Jodit Editor

Jodit Editor Visual Blocks (custom plugin)

Jodit Editor

Features

  • One Blade component<x-jodit::editor name="content" /> covers all use cases
  • Livewire-ready — pass wire-model and the editor syncs with your Livewire component
  • Filament-ready — custom field JoditEditor
  • Secure per-editor storage scopes — assign an isolated disk and base path to each field, with encrypted scope validation preventing access above its root (documentation)
  • Local assets by default — ships Jodit, js-beautify, and Ace; optional CDN URLs use an automatic local fallback (documentation)
  • Fully configurable — publish the config to override defaults, CDN URLs, middleware, etc.
  • Flexible toolbar buttons — supports named profiles, custom button arrays, separators, and dropdown-friendly controls like align

Requirements

  • PHP ^8.3
  • Laravel ^11.0 || ^12.0 || ^13.0
  • intervention/image ^4.0 — bundled as a dependency; powers image resize and crop features

Installation

composer require kamikx/laravel-jodit

The service provider is auto-discovered. Publish the pre-built browser assets:

php artisan vendor:publish --tag=jodit-assets --force
php artisan vendor:publish --tag=jodit-views --force

No Node.js or Vite build is required in the consuming Laravel application. 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" />

Autosize (default), custom height, and connector URL

Jodit grows with its content by default. No height option is required:

<x-jodit::editor name="body" />

For a global setting, keep 'defaults.height' => 'auto' in config/jodit.php. You may also set 'defaults.minHeight' => 300 to change the initial minimum height without disabling autosize.

Set a fixed height for one editor when needed:

<x-jodit::editor
    name="body"
    :height="600"
    connector-url="{{ route('admin.jodit.connector') }}"
/>

3. Use the optional Filament 5 field

The package does not install Filament. If your application does not already use Filament 5, install the Forms package first:

composer require filament/forms:^5.0

Import the optional field in your Filament resource or form schema:

use KamikX\LaravelJodit\Filament\Forms\Components\JoditEditor;

JoditEditor::make('content')
    ->profile('simple')
    ->disk('public')
    ->basePath('uploads/sales')
    ->extraButtons('visualBlocks')
    ->fileBrowser(true)
    ->height(800)
    ->columnSpanFull()
    ->required();

Use extraButtons() to add one or more toolbar buttons without replacing the selected or default profile. Multiple calls are appended in order.

Using JoditEditor without filament/forms installed throws a descriptive exception with the required Composer command. Plain Blade and Livewire usage remain available without Filament.

Per-editor storage roots

Use disk together with base-path to give each editor its own exact storage root. The file browser presents that path as / and cannot navigate above it:

<x-jodit::editor
    name="content"
    disk="s3"
    base-path="images/demo-app"
/>

The equivalent Filament configuration supports closures:

JoditEditor::make('content')
    ->disk(fn (): string => 'public')
    ->basePath(fn (): string => 'uploads/sales');

The older directory option remains supported. When base-path is omitted, it is appended below the global jodit.base_path. An explicit base-path takes precedence over directory. If user_directory is enabled, its user segment is appended to the resulting root in both cases.

The bundled connector receives the effective disk and root through an opaque, encrypted storage_scope. Keep require_storage_scope enabled so request parameters cannot be changed to access another disk or root. Disable it only temporarily for a legacy client that does not use the package components.

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|string config default (auto) Editor height in pixels or auto
file-browser bool true Enable Jodit file browser / uploader
connector-url string auto from config Override the connector endpoint URL
disk string config default Laravel filesystem disk used by this editor
directory string null Subdirectory appended below the configured global base path
base-path string null Exact storage root for this editor; takes precedence over directory
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)
extra-buttons array|string null Toolbar buttons appended to the explicit buttons or selected/default profile
debounce int 300 Livewire sync debounce in milliseconds
disable-plugins array|string null Plugin names disabled only for this editor instance
language string config default Jodit UI language registered by Jodit or a custom language script

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"]' />

Use extra-buttons to extend the explicit button list or selected/default profile:

<x-jodit::editor name="content" profile="simple" :extra-buttons="['visualBlocks']" />

Common toolbar examples

Use align when you want a single alignment dropdown instead of separate left, center, right, and justify buttons:

<x-jodit::editor
    name="content"
    :buttons="['bold', 'italic', '|', 'align', '|', 'ul', 'ol', '|', 'link', 'image']"
/>

Use the package's richer preset when you want the full toolbar profile:

<x-jodit::editor name="content" profile="full" />

Custom languages

Register one or more standalone JavaScript language files globally. Local asset paths and absolute URLs are supported:

// config/jodit.php
'language_scripts' => [
    'js/jodit/langs/sk_sk.js',
    'https://cdn.example.com/jodit/langs/another_language.js',
],

'language' => 'sk_sk,

A standalone language file registers its translations after Jodit is loaded:

// public/js/jodit/langs/sk_sk.js
Jodit.lang.sk_sk = {
    Bold: 'Tučné',
    Italic: 'Kurzíva',
    Cancel: 'Zrušiť',
    Apply: 'Použiť',
};

The configured language is used globally. You can also select a registered language for an individual Blade editor:

<x-jodit::editor name="content" language="sk_sk" />

Or for a Filament field:

JoditEditor::make('content')
    ->language('sk_sk');

Language scripts are loaded once per page, after Jodit and before custom plugins and editor initialization. Jodit's repository files, such as src/langs/cs_cz.js, are source modules with internal imports and may not work directly in a browser. Use a browser-ready build or a standalone file that registers the dictionary through Jodit.lang.

Custom plugins

Register one or more standalone JavaScript plugin files globally:

// config/jodit.php
'plugin_scripts' => [
    'js/jodit/plugins/statistics.js',
    'js/jodit/plugins/custom-buttons.js',
],

Each file must register its plugin after Jodit is loaded:

Jodit.plugins.add('statistics', {
    init(editor) {
        editor.events.on('change.statistics', () => {
            console.log(editor.value.length);
        });
    },

    destruct(editor) {
        editor.events.off('.statistics');
    },
});

Plugin scripts are loaded once per page before the editors are initialized. Disable a globally registered plugin for a specific Blade editor with:

<x-jodit::editor
    name="content"
    :disable-plugins="['statistics']"
/>

Or for a Filament field:

JoditEditor::make('content')
    ->disablePlugins(['statistics']);

Configuration

Static configuration after publishing config/jodit.php

// config/jodit.php
return [
    ...
    /*
    |--------------------------------------------------------------------------
    | Default Editor Options
    |--------------------------------------------------------------------------
    |
    | Any key/value pair here is merged into the Jodit config object before
    | the editor is instantiated.  See https://xdsoft.net/jodit/docs/ for all
    | available options.
    |
    */

    'defaults' => [
        'height'               => 'auto',
        'toolbarSticky'        => true,
        'toolbarButtonSize'    => 'middle',
        'showCharsCounter'     => true,
        'showWordsCounter'     => true,
        'showXPathInStatusbar' => true,
        'hidePoweredByJodit'   => true,
        'defaultActionOnPaste' => 'insert_clear_html',
        'beautifyHTML'          => true,
        'beautifyHTMLCDNUrlsJS' => [],
        'sourceEditor'          => 'ace',
        'sourceEditorCDNUrlsJS' => [],
        'sourceEditorNativeOptions' => [
            'mode'  => 'ace/mode/html',
            'theme' => 'ace/theme/idle_fingers',
            'wrap'  => true,
        ],
        // Example: Add base theme colors to pallet
        'colors' => [
            'full' => ['#EBAFCD', '#C8AA82','#6491C8','#B4EBFA','#E65A37','#c74e30']
        ],
        // Example define custom fonts(1)
        'controls' => [
            'font' => [
                'list' => [
                    '' => 'Default',
                    'Musette,sans-serif' => 'Musette',
                    'Panton-Light,sans-serif' => 'Panton-Light',
                    'Panton-Regular,sans-serif' => 'Panton-Regular',
                    'Panton-Bold,sans-serif' => 'Panton-Bold',
                    'Panton-SemiBold,sans-serif' => 'Panton- SemiBold',
                    'Panton-ExtraBold,sans-serif' => 'Panton-ExtraBold'
                ],
            ],
        ],
    ],

        /*
    |--------------------------------------------------------------------------
    | Atomic Editor Options
    |--------------------------------------------------------------------------
    |
    | Dot-notated paths listed here are wrapped in Jodit.atom() before the
    | editor is instantiated. Atomic values replace Jodit's built-in value
    | instead of being merged with it.
    |
    */
    'atomic_options' => [
        // Example define custom fonts(1)
        'controls.font.list',
    ],
    ...
];

Dynamic configuration with Vite assets

When language files, editor styles, or custom plugins are built by Vite, their final URLs contain generated filenames and cannot be stored directly in config/jodit.php. Resolve them from the Vite manifest in custom service provider JoditEditorServiceProvider and set the Jodit configuration at runtime:

<?php
// bootstrap/providers.php
return [
    App\Providers\AppServiceProvider::class,
    App\Providers\AuthServiceProvider::class,
    App\Providers\EventServiceProvider::class,
    App\Providers\Filament\AdminPanelProvider::class,
    ...
    App\Providers\JoditEditorServiceProvider::class,
];
<?php
// app/Providers/JoditEditorServiceProvider.php
namespace App\Providers;

use Illuminate\Foundation\Vite;
use Illuminate\Support\ServiceProvider;

class JoditEditorServiceProvider extends ServiceProvider
{
    private bool $configured = false;

    public function boot(Vite $vite): void
    {
        // The callback is triggered before rendering jodit component or jodit filament field
        View::composer([
            'jodit::components.editor',
            'jodit::filament.forms.components.jodit-editor',
        ], function () use ($vite): void {
            if ($this->configured) {
                return;
            }

            // Set iframe mode
            config()->set('jodit.defaults.iframe', true);
            // Set custom iframe css
            config()->set('jodit.defaults.iframeCSSLinks', [
                $vite->asset('resources/assets/scss/jodit-editor-content.scss'),
            ]);
            // Set custom plugin JS
            config()->set('jodit.plugin_scripts', [
                $vite->asset('resources/assets/js/jodit/plugins/visual-blocks/visual-blocks.js'),
            ]);

            $this->configured = true;
        });
    }
}

Make sure every path passed to $vite->asset() is included in your Vite build inputs, either directly or through an imported dependency. The example enables iframe mode to prevent conflicts between application styles (for example, Tailwind CSS and Bootstrap) and loads custom jodit-editor-content.scss styles inside the iframe.

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 KamikX\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
align Alignment dropdown (left, center, right, justify)
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 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',
],

Example — full editing toolbar:

'buttons' => [
    'source', '|',
    'bold', 'italic', 'underline', 'strikethrough', 'superscript', 'subscript', 'eraser', '|',
    'paragraph', 'font', 'fontsize', 'brush', 'classSpan', '|',
    'align', '|',
    'ul', 'ol', 'indent', 'outdent', '|',
    'cut', 'copy', 'paste', 'selectall', '|',
    'link', 'image', 'video', 'file', 'table', 'hr', 'symbols', '|',
    'undo', 'redo', '|',
    'find', 'spellcheck', 'speech', 'preview', 'print', 'fullsize',
],

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',
],

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 & credits

This project is licensed under the MIT License.

It is an independently maintained fork of nasirkhan/laravel-jodit and builds upon the work of its original authors and contributors.