kraksoft/redactor-bundle
Symfony bundle for integrating the Imperavi Redactor WYSIWYG editor, with configuration and form support for adding rich-text editing fields to your application.
Reality check before you type composer require. This package is a Symfony2 bundle, not a Laravel package. There is no service provider, no facade, no config/redactor.php to publish, and no bundle-registration hook in Laravel's bootstrap/providers.php. Running the install will either fail on constraints or drag legacy Symfony 2 components into a Laravel 10/11 tree and break autoloading. Confirm the constraint in the repo's composer.json (symfony/symfony ~2.x, PHP 5.x era) before wasting time.
# Do NOT run this in a modern Laravel app — expect constraint conflicts
composer require kraksoft/redactor-bundle
Where to look first, as a Laravel developer. Treat the repo as reference documentation, not a dependency. Two files are worth reading:
AbstractType + Twig form theme) — shows how the editor mounts on a textarea. This is the part you'll reimplement as a Blade component.Your first real use case. A Laravel developer's realistic first task is not "install and use this." It is: "I need a rich-text field on a form." Do this instead:
# Pick a maintained, framework-agnostic editor
npm install @tiptap/core @tiptap/starter-kit
# or tinymce, quill, ckeditor5, lexical
If a stakeholder insists on Redactor specifically, that's a procurement + asset-vendoring task (see below), not a Composer task. Either way, the day-to-day work is a Blade component plus a Vite bundle — nothing from this repo is importable.
Pattern 1 — Port the concept, not the code. The bundle's one transferable idea is: "one reusable form control that renders a textarea, mounts the editor client-side, and returns HTML." Reproduce that as a Blade component.
{{-- resources/views/components/rich-text.blade.php --}}
@props(['name', 'value' => '', 'options' => []])
<textarea name="{{ $name }}" {{ $attributes }}>{{ $value }}</textarea>
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', () => {
// editor init — Redactor, Tiptap, whatever you vendored
window.initRichText(document.querySelector('[name="{{ $name }}"]'), @json($options));
});
</script>
@endpush
Use it as <x-rich-text name="body" :value="$post->body" /> inside any Blade form.
Pattern 2 — Asset delivery via Vite, not web/. The bundle assumes Symfony's assets:install copies files into web/. In Laravel your assets live in resources/ and build into public/build/ through Vite. Vendor the editor package there:
// resources/js/app.js
import './vendor/redactor/redactor.min.js'; // per Imperavi license
import './rich-text';
Never point a <script src="/bundles/kraksoftredactor/..."> at public/ — it won't exist.
Pattern 3 — Config as a published PHP array. Mirror the bundle's config tree in config/rich-text.php, then read it in the component:
// config/rich-text.php
return [
'buttons' => ['html', 'format', 'bold', 'italic', 'link', 'lists'],
'lang' => 'en',
'min_height' => 200,
];
This is the equivalent of the Symfony DI extension — you own it, it's versionable, and it doesn't require a bundle.
Pattern 4 — Sanitize on save and render. This is the pattern the bundle deliberately omits. Build it once:
use Mews\Purifier\Facades\Purifier;
$post->body = Purifier::clean($request->input('body')); // save
// and cache the cleaned output rather than re-running on every render
If you follow the bundle's own conventions too literally, you'll store raw editor HTML and render it unescaped — that's a stored-XSS pattern, not a feature.
Pattern 5 — Uploads are your endpoint. The bundle provides none. If the editor needs inline images, own the controller behind auth + a policy + MIME/size validation:
Route::post('/rich-text/upload', [RichTextUploadController::class, 'store'])
->middleware(['auth', 'throttle:uploads']);
Pattern 6 — If you inherit a Symfony 2 app. Don't add the package as a fresh dependency — vendor the ~200 lines of glue into src/ (form type + Twig theme + config), pin the commit, and treat it as frozen. You're now the maintainer, which you would be anyway, but at least composer update won't take the app down.
Composer will fight you. symfony/symfony ~2.x and PHP 5.x constraints hard-conflict with any modern Laravel install. If you see "your requirements could not be resolved," do not try to --ignore-platform-reqs your way through — you'll end up with Symfony 2 components loaded alongside Laravel and mysterious Class not found errors at boot.
There is no Laravel registration path. AppKernel::registerBundles() has no analog. If a guide tells you to add the bundle to config/app.php providers, that guide is for a different package — stop following it.
The editor is not included. Redactor is commercially licensed by Imperavi, per seat/domain. The bundle's name implies it ships the editor, but you must obtain and vendor the JS yourself. Budget this as a procurement decision, not a composer require.
assets:install targets web/, not public/build/. Even if you port code, the asset path assumptions are Symfony-flavored. Route everything through Vite and delete the bundle's asset commands from your mental model.
jQuery-era assumptions. Older Redactor versions expect a global jQuery and manipulate the DOM in ways that collide with Alpine, Livewire morphs, and Stimulus. If any of those are in the page, test the editor's lifecycle carefully — re-mounting after a Livewire update is a common failure.
CSP is a silent killer. Legacy Redactor initializes with inline scripts and often eval-adjacent code paths. A strict Content-Security-Policy (no unsafe-inline/unsafe-eval) will make the editor fail to mount with no error in the UI — check the browser console first when "the box is empty."
No sanitization, no upload validation, no media library. The bundle provides the mounting glue and nothing else. Treat stored-HTML XSS as the highest-severity risk of a naive port and make Purifier or HTMLPurifier a day-one dependency, not a follow-up ticket.
Store portable HTML, not editor JSON. If you later swap editors (and for a package this abandoned, you almost certainly will), semantic HTML in the body column makes the swap a render-layer change. Editor-specific JSON blobs make it a data migration.
Extension points are Symfony-specific. Overriding defaults here means the DI config tree and Twig form theme — both meaningless in Laravel. Your "extension points" in a Laravel port are the published config/rich-text.php array and the Blade component's $options prop. Design them for overrides from the start so you don't rebuild later.
Debugging checklist when the editor doesn't appear:
public/build?DOMContentLoaded that fired before the component mounted?{!! !!} vs {{ }} balance against your sanitizer.The 3-star / 0-dependent situation is the real tip. There is no community to surface regressions, no security advisories, and no PHP 8.3/8.4 fixes coming. Every failure mode above lands on you. The cheapest day-to-day leverage is to not depend on this package at all — study its config shape for five minutes, then build a small Blade component around a maintained editor and move on.
How can I help you explore Laravel packages today?