Pular para o conteúdo

Schema e conteúdo externo

O schema define o que o lojista pode configurar no editor. O Liquid recebe os valores em component_configs.

buttons e icons não têm campos editáveis. Para esses tipos, use somente o schema técnico { "component_config": {} }. Os demais tipos usam o schema mínimo abaixo.

{
"title": "Meu componente",
"description": "",
"component_config": {
"settings": {},
"content": {}
}
}

Use somente estas chaves na raiz do schema:

ChaveObrigatóriaUso
titleSim, exceto buttons e iconsNome mostrado no editor.
descriptionSim, exceto buttons e iconsExplicação curta para o lojista.
item_labelNãoRótulo usado para cada item repetível.
component_configSimContém os blocos editáveis.
defaultContentNãoValores iniciais ao adicionar o componente.
requires_external_contentNãoTags ou instruções de configuração administrativa.
content_limitNãoLimite de itens em content.items.
content_limit_imagesNãoLimite de imagens adicionadas em listas.

Em component_config, declare apenas os blocos abaixo. A distinção entre settings e content não é cosmética: settings guarda configuração visual - o que define a aparência do componente, independente de qual conteúdo está dentro - e content guarda o editorial - o que o lojista preenche em cada seção. Cor de fundo vai em settings; título e imagem vão em content. Misturar os dois dificulta a experiência no editor e o fallback mobile.

BlocoUsoAcesso no Liquid
settingsCores, espaçamentos, fontes e comportamento visual.component_configs.settings.campo
contentTextos, imagens, links e listas.component_configs.content.campo
settings_mobileAjustes exclusivos para mobile. Opt-in.component_configs.settings_mobile.campo
content_mobileConteúdo exclusivo para mobile. Opt-in.component_configs.content_mobile.campo

Campos fora desses blocos não se tornam opções editáveis.

Um campo é um objeto dentro de um bloco editável:

"title_color": {
"type": "string",
"title": "Cor do título",
"description": "Use uma cor hexadecimal.",
"default": "#03060B"
}
PropriedadeValores aceitosUso
typestring, boolean, number, integerTipo do valor.
titleTextoRótulo mostrado no editor.
descriptionTextoAjuda adicional.
defaultValor compatível com o tipoValor inicial do campo.
enumLista de valoresSelect simples.
oneOfLista de opções com const e titleSelect com rótulos.
formatmarkdown, textarea, date, date-timeVariante de campo.
minimum, maximumNúmeroLimites numéricos.
ui:widgetimageUpload, markdown, textareaVariante explícita de input.

Objetos e arrays genéricos não formam campos editáveis: use campos simples e a convenção items para listas.

Use a tabela abaixo para escolher o mecanismo correto para cada tipo de input:

QueroComo declararO que aparece no editor
Texto curto livretype: stringCampo de texto
Texto longotype: string + format: textareaTextarea redimensionável
Texto com formataçãotype: string + format: markdownEditor markdown
Cortype: string com color no nome do campoColor picker (hex)
Upload de imagemtype: string com image_url, imageurl, logo_url ou icon_url no nomeBotão de upload
Número com limitestype: number + minimum + maximumInput numérico
Liga / desligatype: booleanCheckbox
Select simplestype: string + enum: [...]Dropdown com os valores brutos
Select com rótulostype: string + oneOf: [{const, title}]Dropdown com textos legíveis
Data e horatype: string + format: date-timeSeletor de data e hora

O color picker abre automaticamente quando o nome do campo contém a substring color e o tipo é string. Nenhum ui:widget adicional é necessário.

"background_color": {
"type": "string",
"title": "Cor de fundo",
"default": "#FFFFFF"
}

No Liquid, use o valor diretamente como CSS variable ou atributo style:

<section style="background-color: {{ component_configs.settings.background_color }};">

Campos cujo nome contém image_url, imageurl, logo_url ou icon_url renderizam um botão de upload no editor. O lojista escolhe o arquivo e o campo recebe a URL resultante automaticamente.

"banner_image_url": {
"type": "string",
"title": "Imagem do banner"
}

No Liquid, sempre verifique se o campo está preenchido antes de renderizar. Use a classe .image para reservar o espaço da imagem antes do carregamento e data-src com class="lazy" para ativar o lazyload da loja:

{% if content.banner_image_url != blank %}
<figure class="image -horizontal">
<img
class="lazy"
loading="lazy"
data-src="{{ content.banner_image_url }}"
alt="{{ content.title }}"
/>
</figure>
{% endif %}

A variante de proporção (-square, -horizontal, -vertical) e a lógica de lazyload estão detalhadas em Base de estilos.

Quando o campo de imagem estiver dentro de items, acesse via item.foto_image_url:

{% for item in content.items %}
{% if item.foto_image_url != blank %}
<figure class="image -square">
<img class="lazy" loading="lazy" data-src="{{ item.foto_image_url }}" alt="{{ item.title }}" />
</figure>
{% endif %}
{% endfor %}

Use enum quando os valores brutos forem legíveis para o lojista:

"alignment": {
"type": "string",
"title": "Alinhamento",
"enum": ["left", "center", "right"],
"default": "left"
}

Use oneOf quando quiser rótulos diferentes dos valores internos:

"alignment": {
"type": "string",
"title": "Alinhamento",
"oneOf": [
{ "const": "left", "title": "Esquerda" },
{ "const": "center", "title": "Centro" },
{ "const": "right", "title": "Direita" }
],
"default": "left"
}

Um campo boolean renderiza como checkbox. Use default: true para marcar por padrão.

"show_title": {
"type": "boolean",
"title": "Exibir título",
"default": true
}

No Liquid, compare com == true ou == false:

{% if component_configs.settings.show_title == true %}
<h2>{{ component_configs.content.title }}</h2>
{% endif %}

Use format: markdown para abrir o editor markdown. Evite nomear o campo description dentro de settings, pois o nome description já ativa o editor de markdown independentemente do bloco - prefira nomes como body_text ou caption.

"body_text": {
"type": "string",
"title": "Texto",
"format": "markdown"
}

Use format: date-time quando o conteúdo tiver período de exibição - por exemplo, um banner de campanha que deve aparecer entre datas específicas. O editor mostra um seletor de data e hora; o valor salvo é uma string ISO 8601.

O valor especial "current_timestamp" no default inicializa o campo com o momento atual:

"start_at": {
"type": "string",
"format": "date-time",
"title": "Exibir a partir de",
"default": "current_timestamp"
},
"end_at": {
"type": "string",
"format": "date-time",
"title": "Exibir até"
}

No Liquid, converta as datas para Unix timestamp e compare com o momento atual:

{% assign date_now = "now" | date: "%s" %}
{% assign start_at = nil %}
{% assign end_at = nil %}
{% if item.start_at and item.start_at != "" %}
{% assign start_at = item.start_at | date: "%s" %}
{% endif %}
{% if item.end_at and item.end_at != "" %}
{% assign end_at = item.end_at | date: "%s" %}
{% endif %}
{% if start_at == nil or start_at <= date_now %}
{% if end_at == nil or end_at >= date_now %}
{# conteúdo visível dentro do período #}
{% endif %}
{% endif %}

Quando start_at estiver vazio, o conteúdo aparece imediatamente. Quando end_at estiver vazio, não há prazo de encerramento.

Use items para uma lista repetível em que o lojista pode adicionar, remover e reordenar registros.

"content": {
"items": [
{
"title": { "type": "string", "title": "Título" },
"image_url": { "type": "string", "title": "Imagem" }
}
]
}

items só é válido em content, content_mobile ou dentro de preferences. Não use listas em settings.

Use preferences quando uma coleção precisar de opções compartilhadas e de uma lista própria.

"content": {
"socials": {
"title": "Redes sociais",
"preferences": {
"icon_color": {
"type": "string",
"title": "Cor dos ícones",
"default": "#FFFFFF"
},
"items": [
{
"title": { "type": "string", "title": "Rede" },
"link_url": { "type": "string", "title": "URL" }
}
]
}
}
}

No Liquid, aplique o fallback e percorra a lista:

{% assign socials = component_configs.content_mobile.socials.preferences | default: component_configs.content.socials.preferences %}
<ul style="--social-icon-color: {{ socials.icon_color }};">
{% for item in socials.items %}
{% if item.title != blank and item.link_url != blank %}
<li><a href="{{ item.link_url }}">{{ item.title }}</a></li>
{% endif %}
{% endfor %}
</ul>

content_limit limita content.items. content_limit_images limita imagens adicionadas em listas do componente.

settings e content são a configuração base. settings_mobile e content_mobile são opt-in: declare-os apenas se o resultado mobile realmente precisar divergir. O Liquid deve usar a base quando não houver valor mobile:

{% assign settings = component_configs.settings_mobile | default: component_configs.settings %}
{% assign content = component_configs.content_mobile | default: component_configs.content %}

Quando o lojista adiciona um componente à página pela primeira vez, os campos sem defaultContent nem default próprio aparecem vazios. Sem esses valores iniciais, o componente pode renderizar com títulos em branco, imagens ausentes ou seções visualmente quebradas logo na prévia inicial.

defaultContent permite definir os valores que o componente já terá ao ser adicionado, antes de o lojista tocar em qualquer configuração. É especialmente importante em campos de content que fazem parte da estrutura visual - títulos, imagens de exemplo, textos de placeholder - porque são eles que definem a primeira impressão.

"defaultContent": {
"settings": { "background_color": "#F5F5F5" },
"content": {
"title": "Título da seção",
"body_text": "Descreva aqui o conteúdo desta seção.",
"items": [
{ "title": "Item 1", "link_url": "#" },
{ "title": "Item 2", "link_url": "#" }
]
}
}

defaultContent também pode inicializar configurações mobile separadas:

"defaultContent": {
"settings": { "background_color": "#03060B" },
"content": { "title": "Novidade" },
"settings_mobile": { "background_color": "#FFFFFF" },
"content_mobile": { "title": "Novidade no mobile" }
}

A precedência ao exibir um valor é: defaultContent -> default do campo -> valor vazio.

Campos de settings como cores e espaçamentos geralmente têm default direto no campo, o que já é suficiente. Para content - textos, imagens, listas - prefira defaultContent, que inicializa o componente completo de uma vez e garante uma prévia coerente desde a primeira adição.

Use requires_external_content quando o componente depende de uma ação no Painel Administrativo, como associar produtos a tags, configurar menus ou cadastrar formulários.

"requires_external_content": {
"type": "manual",
"instructions": {
"title": "Configuração necessária",
"steps": [
"1. Acesse [Menus]({{ADMIN_URL}}/navegacao)",
"2. Crie ou atualize o menu usado pelo componente"
]
}
}

instructions.title aparece para o lojista e instructions.steps é uma lista ordenada em Markdown. Todo link administrativo deve usar {{ADMIN_URL}}, nunca uma URL fixa ou caminho relativo.

RecursoLink nos steps
Menus[Menus]({{ADMIN_URL}}/navegacao)
Tags[Tags]({{ADMIN_URL}}/tags)
Produtos[Produtos]({{ADMIN_URL}}/produtos)
Forms[Forms]({{ADMIN_URL}}/config/mensagens-e-avisos/forms)

Use static quando cada ocorrência da seção deve ter uma coleção própria. static_tag_name é obrigatório.

"requires_external_content": {
"type": "static",
"static_tag_name": "home-produtos",
"instructions": {
"title": "Associe produtos à vitrine",
"steps": [
"1. Use a tag sugerida nesta seção",
"2. Acesse [Tags]({{ADMIN_URL}}/tags) e [Produtos]({{ADMIN_URL}}/produtos)",
"3. Vincule a tag aos produtos desejados"
]
}
}

Ao adicionar a primeira ocorrência, a loja insere automaticamente content.tag_name com home-produtos-1. Duplicá-la gera home-produtos-2; duplicar novamente gera home-produtos-3. Reordenar não muda as tags existentes nem suas associações. content.tag_name apenas identifica a coleção: carregue os produtos conforme Vitrines de produtos.

Use dynamic quando cada item de content.items representa uma coleção. prefix e o campo title no modelo de cada item são obrigatórios para esse contrato.

"requires_external_content": {
"type": "dynamic",
"prefix": "home-tab-",
"instructions": {
"title": "Associe produtos às abas",
"steps": ["1. Use a tag sugerida em cada aba"]
}
},
"component_config": {
"settings": {},
"content": {
"items": [
{ "title": { "type": "string", "title": "Título da aba" } }
]
}
}

Cada tag fica em item.tag_name e combina o prefixo ao título normalizado: Promoções de Verão gera home-tab-promocoes-de-verao. Um item sem título ainda não gera tag. Alterar o título recalcula a sugestão, então revise os produtos associados. Duplicar preserva título e sugestão; altere o título da cópia para criar outra coleção. Títulos iguais geram a mesma sugestão e não devem representar coleções diferentes.

Use manual quando não há tag automática. Os steps devem explicar o cadastro administrativo que permite o funcionamento do componente, como menus, formulários ou outro recurso complementar. Veja Formulários, Exemplos e Troubleshooting.