O que Padrões Sanity faz?
Guia Rápido: Use a Sanity para gerenciamento de conteúdo estruturado com consultas GROQ, esquemas tipados via
defineType/defineField, e@sanity/clientpara busca de dados. Sempre definaapiVersionpara uma string datada, useuseCdn: truepara leituras públicas, trate documentos de rascunho explicitamente, use@sanity/image-urlpara transformações de imagem e renderize rich text com@portabletext/react. Gere tipos TypeScript comsanity typegen generate.
<critical_requirements>
CRÍTICO: Antes de Usar Este Skill
Todo código deve seguir as convenções do projeto no CLAUDE.md (kebab-case, exports nomeados, ordenação de imports,
import type, constantes nomeadas)
(Você DEVE sempre definir apiVersion no createClient com uma string datada como '2025-02-19' — omitir isso usa uma API legada que pode quebrar)
(Você DEVE usar useCdn: true para consultas de leitura públicas e useCdn: false ao usar um token ou precisar de dados atualizados)
(Você DEVE usar consultas GROQ parametrizadas ($param) para qualquer valor dinâmico — nunca interpole entrada do usuário em strings GROQ)
(Você DEVE tratar rascunhos explicitamente — documentos de rascunho têm _id prefixado com drafts. e não são retornados por padrão com perspective: 'published')
(Você DEVE usar defineQuery() de groq e atribuir consultas a variáveis nomeadas para geração de tipos TypeGen)
</critical_requirements>
Detecção Automática: Sanity, sanity, @sanity/client, createClient, GROQ, groq, defineType, defineField, defineArrayMember, @sanity/image-url, urlFor, @portabletext/react, PortableText, portable text, block content, sanity.config, sanity.cli, typegen, sanity studio, content lake
Quando usar:
- Configurando
@sanity/clientcomcreateClientpara busca de dados - Escrevendo consultas GROQ (filtros, projeções, junções, ordenação, fatiamento)
- Definindo esquemas de conteúdo com
defineType,defineField,defineArrayMember - Renderizando Portable Text (conteúdo de bloco) com
@portabletext/react - Gerando URLs de imagem com
@sanity/image-url(imagens responsivas, recortes, hotspots) - Realizando mutações (criar, atualizar, deletar, transações)
- Configurando listeners em tempo real com
client.listen() - Gerando tipos TypeScript com Sanity TypeGen
Padrões chave abordados:
- Configuração do cliente com
createCliente configuração deapiVersion - Linguagem de consulta GROQ: filtros, projeções, ordenação, fatiamento, junções, referências
- Definições de esquema: tipos de documento, tipos de objeto, arrays, referências, imagens
- Renderização de Portable Text com componentes personalizados
- Construtor de URLs de imagem com imagens responsivas e transformações
- Mutações: criar, createOrReplace, atualizar, deletar, transações
- Listeners em tempo real via
client.listen() - TypeGen para consultas GROQ type-safe com
defineQuery()
Quando NÃO usar:
- APIs exclusivamente GraphQL (a Sanity suporta GROQ principalmente; use o skill GraphQL se necessário)
- Acesso direto a banco de dados (a Sanity é um content lake hospedado, não um banco de dados)
- Plataformas CMS que não são Sanity (use o skill dedicado para seu CMS)
Recursos Detalhados:
- Para frameworks de decisão e tabelas de referência rápida, veja reference.md
Cliente & GROQ:
- examples/core.md — Configuração do cliente, consultas GROQ, tratamento de erros, TypeGen
Esquemas:
- examples/schemas.md — defineType, defineField, tipos de documento, tipos de objeto, referências, imagens
Conteúdo Rico:
- examples/rich-content.md — Renderização de Portable Text, construtor de URLs de imagem, imagens responsivas
Mutações & Tempo Real:
- examples/mutations.md — Criar, atualizar, deletar, transações, listeners em tempo real
Filosofia
A Sanity é uma plataforma de conteúdo estruturado construída em torno de um content lake em tempo real, GROQ (Graph-Relational Object Queries) como sua linguagem de consulta, e o Sanity Studio como um ambiente de edição personalizável.
Princípios fundamentais:
- Conteúdo estruturado — O conteúdo é definido por esquemas (
defineType,defineField) que descrevem forma, validação e UI editorial. Esquemas são código, não arquivos de configuração. - Consulta com GROQ em primeiro lugar — GROQ permite filtrar, projetar, juntar e remodelar dados em uma única consulta. Diferente de REST ou GraphQL, as consultas GROQ retornam exatamente a forma que você define na projeção.
- Conteúdo como dados — Rich text é armazenado como Portable Text (uma especificação baseada em JSON), tornando-o renderizável em qualquer framework frontend sem dependência de fornecedor.
- Versionamento de API — Todo cliente deve especificar uma string de data
apiVersion. Isso fixa seu código em um comportamento específico da API, evitando que mudanças que quebrem afetem a produção. - Cache CDN — Consultas de leitura públicas usam
useCdn: truepara respostas em cache na borda. Mutações e leituras autenticadas usamuseCdn: falsepara dados atualizados. - Geração de tipos — Sanity TypeGen gera tipos TypeScript tanto dos seus esquemas quanto das consultas GROQ, permitindo segurança de tipos de ponta a ponta, do modelo de conteúdo ao frontend.
Quando usar a Sanity:
- Sites e aplicações orientados a conteúdo (blogs, sites de marketing, documentação)
- Projetos que precisam de edição colaborativa em tempo real em um studio personalizável
- Entrega de conteúdo multicanal (web, mobile, IoT) de uma única fonte de conteúdo
- Equipes que desejam consultas de conteúdo type-safe com GROQ e TypeGen
Quando NÃO usar:
- Dados transacionais que exigem garantias ACID (use um banco de dados)
- Conteúdo gerado por usuário em escala massiva (a Sanity é otimizada para conteúdo editorial)
- Projetos que precisam de um CMS auto-hospedado (o content lake da Sanity é hospedado, embora o Studio seja open source)
Padrões Principais
Padrão 1: Configuração do Cliente com createClient
Configure @sanity/client com ID do projeto, dataset, versão da API e preferência de CDN. Sempre defina apiVersion como uma string datada e useCdn explicitamente.
import { createClient } from "@sanity/client";
export const client = createClient({
projectId: process.env.SANITY_PROJECT_ID!,
dataset: process.env.SANITY_DATASET!,
apiVersion: "2025-02-19", // Fixar em uma data de versão de API específica
useCdn: true, // true para leituras públicas, false para dados autenticados/atualizados
});
Para configuração de cliente duplo (público + preview com token), veja examples/core.md.
Padrão 2: Consultas GROQ com Filtros e Projeções
Consultas GROQ combinam filtros, projeções, ordenação e fatiamento. Sempre use defineQuery() para TypeGen e $param para valores dinâmicos.
import { defineQuery } from "groq";
const POST_BY_SLUG_QUERY = defineQuery(`
*[_type == "post" && slug.current == $slug][0]{
_id, title, body, "author": author->{name, image}
}
`);
const post = await client.fetch(POST_BY_SLUG_QUERY, { slug: "my-post" });
Nunca interpole entrada do usuário em strings GROQ — sempre use parâmetros $param para prevenir injeção GROQ. Para consultas avançadas (consultas combinadas, projeções condicionais), veja examples/core.md.
Padrão 3: Definições de Esquema com defineType e defineField
Defina a estrutura do conteúdo com defineType, defineField e defineArrayMember de "sanity" para segurança de tipos e UI do Studio.
import { defineType, defineField } from "sanity";
export const postType = defineType({
name: "post",
type: "document",
fields: [
defineField({
name: "title",
type: "string",
validation: (r) => r.required(),
}),
defineField({ name: "slug", type: "slug", options: { source: "title" } }),
],
});
Para esquemas completos (imagens com hotspot, arrays, referências, previews, tipos de objeto), veja examples/schemas.md.
Padrão 4: Renderização de Portable Text
Renderize conteúdo de bloco com @portabletext/react. Defina PortableTextComponents personalizados para blocos não padrão (imagens, código) e marks (links, destaques).
import { PortableText } from "@portabletext/react";
<PortableText value={body} components={components} />;
Não use o pacote obsoleto @sanity/block-content-to-react. Para exemplos completos de componentes, veja examples/rich-content.md.
Padrão 5: Construtor de URLs de Imagem
Use @sanity/image-url para gerar URLs de imagem otimizadas e responsivas com suporte a recorte e hotspot.
import { createImageUrlBuilder } from "@sanity/image-url";
const builder = createImageUrlBuilder(client);
export function urlFor(source: SanityImageSource) {
return builder.image(source);
}
// Uso: urlFor(image).width(800).auto("format").url()
Para padrões de srcSet responsivo e transformações de imagem, veja examples/rich-content.md.
Padrão 6: Mutações (Criar, Atualizar, Deletar)
Use os métodos de @sanity/client para mutações de documentos. Sempre chame .commit() em patches e transações.
await client.create({ _type: "post", title: "Novo Post" });
await client.patch("post-123").set({ title: "Atualizado" }).commit();
await client.delete("post-123");
Para createOrReplace, createIfNotExists, transações, inserções em arrays e opções de visibilidade, veja examples/mutations.md.
Padrão 7: Listeners em Tempo Real
Inscreva-se em mudanças de documentos com client.listen(). O listener usa apenas a parte de filtro do GROQ — projeções e ordenação são ignoradas.
const subscription = client.listen(`*[_type == "post"]`).subscribe({
next: (update) => {
/* update.transition: 'update' | 'appear' | 'disappear' */
},
error: (err) => console.error(err),
});
subscription.unsubscribe(); // Limpeza ao terminar
Para frontends de produção, avalie a Live Content API mais nova como uma alternativa mais simples. Para opções e ressalvas do listener, veja examples/mutations.md.
Padrão 8: TypeGen para GROQ Type-Safe
Configure TypeGen em sanity.cli.ts com overloadClientMethods: true para resultados tipados de client.fetch. Use defineQuery() de "groq" para tornar as consultas detectáveis pelo TypeGen.
// sanity.cli.ts — defina typegen.overloadClientMethods: true
// queries/post-queries.ts — envolva consultas com defineQuery()
// sanity.types.ts — tipos de resultado gerados automaticamente
const posts = await client.fetch(allPostsQuery); // Resultado tipado
Strings de consulta inline sem defineQuery() produzem resultados sem tipo (any). Para configuração completa e fluxo de trabalho do TypeGen, veja examples/core.md.
<decision_framework>
Framework de Decisão
useCdn: true vs false
Os dados são públicos e não personalizados?
├─ SIM → useCdn: true (cache na borda, rápido)
└─ NÃO →
├─ Usando um token para leituras autenticadas? → useCdn: false
├─ Precisa de dados atualizados em tempo real (preview)? → useCdn: false
└─ Realizando mutações? → useCdn: false
Tratamento de Rascunhos
Precisa de documentos de rascunho?
├─ SIM → Use perspective: 'previewDrafts' (requer token)
├─ NÃO → Use perspective: 'published' (padrão desde a API v2025-02-19)
└─ Misto (alternar modo de preview)?
└─ Crie dois clientes: um público (useCdn: true), um preview (token + useCdn: false)
Forma de Retorno da Consulta GROQ
Quantos documentos você espera?
├─ Um (por ID, slug, singleton) → Adicione [0] no final (retorna objeto ou null)
├─ Muitos (lista, feed) → Sem sufixo de fatia (retorna array)
└─ Paginado → Adicione fatia [start...end]
Manipulação de Imagens
Como as imagens devem ser entregues?
├─ Tamanho fixo (thumbnails, avatares) → urlFor(img).width(W).height(H).url()
├─ Responsivo (imagens de artigos) → srcSet com múltiplas larguras
├─ Otimização de formato → .auto('format') para WebP/AVIF
└─ Cortado para proporção → .width(W).height(H).fit('crop')
Seleção de Método de Mutação
Qual operação você precisa?
├─ Criar novo documento → client.create()
├─ Criar ou substituir completamente → client.createOrReplace() (para singletons)
├─ Criar apenas se faltar → client.createIfNotExists()
├─ Atualizar campos específicos → client.patch(id).set({...}).commit()
├─ Remover campos → client.patch(id).unset([...]).commit()
├─ Múltiplas alterações relacionadas → client.transaction()...commit()
└─ Deletar documento → client.delete(id)
</decision_framework>
<red_flags>
BANDEIRAS VERMELHAS
Problemas de Alta Prioridade:
apiVersionausente no cliente — OmitirapiVersionusa comportamento de API legado que pode mudar sem aviso. Sempre fixe em uma string de data.- Interpolação de string em consultas GROQ — Interpolar entrada do usuário em strings GROQ permite injeção GROQ. Sempre use parâmetros
$param. - Usar
useCdn: truecom um token — Respostas em cache CDN ignoram tokens de autenticação. Consultas autenticadas devem usaruseCdn: false. - Acessar documentos de rascunho sem um token — Rascunhos (documentos
drafts.*) requerem um token de API eperspective: 'previewDrafts'. Sem um token, os rascunhos são invisíveis.
Problemas de Média Prioridade:
- Buscar todos os campos com
{...}quando apenas alguns são necessários — Buscar demais desperdiça largura de banda e eficiência de cache CDN. Projete apenas os campos que precisa. - Falta de
.commit()em patches —client.patch(id).set({...})sem.commit()não faz nada — a mutação nunca é enviada. - Não usar
defineQuery()para consultas GROQ — TypeGen não pode gerar tipos para consultas que não estão envoltas emdefineQuery()ou atribuídas a variáveis nomeadas. - ID do projeto ou dataset fixo no código — Use variáveis de ambiente; valores fixos impedem a troca de ambiente e vazam detalhes do projeto.
- Usar
@sanity/block-content-to-reactobsoleto — Substituído por@portabletext/react. O pacote antigo não é mantido.
Erros Comuns:
- Esquecer
_keyem itens de array em mutações — Cada item em um array da Sanity deve ter um campo_keyúnico. Mutações sem_keyfalharão. - Usar
_idcomclient.create()—create()gera um_idaleatório. Se você especificar_ide o documento existir, ocorrerá erro. UsecreateOrReplace()oucreateIfNotExists()para operações idempotentes. - Não tratar o caso onde
[0]retornanull— Uma consulta GROQ com[0]retornanullse nenhum documento corresponder. Sempre verifique se o resultado não énullantes de acessar propriedades.
</red_flags>