Laravel Integration
This guide covers the most common way to ship the builder inside a Laravel admin or multi-tenant SaaS dashboard.
The builder is a Vue 3 package — Laravel serves the shell; Vue handles the editor UI.
Quick overview
| Layer | Responsibility |
|---|---|
| Laravel | Auth, routes, Eloquent models, API to save content HTML |
| Vue (Vite) | Mount <PageBuilder />, call startBuilder(), publish via getSavedPageHtml() |
| Database | One content (longText) column per page/post/product description |
1. Install the package
In your Laravel project's frontend (Vite + Vue):
npm install @myissue/vue-website-page-builder piniaImport styles once in your app entry:
import '@myissue/vue-website-page-builder/style.css'Register Pinia if you have not already:
import { createPinia } from 'pinia'
app.use(createPinia())2. Database column
Add a column for saved builder output:
Schema::table('pages', function (Blueprint $table) {
$table->longText('content')->nullable();
});Store the full HTML from getSavedPageHtml() — including the #pagebuilder wrapper and data-meta-title / data-meta-description when set.
3. Inertia page (recommended)
Route (routes/web.php):
Route::middleware(['auth'])->group(function () {
Route::get('/admin/pages/{page}/edit', [PageController::class, 'edit'])->name('pages.edit');
Route::put('/admin/pages/{page}', [PageController::class, 'update'])->name('pages.update');
});Controller:
public function edit(Page $page)
{
return Inertia::render('Admin/Pages/Edit', [
'page' => $page->only('id', 'title', 'content'),
]);
}
public function update(Request $request, Page $page)
{
$validated = $request->validate([
'title' => 'required|string|max:255',
'content' => 'required|string',
]);
$page->update($validated);
return redirect()->route('pages.edit', $page);
}Vue page (resources/js/Pages/Admin/Pages/Edit.vue):
<script setup lang="ts">
import { onMounted } from 'vue'
import { router } from '@inertiajs/vue3'
import { PageBuilder, getPageBuilder } from '@myissue/vue-website-page-builder'
const props = defineProps<{ page: { id: number; title: string; content: string | null } }>()
const pageBuilderService = getPageBuilder()
onMounted(async () => {
const html = props.page.content ?? ''
const { components, pageSettings } = pageBuilderService.parsePageBuilderHTML(html)
await pageBuilderService.startBuilder(
{
updateOrCreate: { formType: 'update', formName: 'page' },
resourceData: { id: props.page.id, title: props.page.title },
pageSettings,
userSettings: { autoSave: true },
},
components,
)
})
function publish() {
const content = pageBuilderService.getSavedPageHtml()
router.put(route('pages.update', props.page.id), {
title: props.page.title,
content,
})
}
</script>
<template>
<PageBuilder :showPublishButton="true" @handlePublishPageBuilder="publish" />
</template>4. Page meta (title + description)
Users can set Meta title and Meta description in the builder Settings tab. Values persist on #pagebuilder as:
<div
id="pagebuilder"
data-meta-title="My Blog Post"
data-meta-description="A short summary for SEO."
>
...
</div>On the Laravel side, parse these when rendering the public page:
// Example: extract meta from stored HTML before Blade layout
$dom = new DOMDocument();
@$dom->loadHTML($page->content);
$xpath = new DOMXPath($dom);
$wrapper = $dom->getElementById('pagebuilder');
$metaTitle = $wrapper?->getAttribute('data-meta-title') ?: $page->title;
$metaDescription = $wrapper?->getAttribute('data-meta-description') ?: '';Or use parsePageBuilderHTML() on the frontend and send meta as separate API fields if you prefer normalized storage.
5. New page (create flow)
public function create()
{
return Inertia::render('Admin/Pages/Create', [
'page' => ['id' => null, 'title' => 'Untitled', 'content' => null],
]);
}await pageBuilderService.startBuilder({
updateOrCreate: { formType: 'create', formName: 'page' },
resourceData: { title: 'Untitled' },
userSettings: { autoSave: true },
})6. Blade-only embed (no Inertia)
Load a Vite entry that mounts the builder into a Blade view:
{{-- resources/views/admin/pages/edit.blade.php --}}
@vite(['resources/js/admin-page-builder.ts'])
<div id="page-builder-root"
data-page-id="{{ $page->id }}"
data-title="{{ $page->title }}"
data-content="{{ $page->content }}">
</div>Pass data-content via a JSON script tag if HTML is large (avoid attribute size limits).
7. Multi-tenant notes
- Use a unique
resourceData.id(or slug) per tenant page so localStorage draft keys do not collide across users on shared machines. - Scope Laravel policies so tenants only load their own
content. - For product grids, pass
:DisplayProductswith your catalog picker when you need real SKUs, pagination, or API search (see Display Products). Omit the prop to use the built-in sample catalog, or set:enableDefaultProducts="false"to hide product sections entirely.
8. Rendering public pages
The saved HTML is self-contained sections. Render it inside your layout:
@section('meta')
<title>{{ $metaTitle }}</title>
<meta name="description" content="{{ $metaDescription }}">
@endsection
<article class="prose">
{!! $cleanHtml !!}
</article>Strip builder-only attributes for production if needed — see Rendering HTML Output.
Related docs
- Backend Integration — save/restore contract
- Vue Integration — generic Vue setup
- Providing Configuration —
startBuilder()options - Display Products — ecommerce product sections