Skip to content

Content Editors

Content editors define the merchant-editable content fields for your section. Each editor maps to a specific input widget in the Instant Site Editor — text inputs, image uploaders, toggles, dropdown selects, and more.

Overview

All content editors are created using factory functions from @lightspeed/crane-api:

typescript
import { content } from '@lightspeed/crane-api';
TypeFactoryPurpose
INPUTBOXcontent.inputbox()Single-line text input
TEXTAREAcontent.textarea()Multi-line text input
BUTTONcontent.button()Button with text, link, and action
IMAGEcontent.image()Image upload
TOGGLEcontent.toggle()Boolean on/off switch
SELECTBOXcontent.selectbox()Dropdown selection
DECKcontent.deck()Collection of repeatable cards
MENUcontent.menu()Menu items list
NAVIGATION_MENUcontent.navigationMenu()Navigation menu (header)
LOGOcontent.logo()Logo (text or image)
DIVIDERcontent.divider()Visual separator in the editor
INFOcontent.info()Informational text in the editor
PRODUCT_SELECTORcontent.productSelector()Product picker
CATEGORY_SELECTORcontent.categorySelector()Category picker

💡 Label Convention

All label, placeholder, and description properties must be translation keys starting with $ (e.g., '$label.content.title'). These are resolved from your translation files.

INPUTBOX

Single-line text input for short content like titles and headings.

Properties

PropertyTypeRequiredDescription
labelstringYesTranslation key for the editor label
placeholderstringYesTranslation key for placeholder text
defaultsobjectNoDefault values

Defaults

PropertyTypeRequiredDescription
textstringYesDefault text value (translation key)

Factory

typescript
content.inputbox({
  label: '$label.content.title',
  placeholder: '$label.content.title_placeholder',
  defaults: { text: '$label.defaults.title' },
})

TEXTAREA

Multi-line text input for longer content like descriptions and paragraphs.

Properties

PropertyTypeRequiredDescription
labelstringYesTranslation key for the editor label
placeholderstringYesTranslation key for placeholder text
defaultsobjectNoDefault values

Defaults

PropertyTypeRequiredDescription
textstringYesDefault text value (translation key)

Factory

typescript
content.textarea({
  label: '$label.content.description',
  placeholder: '$label.content.description_placeholder',
  defaults: { text: '$label.defaults.description' },
})

BUTTON

Button with configurable text, action type, and target. The button action type determines which additional fields are required.

Properties

PropertyTypeRequiredDescription
labelstringYesTranslation key for the editor label
defaultsobjectNoDefault values

Defaults

PropertyTypeRequiredDescription
titlestringYesDefault button text (translation key)
buttonTypestringYesAction type (see table below)
linkstringNoURL for HYPER_LINK type
linkTargetstringNoLink target (e.g., _blank)
emailstringNoEmail address for MAIL_LINK type
phonestringNoPhone number for TEL_LINK type
tileIdstringNoSection ID for SCROLL_TO_TILE type
categoryIdnumberNoCategory ID for GO_TO_CATEGORY types

Button Types

TypeRequired FieldsDescription
HYPER_LINKlinkNavigate to a URL
MAIL_LINKemailOpen email client
TEL_LINKphoneOpen phone dialer
SCROLL_TO_TILEtileIdScroll to a section on the page
GO_TO_CATEGORYcategoryIdNavigate to a store category
GO_TO_CATEGORY_LINKcategoryIdLink to a store category
GO_TO_STORENavigate to the store page
GO_TO_STORE_LINKLink to the store page
GO_TO_PAGENavigate to a specific page

Factory

typescript
content.button({
  label: '$label.content.cta_button',
  defaults: {
    title: '$label.defaults.cta',
    buttonType: 'HYPER_LINK',
    link: 'https://example.com',
  },
})

⚠️ Validation

Crane validates that required fields are present based on buttonType. For example, link is required when buttonType is HYPER_LINK, and email is required when buttonType is MAIL_LINK. Missing required fields produce build errors.

IMAGE

Image upload with optional static flag. Images support multiple resolutions for responsive rendering.

Properties

PropertyTypeRequiredDescription
labelstringYesTranslation key for the editor label
staticbooleanNoIf true, marks the image as static (non-editable by merchants). Must match the corresponding design editor's static flag
defaultsobjectNoDefault values

Defaults

PropertyTypeRequiredDescription
imageDataobjectYesImage data with set (image URLs by resolution) and optional borderInfo

The imageData.set object must contain at least one key from: ORIGINAL, WEBP_LOW_RES, WEBP_HI_2X_RES, MOBILE_WEBP_LOW_RES, MOBILE_WEBP_HI_RES. Each value has url (required), width (optional), and height (optional).

Factory

typescript
content.image({
  label: '$label.content.hero_image',
})

⚠️ Validation

If both a content IMAGE editor and a design IMAGE editor reference the same field, their static property values must match. Mismatches produce the error: "Both content and design editor need to have same value for attribute static."

TOGGLE

Boolean on/off switch for enabling or disabling features.

Properties

PropertyTypeRequiredDescription
labelstringYesTranslation key for the editor label
descriptionstringYesTranslation key for the toggle description
defaultsobjectNoDefault values

Defaults

PropertyTypeRequiredDescription
enabledbooleanYesDefault toggle state

Factory

typescript
content.toggle({
  label: '$label.content.show_overlay',
  description: '$label.content.show_overlay_description',
  defaults: { enabled: true },
})

SELECTBOX

Dropdown selection with predefined options.

Properties

PropertyTypeRequiredDescription
labelstringYesTranslation key for the editor label
placeholderstringYesTranslation key for placeholder text
descriptionstringYesTranslation key for description text
optionsarrayYesArray of selectable options (min 1)
defaultsobjectNoDefault values

Each option in options:

PropertyTypeRequiredDescription
valuestringYesOption value (must not be empty)
labelstringYesTranslation key for the option label

Defaults

PropertyTypeRequiredDescription
valuestringYesDefault selected value (must match one of the options values)

Factory

typescript
content.selectbox({
  label: '$label.content.alignment',
  placeholder: '$label.content.alignment_placeholder',
  description: '$label.content.alignment_description',
  options: [
    { value: 'left', label: '$label.options.left' },
    { value: 'center', label: '$label.options.center' },
    { value: 'right', label: '$label.options.right' },
  ],
  defaults: { value: 'center' },
})

⚠️ Validation

In showcases, selectbox default values must match one of the options defined in the content editor. Invalid option values produce the error: "Option value is not defined in content.ts options."

DECK

Collection of repeatable cards. Merchants can add, remove, and reorder cards. Each card contains its own set of content editors.

Properties

PropertyTypeRequiredDescription
labelstringYesTranslation key for the editor label
addButtonLabelstringYesTranslation key for the "Add card" button
maxCardsintegerYesMaximum number of cards (min 1)
cardsobjectYesCard configuration (see below)
defaultsobjectNoDefault values

Card Configuration

The cards object has the following structure:

typescript
cards: {
  defaultCardContent: {
    label: string;       // Translation key for the card label
    settings: Record<string, ContentEditor>;  // Editors within each card
  }
}

The settings record can contain any content editor type except DECK (no nesting), MENU, NAVIGATION_MENU, or LOGO. Each card gets a copy of these settings.

Factory

typescript
content.deck({
  label: '$label.content.slides',
  addButtonLabel: '$label.content.add_slide',
  maxCards: 10,
  cards: {
    defaultCardContent: {
      label: '$label.content.slide',
      settings: {
        slide_image: content.image({ label: '$label.content.slide_image' }),
        slide_title: content.inputbox({
          label: '$label.content.slide_title',
          placeholder: '$label.content.slide_title_placeholder',
          defaults: { text: '$label.defaults.slide_title' },
        }),
      },
    },
  },
})

⚠️ Validation

In showcases, deck card settings are validated against the defaultCardContent.settings definition. Settings keys that don't exist in the deck definition produce errors.

Menu items list. The label property is optional for this editor.

Properties

PropertyTypeRequiredDescription
labelstringNoTranslation key for the editor label
defaultsobjectNoDefault values

Defaults

PropertyTypeRequiredDescription
itemsarrayNoArray of menu items

Each menu item:

PropertyTypeRequiredDescription
idstringYesUnique identifier
titlestringNoDisplay text
typestringNoAction type (same as button types, plus GO_TO_PAGE)
linkstringNoURL (for HYPER_LINK)
emailstringNoEmail (for MAIL_LINK)
phonestringNoPhone (for TEL_LINK)
tileIdForScrollstringNoSection ID (for SCROLL_TO_TILE)
pageIdForNavigatestringNoPage ID (for GO_TO_PAGE)
showStoreCategoriesbooleanNoShow category submenu
isSubmenuOpenedbooleanNoSubmenu expanded state
categoryIdnumberNoCategory ID (for GO_TO_CATEGORY)

Factory

typescript
content.menu({
  label: '$label.content.footer_links',
  defaults: {
    items: [
      { id: 'about', title: 'About', type: 'HYPER_LINK', link: '/about' },
      { id: 'contact', title: 'Contact', type: 'MAIL_LINK', email: 'info@example.com' },
    ],
  },
})

Navigation menu for site headers. Similar to MENU but without a configurable label — it is always rendered as the main navigation.

Properties

PropertyTypeRequiredDescription
defaultsobjectNoDefault values

Defaults

Same structure as MENU defaults.

Factory

typescript
content.navigationMenu({
  defaults: {
    items: [
      { id: 'home', title: 'Home', type: 'GO_TO_PAGE', pageIdForNavigate: 'home' },
      { id: 'shop', title: 'Shop', type: 'GO_TO_STORE' },
    ],
  },
})

ℹ️ Mandatory for Headers

The NAVIGATION_MENU editor is mandatory for sections with type HEADER. It must be defined with the key menu. See Sections.

Logo element supporting both text and image modes.

Properties

PropertyTypeRequiredDescription
labelstringNoTranslation key for the editor label
defaultsobjectNoDefault values

Defaults

PropertyTypeRequiredDescription
logoTypestringNo"TEXT" or "IMAGE"
textstringNoDefault logo text (translation key)
imageDataobjectNoDefault logo image (same structure as IMAGE defaults)

Factory

typescript
content.logo({
  label: '$label.content.store_logo',
  defaults: { logoType: 'TEXT', text: '$label.defaults.store_name' },
})

ℹ️ Mandatory for Headers

The LOGO editor is mandatory for sections with type HEADER. It must be defined with the key logo. See Sections.

DIVIDER

Visual separator in the editor UI. Does not render any content in the storefront — it is purely for organizing the editor panel.

Properties

PropertyTypeRequiredDescription
labelstringYesTranslation key for the divider label
defaultsobjectNoDefault values (empty, type only)

Factory

typescript
content.divider({
  label: '$label.content.advanced_section',
})

INFO

Informational text displayed in the editor UI. Like DIVIDER, this does not render in the storefront — it provides guidance to merchants.

Properties

PropertyTypeRequiredDescription
labelstringYesTranslation key for the info title
descriptionstringYesTranslation key for the info description
buttonobjectNoOptional link button in the editor
defaultsobjectNoDefault values

The button object:

PropertyTypeRequiredDescription
labelstringYesTranslation key for button text
linkstringYesURL (must be a valid URL)

Defaults

PropertyTypeRequiredDescription
textstringNoDefault info text (translation key)
buttonobjectNo{ title?: string, link?: string }

Factory

typescript
content.info({
  label: '$label.content.help_info',
  description: '$label.content.help_description',
  button: { label: '$label.content.learn_more', link: 'https://docs.example.com' },
})

PRODUCT_SELECTOR

Product picker allowing merchants to select products from their store catalog.

Properties

PropertyTypeRequiredDescription
labelstringYesTranslation key for the editor label
maxProductsintegerYesMaximum number of products (1–32)
defaultsobjectNoDefault values (empty, type only)

Factory

typescript
content.productSelector({
  label: '$label.content.featured_products',
  maxProducts: 8,
})

CATEGORY_SELECTOR

Category picker allowing merchants to select store categories.

Properties

PropertyTypeRequiredDescription
labelstringYesTranslation key for the editor label
maxCategoriesintegerYesMaximum number of categories (1–32)
defaultsobjectNoDefault values (empty, type only)

Factory

typescript
content.categorySelector({
  label: '$label.content.featured_categories',
  maxCategories: 6,
})

Showcase Default Factories

When defining showcase configurations, you need to provide default values for content fields independently of the editor definition. The content.default.*() factories create these standalone defaults:

typescript
import { content } from '@lightspeed/crane-api';

// Used in showcase files, not in content.ts
content.default.inputbox({ text: '$label.showcase.title' });
content.default.textarea({ text: '$label.showcase.description' });
content.default.button({ title: '$label.showcase.cta', buttonType: 'HYPER_LINK', link: '/' });
content.default.image({ imageData: { set: { ORIGINAL: { url: '/assets/hero.webp' } } } });
content.default.toggle({ enabled: false });
content.default.selectbox({ value: 'left' });

These factories auto-inject the type discriminant. See Showcases for usage in showcase files.

Complete Example

typescript
// settings/content.ts
import { content } from '@lightspeed/crane-api';

export default {
  title: content.inputbox({
    label: '$label.content.title',
    placeholder: '$label.content.title_placeholder',
    defaults: { text: '$label.defaults.title' },
  }),
  description: content.textarea({
    label: '$label.content.description',
    placeholder: '$label.content.description_placeholder',
    defaults: { text: '$label.defaults.description' },
  }),
  hero_image: content.image({
    label: '$label.content.hero_image',
  }),
  cta_button: content.button({
    label: '$label.content.cta_button',
    defaults: {
      title: '$label.defaults.cta',
      buttonType: 'HYPER_LINK',
      link: 'https://example.com',
    },
  }),
  show_overlay: content.toggle({
    label: '$label.content.show_overlay',
    description: '$label.content.show_overlay_desc',
    defaults: { enabled: true },
  }),
};