Repository navigation
Page JSON Structure
Selldone pages are plain JSON. The editor writes it, the viewer reads it, and your server stores it as the page's
content. This page documents the structure as implemented in Builder.ts, src/section/section.ts and
models/element/LModelElement.ts, so you can store, inspect, migrate or generate pages safely.
Page model (from @selldone/core-js)
├── title, description, image, direction ("ltr" | "rtl" | "auto"), ...
├── content ← what the builder exports
│ ├── title
│ ├── style ← page-wide typography and background
│ └── sections[] ← ordered list of sections
│ └── { uid, label, object }
│ └── element tree (XSection → XContainer → XRow → XColumn → ...)
└── css ← optional custom CSS { classes[], raw }
$builder.export() returns { title, sections, style }. $builder.loadPage({ content, css, direction }) loads it back.
| Key | Type | Description |
|---|---|---|
title |
string | Optional title stored with the content. |
style |
object | Page-wide style (see below). |
sections |
array | Sections in display order. |
{
"uid": "auto_48213301275",
"label": "Horizontal Hero",
"object": { "component": "XSection", "...": "..." }
}| Key | Description |
|---|---|
uid |
Unique section id. If missing, the builder generates one (auto_ + random number). It is also used as the DOM id of the rendered section. |
label |
Human-readable name shown in the editor. Defaults to Section. |
object |
Root element of the section, normally an XSection. |
Legacy (v1) sections use name and data instead of object. They are converted automatically when loaded (see
Migration).
Every element in the tree has the same shape (LModelElement.toJson()):
| Key | Type | Description |
|---|---|---|
component |
string | Element type, for example XText. Must be a registered component (list below). |
id |
string | null | Optional element id. |
background |
object | Background settings (see below). |
style |
object | Inline CSS as a JavaScript style object, for example { "paddingTop": "24px" }. |
classes |
string[] | CSS classes, for example Vuetify utility classes such as mb-2 or text-center. |
data |
object | Element-specific data (see examples). |
props |
object | null | Extra properties. |
children |
array | Child elements. |
label |
string | null | Optional label used to find specific children (for example a title inside a column). |
background follows LModelBackground: bg_color, bg_gradient (array of colors), bg_image, bg_size
(default cover), bg_repeat, bg_position, bg_rotation (degrees), bg_custom, bg_backdrop (backdrop
filters), bg_video and dark.
These element classes are registered in LUtilsComponents.XObjects and can be loaded from JSON:
| Group | Components |
|---|---|
| Layout |
XSection, XContainer, XRow, XColumn, XColumnImageText, XScroller
|
| Content |
XText, XButton, XButtons, XUploader (image), XLottie, XMarquee, XArticle, XCode
|
| Galleries |
XSwiper, XGalleryExpandable, XGalleryExpandableItem
|
| Commerce |
XProduct, XProducts, XProductOverview, XFeederProducts, XSearch
|
| Blog | XFeederBlogs |
| Forms |
XForm, XInputText
|
| Embed | XEmbedPage |
If a component is unknown, the loader logs LUtilsLoader | JsonObjectToInstance | Class not found! and skips that
element.
| Component | data |
|---|---|
XSection |
{ "hide": { "sm": false, "md": false, "lg": false, "xl": false, "guest": false, "user": false } } |
XContainer |
{ "fluid": false } |
XRow |
{ "align": "center", "justify": "center", "no_wrap": false, "reverse": false } — align: start, center, end, stretch, baseline; justify: start, center, end, space-around, space-between, space-evenly, stretch
|
XColumn |
{ "grid": { "mobile": 12, "tablet": 6, "desktop": 4, "widescreen": null } } — values 1 to 12 or null
|
XText |
{ "value": "Hello <b>world</b>", "tag": "h1" } — tag: p, h1–h6, span, div
|
XButton |
{ "content": "Shop now", "href": "/products", "color": "", "size": "x-large", "variant": "elevated", "align": "center", ... } |
XUploader |
{ "src": "https://.../image.jpg", "link": null, "alt": "Product photo", "setting": { ... } } |
XProducts |
{ "align": "center", "justify": "center", "no_wrap": false, "filter": { "count": 8, "sort": "...", "only_available": true, ... } } |
XCode |
{ "code": "<div>...</div>", "mode": "html", "properties": null } — mode: html or vue
|
The data classes live next to each component in components/x/<name>/X<Name>ObjectData.ts. Read them for the full
list of fields and defaults.
content.style holds page-wide settings. When a page is loaded, missing values get defaults:
| Key | Default | Notes |
|---|---|---|
font_size |
16 |
Base font size. |
fonts |
[] |
Google Fonts used by the page. Shop custom fonts are not stored here; pass them as customFonts. |
bg_color, bg_gradient, bg_size, bg_custom, bg_repeat
|
"", [], cover, null, null
|
Page background. |
h1_size … h6_size, p_size
|
builder defaults | Heading and paragraph sizes. Responsive variants such as h1_size_md exist in the Core Page.IStyle type. |
The storefront also reads header options from the style: menu_transparent, menu_dark, header_mode,
header_color and header_filter.
page.css is separate from content:
{
"classes": [{ "selector": ".hero-title", "value": "letter-spacing: 2px;" }],
"raw": ".my-banner { border-radius: 24px; }"
}The builder converts it to CSS text and injects it into a <style id="page-custom-style"> element inside the page
container.
The editor's Export action downloads <page title>.landing, a JSON file you can import into another shop:
{
"content": { "sections": [], "style": {} },
"title": "Spring sale",
"description": "...",
"image": "...",
"direction": "ltr",
"note": null,
"service": "https://selldone.com"
}On import, the builder migrates the content, keeps only sections that have an object, copies title,
description, image, direction and note to the current page, and saves a history entry. An empty sections
array is rejected with Sections in the file is empty!.
Older pages stored each section as { name, data }. When such a section is loaded, the builder renames it to the
current section name, converts data to an element tree, and continues as normal. Save the page once to store the
new format. If a section cannot be migrated, the console shows The object is not defined! Please check migration from V1 to V2!.
- Sections and Elements
- AI-Friendly JSON
-
Core models (
Page,Popup,ShopMenu) - FAQ