stevenyangecho/laravel-u-editor
Laravel wrapper for Baidu's UEditor rich text editor. Drop a full-featured WYSIWYG editor into Blade views, publish its config, and route image, file, video, and scrawl uploads through Laravel storage and authentication. Simplifies server-side handling for a classic rich text editor.
The package is a Laravel 5–era wrapper around Baidu's UEditor, so the setup is the classic three-step flow: install, register, publish.
composer require stevenyangecho/laravel-u-editor
config/app.php under providers. This package predates auto-discovery (2015 release), so you must add it manually and, if it exposes an alias/facade, add that to aliases too.
'providers' => [
// ...
Stevenyangecho\UEditor\UEditorServiceProvider::class,
],
php artisan vendor:publish
This should drop the UEditor front-end (JS, CSS, themes, lang, dialogs) into public/ and the backend config into config/. Confirm where the JS actually landed (public/vendor/ueditor/ueditor.all.js is typical) before wiring it into a view.php artisan route:list | grep -i ueditor
You should see the controller endpoint UEditor calls for action=config and the upload actions. Note the URI — the JS config must point at it.First use case: a CMS "post body" field. In your Blade form:
<textarea name="body" id="editor">{{ old('body', $post->body ?? '') }}</textarea>
<script src="{{ asset('vendor/ueditor/ueditor.config.js') }}"></script>
<script src="{{ asset('vendor/ueditor/ueditor.all.js') }}"></script>
<script>
var ue = UE.getEditor('editor', {
serverUrl: '{{ url('/ueditor/serve') }}' // match the route from route:list
});
</script>
Before building anything on top, hit the controller directly — GET /ueditor/serve?action=config — and confirm it returns the JSON config UEditor expects. That single check tells you whether routing, config publishing, and the response format are all wired correctly.
Blade partial for editor init. Wrap the two <script> tags plus the instantiation in a partial (partials/ueditor, taking a $name and $value) so every form reuses it. Pass serverUrl from route() rather than hard-coding, so staging/production URLs stay consistent.
Repopulating content after save/validation failure. UEditor renders into an iframe; a value attribute on the textarea is ignored. Use the textarea old()/model value as the initial HTML and either set it via ue.ready(() => ue.setContent(...)) or — simpler — let UEditor read from the textarea by using UE.getEditor with the textarea as source and calling setContent on ready. Always set content inside the ready callback, never immediately after instantiation.
Multiple editors per page. Each editor needs a unique container id and its own UE.getEditor('id'). Don't reuse the default serverUrl if different forms need different upload directories — pass per-instance config.
Per-context upload paths. Publish the config and edit the image/file/video path rules. UEditor path templates use its own tokens ({yyyy}, {mm}, {dd}, {time}, {rand:n}) — not Laravel's date() tokens. Set these per project, e.g. uploads/posts/{yyyy}{mm}/, and commit the config so path behaviour is reproducible.
Storage integration. If you're on Laravel 5.2+ with the public disk, point the upload path at storage/app/public/... and run php artisan storage:link, or keep uploads in public/uploads. Choose one and document it — mixing both is a common source of "images upload but 404" bugs.
Customising the controller. When you need auth on uploads, custom responses, or S3-style storage, publish/extend the package controller rather than editing vendor files. Register your own route that points at your subclass, or overlay the route in routes/web.php after disabling the package's (check the provider for a config flag or route-registration toggle).
Protecting the editor endpoints. Put the upload/config route behind your auth middleware group so anonymous users can't upload files. If you need a public read-only config endpoint but authenticated uploads, split them.
Sanitising on save. UEditor sends raw HTML. Store it, but sanitise on output (or on save with HTMLPurifier / a whitelist) — treat the editor content as untrusted user input even from "trusted" admins, since the editor allows pasted/raw HTML.
Form handling. Nothing special is required on the Laravel side: the textarea posts as a normal field. Validate body as required|string, and keep a FormRequest in front of it so the raw HTML doesn't bypass your usual validation boundary.
str_* helpers, container bindings). Test the service provider boot on your target version before committing to the package; if it breaks, the fallback is to use the published UEditor front-end assets and write a small controller yourself — you'd be reimplementing only the config/upload actions.action=config response wasn't valid JSON or the serverUrl is wrong. Curl the endpoint and inspect the body before touching the JS.@csrf token. Either add the upload route to VerifyCsrfToken::$except, or inject the token into UEditor's headers (ue.execCommand/custom XHR) — the exempt route is the pragmatic choice for most apps.php.ini (upload_max_filesize, post_max_size), and Nginx client_max_body_size. If uploads cap unexpectedly, check all three — the editor's error messages are unspecific.lang assets and set lang: 'en' in the editor config if you want English UI; otherwise the toolbar labels stay zh-cn.{yyyy}{mm}{dd} placeholders in the path format are UEditor's, and are not evaluated by Laravel. Don't try to pass a date('Y/m') string there.ueditor.config.js references an absolute path (or http), you'll get mixed-content or broken dialogs behind a proxy/https. Grep the published config for http:// and switch to relative or asset()-driven URLs.curl/Postman: POST /ueditor/serve?action=uploadimage with a multipart file. That isolates package vs. browser vs. network issues fast.How can I help you explore Laravel packages today?