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": {} }}Estrutura aceita
Seção intitulada “Estrutura aceita”Use somente estas chaves na raiz do schema:
| Chave | Obrigatória | Uso |
|---|---|---|
title | Sim, exceto buttons e icons | Nome mostrado no editor. |
description | Sim, exceto buttons e icons | Explicação curta para o lojista. |
item_label | Não | Rótulo usado para cada item repetível. |
component_config | Sim | Contém os blocos editáveis. |
defaultContent | Não | Valores iniciais ao adicionar o componente. |
requires_external_content | Não | Tags ou instruções de configuração administrativa. |
content_limit | Não | Limite de itens em content.items. |
content_limit_images | Não | Limite 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.
| Bloco | Uso | Acesso no Liquid |
|---|---|---|
settings | Cores, espaçamentos, fontes e comportamento visual. | component_configs.settings.campo |
content | Textos, imagens, links e listas. | component_configs.content.campo |
settings_mobile | Ajustes exclusivos para mobile. Opt-in. | component_configs.settings_mobile.campo |
content_mobile | Conteúdo exclusivo para mobile. Opt-in. | component_configs.content_mobile.campo |
Campos fora desses blocos não se tornam opções editáveis.
Campos aceitos
Seção intitulada “Campos aceitos”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"}| Propriedade | Valores aceitos | Uso |
|---|---|---|
type | string, boolean, number, integer | Tipo do valor. |
title | Texto | Rótulo mostrado no editor. |
description | Texto | Ajuda adicional. |
default | Valor compatível com o tipo | Valor inicial do campo. |
enum | Lista de valores | Select simples. |
oneOf | Lista de opções com const e title | Select com rótulos. |
format | markdown, textarea, date, date-time | Variante de campo. |
minimum, maximum | Número | Limites numéricos. |
ui:widget | imageUpload, markdown, textarea | Variante 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.
Receitas de campo
Seção intitulada “Receitas de campo”Use a tabela abaixo para escolher o mecanismo correto para cada tipo de input:
| Quero | Como declarar | O que aparece no editor |
|---|---|---|
| Texto curto livre | type: string | Campo de texto |
| Texto longo | type: string + format: textarea | Textarea redimensionável |
| Texto com formatação | type: string + format: markdown | Editor markdown |
| Cor | type: string com color no nome do campo | Color picker (hex) |
| Upload de imagem | type: string com image_url, imageurl, logo_url ou icon_url no nome | Botão de upload |
| Número com limites | type: number + minimum + maximum | Input numérico |
| Liga / desliga | type: boolean | Checkbox |
| Select simples | type: string + enum: [...] | Dropdown com os valores brutos |
| Select com rótulos | type: string + oneOf: [{const, title}] | Dropdown com textos legíveis |
| Data e hora | type: string + format: date-time | Seletor 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 }};">Upload de imagem
Seção intitulada “Upload de imagem”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 %}Select simples e select com rótulos
Seção intitulada “Select simples e select com rótulos”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"}Checkbox (boolean)
Seção intitulada “Checkbox (boolean)”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 %}Texto formatado em markdown
Seção intitulada “Texto formatado em markdown”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"}Data e hora
Seção intitulada “Data e hora”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.
Listas e preferences
Seção intitulada “Listas e preferences”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.
Mobile e valores iniciais
Seção intitulada “Mobile e valores iniciais”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 %}defaultContent e a primeira renderização
Seção intitulada “defaultContent e a primeira renderização”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.
Conteúdo externo
Seção intitulada “Conteúdo externo”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.
| Recurso | Link 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) |
Tag estática
Seção intitulada “Tag estática”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.
Tag dinâmica
Seção intitulada “Tag dinâmica”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.
Instruções manuais
Seção intitulada “Instruções manuais”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.