Integrating with the Visual Editor ​
Bagisto Visual includes a live preview editor that allows merchants to build and customize storefront pages interactively, without reloading the page. When a section or block is added, updated, or removed through the editor, its HTML is dynamically re-rendered from the backend and injected into the DOM in place—without triggering a full page reload.
However, any JavaScript behavior associated with sections or blocks (like carousels, modals, or event listeners) is not automatically re-initialized. Additionally, some setting changes—such as text, image URLs, or inline styles—can be updated instantly in the browser, without requiring a backend re-render.
This guide explains how to:
- Reinitialize JavaScript behavior when sections or blocks are re-rendered
- Enable instant, client-side updates for simple setting types
- Ensure blocks are visible and interactable when being edited
By integrating with these editor behaviors, your sections and blocks will feel fast, predictable, and intuitive to customize.
1. Reinitializing JavaScript ​
When a section or block is updated in the editor, its DOM is replaced. Any interactive JavaScript (like sliders or dropdowns) must be reattached.
Bagisto Visual emits events during this lifecycle:
Common Events ​
| Event | Timing | Use case |
|---|---|---|
visual:section:load | After section is added or re-rendered | Re-run any necessary JavaScript to ensure the section functions and displays correctly, as if the page were freshly loaded. You may also want to restore the section state. |
visual:section:unload | Before section is removed or re-rendered | Make sure to clean up event listeners, variables, and anything else to prevent issues when interacting with the page and avoid memory leaks. Also, save the section state. |
visual:block:load | After block is added or re-rendered | Re-run block-specific JavaScript behavior. |
visual:block:unload | Before block is removed or re-rendered | Clean up block-specific event listeners and state. |
Lifecycle Events ​
| Event | Timing |
|---|---|
visual:section:adding | Before section is added |
visual:section:added | After section is added |
visual:section:removing | Before section is removed |
visual:section:removed | After section is removed |
visual:section:updating | Before section is updated |
visual:section:updated | After section is updated |
visual:section:moving | Before section is moved |
visual:section:moved | After section is moved |
visual:section:setting:updated | When a section setting changes |
visual:block:adding | Before block is added |
visual:block:added | After block is added |
visual:block:removing | Before block is removed |
visual:block:removed | After block is removed |
visual:block:updating | Before block is updated |
visual:block:updated | After block is updated |
visual:block:moving | Before block is moved |
visual:block:moved | After block is moved |
visual:block:setting:updated | When a block setting changes |
Each event exposes:
event.detail = {
sectionId, // Section ID
section, // Section object
blockId, // Block ID (when applicable)
block, // Block object (when applicable)
};Detect the Theme Editor ​
Use @visual_design_mode and @end_visual_design_mode directives to scope code that should only run in the Visual Editor live preview.
@visual_design_mode
<!-- This code only runs in the editor -->
<div class="editor-notice">You are in design mode</div>
@pushOnce('scripts')
<script>
// JavaScript that only runs in the editor
console.log('Editor mode active');
</script>
@endPushOnce
@end_visual_design_modeThis prevents editor-specific code from running on the live storefront, keeping your production code clean and performant.
Section Example ​
@visual_design_mode
@pushOnce('scripts')
<script>
document.addEventListener('visual:section:unload', (event) => {
if (event.detail.section.type === '{{ $section->type }}') {
// Save scroll position, destroy carousels, etc.
}
});
document.addEventListener('visual:section:load', (event) => {
if (event.detail.section.type === '{{ $section->type }}') {
// Reinitialize interactivity: sliders, modals, listeners
}
});
</script>
@endPushOnce
@end_visual_design_modeBlock Example ​
@visual_design_mode
@pushOnce('scripts')
<script>
document.addEventListener('visual:block:unload', (event) => {
if (event.detail.block.type === '{{ $block->type }}') {
// Clean up block-specific listeners, state, etc.
}
});
document.addEventListener('visual:block:load', (event) => {
if (event.detail.block.type === '{{ $block->type }}') {
// Reinitialize block-specific JavaScript
}
});
</script>
@endPushOnce
@end_visual_design_modeIf you are using Alpine.js or Livewire, your state will automatically persist between updates—no additional setup is needed. However, if you rely on vanilla JS or third-party libraries, you should reinitialize them after every update.
2. Enabling Instant Setting Updates ​
For simple updates (text, image URLs, classes), you can avoid full re-renders and apply changes directly in the DOM to provide instant preview without delay.
Bagisto Visual supports this via:
Option 1: liveUpdate() Blade Directives ​
Use $section->liveUpdate() or $block->liveUpdate() to bind settings to elements.
These helpers inject metadata to enable the editor to update the live preview without requiring a server-side re-render.
->text(string $settingId) ​
Updates the element's textContent whenever the specified setting changes.
<h1 {{ $section->liveUpdate()->text('heading') }}>
{{ $section->settings->heading }}
</h1>->html(string $settingId) ​
Updates the element's innerHTML with the new setting value.
<div {{ $section->liveUpdate()->html('html_content') }}>
{!! $section->settings->html_content !!}
</div>->outerHtml(string $settingId) ​
Replaces the entire element (outerHTML) with the setting value.
<div {{ $section->liveUpdate()->outerHtml('html_block') }}>
{!! $section->settings->html_block !!}
</div>->attr(string $settingId, string $attributeName) ​
Updates the specified HTML attribute (e.g. src, href, alt) with the setting value.
<img
src="{{ $section->settings->image }}"
{{ $section->liveUpdate()->attr('image', 'src') }}>->style(string $settingId, string $property) ​
Updates a specific CSS style property on the element using the setting value.
<div
style="width: {{ $section->settings->width }}"
{{ $section->liveUpdate()->style('width', 'width') }}>
</div>🔹 Multiple Bindings on a Single Element ​
You can bind multiple settings fluently to different parts of the same element:
<a
href="{{ $section->settings->link_url }}"
{{ $section->liveUpdate()
->text('link_text')
->attr('link_url', 'href') }}>
{{ $section->settings->link_text }}
</a>🔹 Using with Blocks ​
The liveUpdate() method works with both sections and blocks:
In a section:
<h1 {{ $section->liveUpdate()->text('heading') }}>
{{ $section->settings->heading }}
</h1>In a block:
<p {{ $block->liveUpdate()->text('text') }}>
{{ $block->settings->text }}
</p>Option 2: JavaScript API (Visual.handleLiveUpdate()) ​
For more complex cases (e.g. multiple targets, transform logic, or styling), use Visual.handleLiveUpdate():
@visual_design_mode
@pushOnce('scripts')
<script>
document.addEventListener('visual:editor:init', () => {
window.Visual.handleLiveUpdate('{{ $section->type }}', {
// handle update of section level settings
section: {
title: { target: 'h2', text: true },
image: { target: 'img', attr: 'src' }
},
blocks: {
// handle update of block settings
announcement: {
text: { target: 'p', text: true }
}
}
});
});
</script>
@endPushOnce
@end_visual_design_modeAPI Reference: handleLiveUpdate ​
handleLiveUpdate(
sectionType: string,
mappings: {
section?: Record<string, LiveUpdateOptions>;
blocks?: Record<string, Record<string, LiveUpdateOptions>>;
}
)LiveUpdateOptions ​
| Option | Description |
|---|---|
target | CSS selector within the section |
text | Replace text content |
html | Replace inner HTML |
attr | Set a DOM attribute (e.g. src, href) |
style | Set a CSS style property |
handler | Custom JS function (el, value) => {} |
transform | Modify the value before applying it |
3. Keep Edited Blocks Visible ​
When a merchant is editing a block, that block should remain visible — even if it's part of a carousel, tab, or other dynamic view.
Best Practice:
- When rendering blocks dynamically (e.g. in a slider), ensure the currently edited block is active or in view.
- This enhances clarity and ensures live changes are reflected immediately.
You can detect which block is being edited using the
visual:section:updatedevent andevent.detail.
No JavaScript is strictly required, but your UI logic should accommodate visibility for active blocks.
4. Summary ​
- Use
@visual_design_modeto scope editor-specific behavior - Use
liveUpdate()for simple instant updates - Use
handleLiveUpdate()for advanced DOM control - Reinitialize JavaScript using
visual:section:loadandvisual:block:load - Make edited blocks clearly visible in the preview
These patterns help ensure your sections and blocks behave consistently and responsively within the live editor environment.