A field that drops the Gutenberg block editor into an Orchid Platform screen. Wraps van-ons/laraberg with an Orchid Field, a paste handler that strips Word/Docs cruft so pasting doesn't freeze the browser, and an opt-in bridge to a Laravel media-upload endpoint.
Requires PHP 8.1+, Orchid Platform ^14.15. License: MIT (this wrapper). Laraberg itself is GPL-3.0-or-later — read its license before shipping commercial software.
composer require protrafficgroup/orchid-larabergThe service provider auto-discovers and publishes Laraberg + wrapper assets on first boot. If something prevents auto-publishing (custom container, console-only runs), do it explicitly:
php artisan vendor:publish --provider="VanOns\Laraberg\LarabergServiceProvider" --tag=publicTo customise the default options array:
php artisan vendor:publish --tag=laraberg-configuse Orchid\Support\Facades\Layout;
use ProTrafficGroup\OrchidLaraberg\Laraberg;
public function layout(): iterable
{
return [
Layout::rows([
Laraberg::make('content')->title('Body'),
]),
];
}The submit handler receives the raw Gutenberg HTML in $request->input('content') — store it directly. To render on the public site, use the RendersContent trait from the upstream Laraberg:
use Illuminate\Database\Eloquent\Model;
use VanOns\Laraberg\Traits\RendersContent;
class Post extends Model
{
use RendersContent;
}
// in a Blade view:
// {!! $post->render() !!}By default the trait reads the content column. Override via protected $contentColumn = 'body'; or pass the name into $post->render('body').
Laraberg::make()->setOptions([...]) forwards options into Laraberg.init on the client. Anything supported by Laraberg's EditorSettings works — height, sizes, colours, gradients, disabled core blocks, and so on.
Laraberg::make('content')
->setOptions([
'height' => '600px',
'maxWidth' => 800,
'disabledCoreBlocks' => ['core/code', 'core/freeform'],
'colors' => [
['name' => 'Brand red', 'slug' => 'brand-red', 'color' => '#c0392b'],
],
]);Use mergeOptions([...]) to add to an existing set without replacing it. The default option set lives in config/orchid-laraberg.php and is read on every Laraberg::make() call.
Set the special _mediaUploadEndpoint option to a route URL — the field wires that URL into Gutenberg's mediaUpload callback, so Image / Gallery / Cover / Video blocks send selected files there.
Laraberg::make('content')
->setOptions([
'_mediaUploadEndpoint' => route('platform.laraberg.media'),
]);The frontend posts multipart/form-data with files[], plus a CSRF header (X-CSRF-TOKEN from <meta name="csrf-token"> if present, otherwise X-XSRF-TOKEN derived from the cookie — one of them will be accepted by Laravel's VerifyCsrfToken middleware).
Your endpoint must return JSON in this shape:
[
{
"id": 42,
"url": "https://example.test/storage/2026/05/17/abc.png",
"mime": "image/png",
"title": "logo.png",
"alt": "logo.png",
"caption": ""
}
]Using Orchid's built-in Attachment (deduplicates by hash, ships with the platform):
namespace App\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Orchid\Attachment\File;
class LarabergMediaController extends Controller
{
public function __invoke(Request $request): JsonResponse
{
$request->validate([
'files' => 'required|array|min:1',
'files.*' => 'file|max:20480',
]);
$payload = [];
foreach ($request->file('files') as $uploaded) {
$attachment = (new File($uploaded))->load();
$payload[] = [
'id' => $attachment->id,
'url' => $attachment->url(),
'mime' => $attachment->mime,
'title' => $attachment->original_name,
'alt' => $attachment->original_name,
'caption' => '',
];
}
return response()->json($payload);
}
}Route — register it inside routes/platform.php so the platform middleware adds auth + CSRF + the dashboard prefix:
Route::post('/laraberg/media', LarabergMediaController::class)
->name('platform.laraberg.media');- Set
APP_URLto the URL the editor is opened on (http://example.test:81).Storage::disk('public')->url(...)builds absolute URLs from it; ifAPP_URLis wrong, every uploaded image 404s. php artisan storage:linkmust have been run; otherwise the/storage/...URL has no symlink to serve.- Default
nginxclient_max_body_sizeis1m. Raise it for larger uploads. PHPupload_max_filesizeandpost_max_sizeapply too — the real ceiling is the minimum of nginx / php /files.*|max:validator.
When pasting from Word, Google Docs, or a browser page, Gutenberg's pasteHandler parses the HTML through a multi-pass DOMParser pipeline and matches it against every registered block. Rich HTML from word processors can trigger multi-second freezes.
The field auto-registers a blocks.pasteHandler filter that strips, before Gutenberg sees the HTML:
- Word-only attributes —
style,class,lang,mso-* - Conditional comments —
<!--[if mso]>...<![endif]--> - Namespaced tags —
<o:p>,<w:*>,<m:*>,<st1:*> - Bare
<font>/<span>wrappers
For most Word / Docs paste flows this turns multi-second freezes into instant pastes. No configuration needed.
Plain-text paste (Ctrl+Shift+V / ⌘+Shift+V) bypasses the filter entirely and is always fast.
Anything that works against van-ons/laraberg v2.0.x works here unchanged — this wrapper does not touch block registration. See the upstream custom block docs.
The bundled WordPress packages are exposed via the global Laraberg object:
Laraberg.wordpress.blockEditorLaraberg.wordpress.blocksLaraberg.wordpress.componentsLaraberg.wordpress.dataLaraberg.wordpress.elementLaraberg.wordpress.hooksLaraberg.wordpress.serverSideRender
This package is a thin Orchid-flavored wrapper around van-ons/laraberg (GPL-3.0-or-later). All of the heavy lifting — bundling Gutenberg, rendering blocks, exposing WordPress packages — happens in that project.