Live Preview
How the admin editor provides instant preview of changes.
Overview
When editing section settings in the admin, changes appear instantly in the preview iframe. The system uses two update paths based on setting type:
- CSS hot-reload - For colors, fonts (save + reload section CSS)
- HTML re-render - For text and structural changes (server renders full section HTML)
Both paths are server-side. The difference is efficiency - CSS hot-reload only fetches new CSS, while HTML re-render fetches the entire section.
Update Paths
Path 1: CSS Hot-Reload
For CSS-relevant settings (color, color_alpha, font):
- Settings auto-save to database (150ms debounce)
- Server regenerates section CSS from
.css.liquidtemplate - CSS link tag in iframe refreshes with cache-busting timestamp
- Visual update applies without touching HTML
Path 2: HTML Re-render
For content settings (text, textarea, link, etc.) and structural settings (select, checkbox, number):
- Settings sent to preview endpoint (300ms debounce)
- Server renders complete section HTML with temporary values
- JavaScript replaces the section in iframe via postMessage
- Settings auto-save (500ms debounce)
Setting Type Categories
// CSS-only: Save + CSS hot-reload (fastest)
const CSS_ONLY_TYPES = ["color", "color_alpha", "font"]
// HTML-only: Server re-render (fast)
const HTML_ONLY_TYPES = ["text", "textarea", "collection", "post", "tag", "link", "url", "header", "info", "icon"]
// Ambiguous: Do BOTH CSS + HTML (safe fallback)
// range, number, select, checkbox, image
CSS Variables
For colors and images, use CSS variables with the data-css-vars attribute:
{% capture section_style %}
--bgcolor: {{ section.settings.bgcolor }};
--overlay: {{ section.settings.overlay | color_alpha_to_rgba }};
--bg-image: url('{{ section.settings.image | image_url: 'hero' }}');
{% endcapture %}
{% section_wrapper 'section' style: section_style data-css-vars: 'bgcolor,overlay,image' %}
<div style="background: var(--overlay), var(--bg-image); background-color: var(--bgcolor);">
...
</div>
{% endsection_wrapper %}
The data-css-vars attribute lists which settings should trigger CSS hot-reload.
The .attr Accessor (Optional)
The .attr accessor outputs data attributes for element identification:
<h2 {{ section.settings.title.attr }}>{{ section.settings.title }}</h2>
Outputs:
<h2 data-setting="title" data-section-id="abc123">My Title</h2>
Note: .attr is NOT required for live preview to work. It's a convenience that:
- Provides unique identifiers for debugging
- May be used by future optimizations
- Helps with CSS targeting via
[data-setting="..."]
Live preview works via server re-render regardless of whether .attr is used.
Section ID
Each section has a unique UUID (section.id) provided by section_wrapper:
<section data-section-id="abc123">
<!-- All content inherits this section context -->
</section>
This ID is used to:
- Target the correct section for HTML replacement
- Scope CSS variables to the section
- Trigger CSS hot-reload for the right CSS link tag
Complete Example
{% capture section_style %}
--bgcolor: {{ section.settings.bgcolor }};
--text-color: {{ section.settings.text_color }};
--overlay: {{ section.settings.overlay | color_alpha_to_rgba }};
--bg-image: url('{{ section.settings.image | image_url: 'hero' }}');
{% endcapture %}
{% section_wrapper 'section' class: 'cta' style: section_style data-css-vars: 'bgcolor,text_color,overlay,image' %}
<div class="cta-background" style="background: var(--overlay), var(--bg-image); background-color: var(--bgcolor);">
<div class="cta-content" style="color: var(--text-color);">
{% htmltag 'h2' section.settings.title class: 'cta-title' %}
{% htmltag 'p' section.settings.body class: 'cta-body' %}
{% if section.settings.show_button %}
<a href="{{ section.settings.button | link_href }}" class="btn">
{{ section.settings.button | link_text }}
</a>
{% endif %}
</div>
</div>
{% endsection_wrapper %}
{% schema %}
{
"name": "CTA Section",
"category": "cta",
"settings": [
{
"type": "text",
"id": "title",
"label": "Title",
"default": "Ready to get started?"
},
{
"type": "text",
"id": "body",
"label": "Body",
"nb_rows": 3
},
{
"type": "color",
"id": "bgcolor",
"label": "Background Color",
"default": "#1F2937"
},
{
"type": "color",
"id": "text_color",
"label": "Text Color",
"default": "#FFFFFF"
},
{
"type": "color_alpha",
"id": "overlay",
"label": "Image Overlay",
"default": {"color": "#000000", "alpha": 50}
},
{
"type": "image",
"id": "image",
"label": "Background Image"
},
{
"type": "checkbox",
"id": "show_button",
"label": "Show Button",
"default": true
},
{
"type": "link",
"id": "button",
"label": "Button",
"with_text": true,
"show_if": { "setting": "show_button", "eq": true }
}
]
}
{% endschema %}
What Updates How
| Setting Type | Update Method | Speed |
|---|---|---|
| color | CSS hot-reload | ~150ms |
| color_alpha | CSS hot-reload | ~150ms |
| font | CSS hot-reload | ~150ms |
| text | Server re-render | ~300ms |
| textarea | Server re-render | ~300ms |
| link | Server re-render | ~300ms |
| select | CSS + HTML (both) | ~300ms |
| number | CSS + HTML (both) | ~300ms |
| range | CSS + HTML (both) | ~300ms |
| checkbox | CSS + HTML (both) | ~300ms |
| image | CSS + HTML (both) | ~300ms |
Tips
- Use CSS variables for any style-related setting (colors, images)
- Use
htmltagfor text elements - cleaner than manual.attr - Test live preview when developing new sections
- Use natural boolean checks for checkbox settings (
if,unless)