Usando Airtable como CMS com Astro & Next.js em 2026
Vou ser sincero: quando um cliente me pediu para usar Airtable como CMS em 2023, pensei que estava brincando. Um app de planilha alimentando um site em produção? Mas depois de construir meia dúzia de sites assim — alguns com Astro, alguns com Next.js — mudei de ideia. Airtable atinge um ponto ideal para certos projetos que plataformas headless CMS tradicionais completamente perdem. Seu time de marketing já sabe como usá-lo. É flexível o suficiente para modelar a maioria dos conteúdos. E a API é simples demais.
Mas não está isento de arestas afiadas. Rate limits, manipulação de anexos, peculiaridades de dados relacionais — há muito que os posts de blog sobre "Airtable como CMS" de 2023 nunca te contaram. Este guia cobre tudo o que aprendi lançando projetos reais com essa stack em 2026.
Índice
- Por que Airtable como CMS realmente faz sentido
- Quando NÃO usar Airtable como CMS
- Configurando sua base Airtable para conteúdo
- Conectando Airtable ao Astro
- Conectando Airtable ao Next.js
- Manipulando imagens e anexos
- Caching, rate limits e performance
- Conteúdo Rich Text e Markdown
- Airtable vs opções tradicionais de headless CMS
- Padrões de arquitetura do mundo real
- FAQ

Por que Airtable como CMS realmente faz sentido
O maior argumento para Airtable não é técnico — é humano. Seus editores de conteúdo já sabem como usá-lo. Não há atrito de onboarding, nenhum novo login para esquecer, nenhuma UI de modelagem de conteúdo para aprender. Eles abrem uma interface tipo planilha, digitam coisas, e aparece no site.
Aqui está o que o torna genuinamente bom para certos casos de uso:
- Curva de aprendizado zero para editores. Se alguém consegue usar Google Sheets, consegue usar Airtable.
- Schema flexível. Adicionar um novo campo leva cinco segundos. Sem migrações, sem deployments de schema.
- Views e filtros integrados. Editores podem criar views filtradas, quadros Kanban, galerias — tudo sem ajuda do desenvolvedor.
- Dados relacionais. Ao contrário de planilhas planas, Airtable suporta registros vinculados, lookups e rollups.
- O plano gratuito é generoso o suficiente. 1.000 registros por base e 1.000 chamadas API por mês no plano gratuito. O plano Team ($20/assento/mês em 2026) oferece 50.000 registros e limites API mais altos.
Usei Airtable como CMS para sites de portfólio, listagens de eventos, diretórios de time, catálogos de produtos, quadros de empregos e pequenos blogs. Funciona surpreendentemente bem em todos esses.
Quando NÃO usar Airtable como CMS
Deixa eu economizar sua dor. Não use Airtable como seu CMS se:
- Você tem mais de ~10.000 registros de conteúdo. Fica lento, e a paginação da API vira uma dor de cabeça em escala.
- Você precisa de rich text com componentes incorporados. Os campos de texto longo do Airtable suportam Markdown básico, mas você não consegue incorporar componentes React ou blocos customizados como consegue com Sanity ou Contentful.
- Você precisa de permissões granulares em conteúdo. O modelo de permissão do Airtable é por base e por tabela, não por registro. Se o editor A não deve ver rascunhos do editor B, você vai ter um problema.
- Você precisa de preview em tempo real. Não há workflow draft/preview integrado. Você pode hackeá-lo com views filtradas e um campo status, mas é capenga.
- Você precisa de transformações de imagem. URLs de anexos do Airtable são temporárias (expiram após cerca de 2 horas). Você vai precisar de um pipeline de imagem separado.
Para qualquer coisa além de um site de conteúdo pequeno-médio, você provavelmente quer algo melhor. Cobrimos isso em nosso trabalho de desenvolvimento headless CMS.
Configurando sua base Airtable para conteúdo
Antes de escrever qualquer código, configure sua base Airtable corretamente. Aqui está a estrutura que uso para um blog típico:
Estrutura da Base
Crie uma tabela chamada Posts com esses campos:
| Nome do Campo | Tipo de Campo | Notas |
|---|---|---|
| Title | Single line text | Campo primário |
| Slug | Single line text | Minúsculo e seguro para URL |
| Body | Long text (Markdown) | Ativar formatação rich text |
| Excerpt | Long text | Texto plano, 1-2 frases |
| Published | Checkbox | Filtrar isso para produção |
| Publish Date | Date | Ordenar por isso descendente |
| Author | Link to Authors table | Link relacional |
| Tags | Multiple select | Ou link para tabela Tags |
| Featured Image | Attachment | Imagem única |
| SEO Title | Single line text | Override opcional |
| SEO Description | Long text | Meta description |
Crie uma view filtrada chamada "Published" que mostra apenas registros onde Published está marcado. Este é seu conteúdo de produção.
Configuração da API
- Vá para airtable.com/create/tokens e crie um token de acesso pessoal.
- Dê a ele escopo
data.records:read(edata.records:writese precisar acesso de escrita). - Escope-o para a base específica que está usando.
- Armazene o token em seu arquivo
.env. Nunca faça commit dele.
# .env
AIRTABLE_TOKEN=pat_xxxxxxxxxxxxx
AIRTABLE_BASE_ID=appXXXXXXXXXXXXXX
Você pode encontrar seu ID de base na documentação da API do Airtable ou na URL quando visualiza sua base.

Conectando Airtable ao Astro
Astro é meu framework preferido para sites alimentados por Airtable quando o conteúdo é na maioria estático. Como Astro constrói para HTML estático por padrão, você busca todos os dados do Airtable em tempo de construção, o que significa zero chamadas de API de seus visitantes e nenhuma preocupação com rate limit em produção.
Se você está explorando Astro para seu próximo projeto, temos experiência profunda com isso — confira nossos serviços de desenvolvimento Astro.
Instale o SDK
npm install airtable
Crie um utilitário de busca de dados
// src/lib/airtable.ts
import Airtable from 'airtable';
const base = new Airtable({ apiKey: import.meta.env.AIRTABLE_TOKEN })
.base(import.meta.env.AIRTABLE_BASE_ID);
export interface Post {
id: string;
title: string;
slug: string;
body: string;
excerpt: string;
publishDate: string;
featuredImage: { url: string; filename: string } | null;
tags: string[];
}
export async function getPosts(): Promise<Post[]> {
const records = await base('Posts')
.select({
view: 'Published',
sort: [{ field: 'Publish Date', direction: 'desc' }],
})
.all();
return records.map((record) => ({
id: record.id,
title: record.get('Title') as string,
slug: record.get('Slug') as string,
body: record.get('Body') as string,
excerpt: record.get('Excerpt') as string,
publishDate: record.get('Publish Date') as string,
featuredImage: record.get('Featured Image')
? {
url: (record.get('Featured Image') as any[])[0].url,
filename: (record.get('Featured Image') as any[])[0].filename,
}
: null,
tags: (record.get('Tags') as string[]) || [],
}));
}
export async function getPostBySlug(slug: string): Promise<Post | undefined> {
const records = await base('Posts')
.select({
view: 'Published',
filterByFormula: `{Slug} = '${slug}'`,
maxRecords: 1,
})
.all();
if (records.length === 0) return undefined;
const record = records[0];
return {
id: record.id,
title: record.get('Title') as string,
slug: record.get('Slug') as string,
body: record.get('Body') as string,
excerpt: record.get('Excerpt') as string,
publishDate: record.get('Publish Date') as string,
featuredImage: record.get('Featured Image')
? {
url: (record.get('Featured Image') as any[])[0].url,
filename: (record.get('Featured Image') as any[])[0].filename,
}
: null,
tags: (record.get('Tags') as string[]) || [],
};
}
Use-o em páginas Astro
---
// src/pages/blog/[slug].astro
import { getPosts, getPostBySlug } from '../../lib/airtable';
import Layout from '../../layouts/Layout.astro';
export async function getStaticPaths() {
const posts = await getPosts();
return posts.map((post) => ({
params: { slug: post.slug },
}));
}
const { slug } = Astro.params;
const post = await getPostBySlug(slug!);
if (!post) return Astro.redirect('/404');
---
<Layout title={post.title}>
<article>
<h1>{post.title}</h1>
<time>{post.publishDate}</time>
<div set:html={post.body} />
</article>
</Layout>
Tudo pronto. Em astro build, cada post é buscado do Airtable e renderizado para HTML estático. Seu site em produção faz zero chamadas de API.
Conectando Airtable ao Next.js
Next.js oferece mais flexibilidade. Você pode buscar em tempo de construção com generateStaticParams, em tempo de requisição com server components, ou usar ISR (Incremental Static Regeneration) para o melhor dos dois mundos.
Construímos muitos sites Next.js — é nosso pão de cada dia. Veja nossas capacidades de desenvolvimento Next.js.
O utilitário de busca (versão Next.js)
Eu prefiro usar a API REST do Airtable diretamente com fetch no Next.js em vez do SDK. Oferece melhor controle sobre caching com as extensões de fetch do Next.js.
// lib/airtable.ts
const AIRTABLE_TOKEN = process.env.AIRTABLE_TOKEN!;
const AIRTABLE_BASE_ID = process.env.AIRTABLE_BASE_ID!;
const headers = {
Authorization: `Bearer ${AIRTABLE_TOKEN}`,
'Content-Type': 'application/json',
};
export async function fetchPosts() {
const url = new URL(
`https://api.airtable.com/v0/${AIRTABLE_BASE_ID}/Posts`
);
url.searchParams.set('view', 'Published');
url.searchParams.set('sort[0][field]', 'Publish Date');
url.searchParams.set('sort[0][direction]', 'desc');
const res = await fetch(url.toString(), {
headers,
next: { revalidate: 60 }, // ISR: revalidar a cada 60 segundos
});
if (!res.ok) throw new Error(`Airtable API error: ${res.status}`);
const data = await res.json();
return data.records.map((record: any) => ({
id: record.id,
title: record.fields['Title'],
slug: record.fields['Slug'],
body: record.fields['Body'],
excerpt: record.fields['Excerpt'],
publishDate: record.fields['Publish Date'],
tags: record.fields['Tags'] || [],
}));
}
Página ISR com App Router
// app/blog/[slug]/page.tsx
import { fetchPosts } from '@/lib/airtable';
import { notFound } from 'next/navigation';
export async function generateStaticParams() {
const posts = await fetchPosts();
return posts.map((post: any) => ({ slug: post.slug }));
}
export default async function BlogPost({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const posts = await fetchPosts();
const post = posts.find((p: any) => p.slug === slug);
if (!post) notFound();
return (
<article>
<h1>{post.title}</h1>
<time>{post.publishDate}</time>
<div dangerouslySetInnerHTML={{ __html: post.body }} />
</article>
);
}
Com revalidate: 60, Next.js vai servir a página cacheada e atualizá-la em background no máximo uma vez a cada 60 segundos. Seus editores atualizam Airtable, e o site se atualiza em um minuto. Nenhuma configuração de webhook, nenhum trigger de rebuild.
Manipulando imagens e anexos
Esta é a maior armadilha com Airtable como CMS. URLs de anexo do Airtable expiram. São URLs assinadas que se tornam inválidas após aproximadamente 2 horas. Se você renderizá-las diretamente em seu HTML, vão quebrar.
Aqui estão suas opções:
Opção 1: Baixar em tempo de construção (Astro)
Para sites estáticos, baixe imagens durante a construção e sirva-as localmente:
import fs from 'fs/promises';
import path from 'path';
async function downloadImage(url: string, filename: string) {
const res = await fetch(url);
const buffer = Buffer.from(await res.arrayBuffer());
const outputPath = path.join('public', 'images', 'cms', filename);
await fs.mkdir(path.dirname(outputPath), { recursive: true });
await fs.writeFile(outputPath, buffer);
return `/images/cms/${filename}`;
}
Opção 2: Proxy através de um CDN
Configure um Cloudflare Worker ou Vercel Edge Function que faz proxy de URLs de imagem do Airtable, as cacheia e as serve através de seu próprio domínio. Funciona para Astro e Next.js.
Opção 3: Use um host de imagem separado
Faça upload de imagens para Cloudinary, Imgix ou um bucket S3, e armazene a URL permanente em um campo de texto em vez de usar o campo de anexo do Airtable. Isto é o que recomendo para sites em produção — é a abordagem mais confiável.
Caching, rate limits e performance
A API do Airtable tem rate limits rigorosos: 5 requisições por segundo por base. Isso não é muito. Aqui está como ficar bem abaixo disso.
| Estratégia | Framework | Como funciona |
|---|---|---|
| Static generation | Astro | Todas as chamadas de API acontecem em tempo de construção. Zero chamadas runtime. |
| ISR | Next.js | Respostas cacheadas, revalidadas em um timer. |
| In-memory cache | Ambos | Cache respostas de API em um Map com TTL. |
| Webhook + rebuild | Ambos | Automação Airtable dispara rebuild Vercel/Netlify. |
| Redis/KV cache | Next.js (Vercel) | Armazenar respostas de API em Vercel KV ou Upstash Redis. |
Para sites Astro, a abordagem em tempo de construção significa que você só acessa a API durante deploys. Para Next.js com ISR, você acessará no máximo uma vez por intervalo de revalidação por página.
Se você tem muitas páginas e intervalos de revalidação curtos, considere buscar todos os registros de uma vez e cachear o dataset inteiro em vez de fazer chamadas de API por página.
Paginação importa
Airtable retorna um máximo de 100 registros por requisição. O método .all() no SDK lida com paginação automaticamente, mas se está usando fetch diretamente, você precisa seguir o token offset:
async function fetchAllRecords(tableName: string) {
let allRecords: any[] = [];
let offset: string | undefined;
do {
const url = new URL(
`https://api.airtable.com/v0/${AIRTABLE_BASE_ID}/${tableName}`
);
url.searchParams.set('view', 'Published');
if (offset) url.searchParams.set('offset', offset);
const res = await fetch(url.toString(), { headers });
const data = await res.json();
allRecords = [...allRecords, ...data.records];
offset = data.offset;
} while (offset);
return allRecords;
}
Conteúdo Rich Text e Markdown
Os campos de texto longo do Airtable podem armazenar Markdown se você habilitar a opção "rich text". Mas o que você recebe de volta da API é texto formatado em Markdown, não HTML.
Você precisa convertê-lo. Eu uso marked para casos simples ou unified com plugins remark para mais controle:
import { marked } from 'marked';
const htmlContent = marked.parse(post.body);
Para Astro, você também pode usar o processamento Markdown integrado:
---
import { marked } from 'marked';
const html = marked.parse(post.body);
---
<article set:html={html} />
Uma coisa a prestar atenção: o editor rich text do Airtable produz seu próprio sabor de Markdown. Ele lida bem com negrito, itálico, links, headings e listas. Code blocks e tabelas são suportados mas podem ser finais. Se seu conteúdo precisa de formatação complexa, considere ter editores escreverem em modo Markdown plano.
Airtable vs opções tradicionais de headless CMS
Vamos ser reais sobre os trade-offs. Aqui está como Airtable se compara a plataformas headless CMS especializadas em 2026:
| Feature | Airtable | Sanity | Contentful | Strapi |
|---|---|---|---|---|
| Curva de aprendizado do editor | Muito baixa | Média | Média | Média |
| Modelagem de conteúdo | Flexível, informal | Excelente | Excelente | Bom |
| Rate limits de API | 5 req/s por base | Generoso (CDN) | Generoso (CDN) | Auto-hospedado |
| Manipulação de imagem | URLs expirando | CDN integrado | CDN integrado | Auto-hospedado |
| Preview/drafts | Manual (checkbox) | Integrado | Integrado | Integrado |
| Preço (time de 5) | $100/mês (Team) | Tier gratuito viável | $300/mês+ | Gratuito (auto-hospedado) |
| Suporte a webhook | Via automações | Integrado | Integrado | Integrado |
| Qualidade de rich text | Markdown básico | Portable Text | Estruturado | Rich text |
| Conteúdo relacional | Registros vinculados | Referências | Referências | Relações |
Airtable vence em experiência do editor e flexibilidade. Perde em manipulação de imagem, workflows de preview e confiabilidade de API em escala. Para sites pequeno-médio onde seus editores já estão no Airtable? É uma escolha sólida. Para sites ricos em conteúdo com workflows complexos? Vá com um CMS real.
Padrões de arquitetura do mundo real
Aqui estão os padrões que usei em produção:
Padrão 1: Completamente estático com Astro + Webhooks de rebuild
Melhor para: sites de marketing, portfólios, diretórios com < 500 registros.
- Astro busca todos os dados do Airtable em tempo de construção.
- Automação Airtable envia um webhook para Vercel/Netlify em atualização de registro.
- Site se reconstrói em 30-60 segundos.
- Imagens baixadas em tempo de construção — nenhum problema de URL expirando.
Padrão 2: ISR com Next.js
Melhor para: blogs, catálogos, sites com atualizações frequentes.
- Next.js gera páginas com ISR (revalidar a cada 60-300 segundos).
- API Airtable chamada no máximo uma vez por revalidação por página única.
- Imagens fazem proxy através de Cloudinary ou são baixadas para um CDN.
- Editores veem atualizações em minutos sem disparar rebuild completo.
Padrão 3: Airtable + CMS suplementar
Melhor para: sites onde alguns conteúdos vivem no Airtable e outro conteúdo precisa de edição mais rica.
- Dados estruturados (membros de time, eventos, produtos) permanecem no Airtable.
- Conteúdo de forma longa (posts de blog, case studies) vai para Sanity ou Notion.
- Frontend busca de ambas as fontes em tempo de construção ou com ISR.
Esse padrão híbrido é mais comum do que você pensaria. Construímos vários sites assim — se está considerando algo similar, vamos conversar sobre isso.
Disparando rebuilds do Airtable
Airtable tem automações integradas que podem disparar webhooks. Configure um trigger em "When a record is updated" na sua tabela Posts, depois envie uma requisição POST para o hook de build de sua plataforma de deployment:
// Vercel deploy hook
https://api.vercel.com/v1/integrations/deploy/prj_xxxx/yyyy
// Netlify build hook
https://api.netlify.com/build_hooks/xxxxxxxxxxxx
Adicione um atraso de 30 segundos na automação para agrupar edições rápidas.
FAQ
É gratuito usar Airtable como CMS?
O plano gratuito do Airtable inclui 1.000 registros por base e 1.000 chamadas de API por mês. Isso é suficiente para um site pequeno, mas você provavelmente vai precisar do plano Team ($20/assento/mês em 2026) para qualquer coisa séria. O plano Team oferece 50.000 registros e limites de API mais altos.
Como lidar com URLs de imagem expirando do Airtable?
URLs de anexo do Airtable expiram após cerca de 2 horas. Para sites estáticos construídos com Astro, baixe imagens em tempo de construção. Para Next.js com ISR, ou faça proxy de imagens através de um CDN como Cloudinary, ou armazene URLs de imagem em um serviço de hospedagem de imagem separado e referencie-as como campos de texto no Airtable.
Airtable consegue lidar com um blog com centenas de posts?
Sim, até um ponto. Airtable lida bem com centenas de registros. Uma vez que você chega aos milhares, a paginação de API e os tempos de construção começam a ficar notáveis. Para um blog com menos de 1.000 posts, funciona bem. Além disso, considere um headless CMS dedicado.
Airtable é melhor que Notion como CMS?
Eles resolvem problemas diferentes. Airtable é melhor para dados estruturados (produtos, eventos, membros de time) por causa de seu modelo de banco de dados relacional. Notion é melhor para conteúdo escrito de forma longa por causa de seu editor baseado em blocos. A API do Airtable também é mais madura e rápida que a do Notion.
Como configuro funcionalidade preview/draft com Airtable?
Adicione um campo "Status" com seleção única com opções como "Draft", "In Review" e "Published". Crie uma view filtrada para cada status. Seu site de produção busca da view "Published". Para previews, crie uma rota de preview separada que busca da view "In Review", protegida por autenticação.
Devo usar o SDK do Airtable ou a API REST diretamente?
Para Astro, o pacote airtable npm oficial funciona bem já que você está buscando em tempo de construção. Para Next.js, recomendo usar fetch diretamente com a API REST — oferece controle sobre diretivas de cache do Next.js como revalidate e tags. O SDK não entende as opções estendidas de fetch do Next.js.
Qual é o número máximo de chamadas de API que Airtable permite?
Airtable força um rate limit de 5 requisições por segundo por base. Exceder isso retorna um código de status 429. No plano Team, você recebe uma permissão de chamada mensal mais alta, mas o rate limit por segundo permanece o mesmo. Static generation e ISR são as melhores formas de minimizar uso de API.
Posso usar Airtable com Astro e Next.js no mesmo projeto?
Não exatamente no mesmo projeto, mas você pode ter uma base Airtable compartilhada alimentando múltiplos frontends. Alguns times usam Astro para seu site de marketing e Next.js para seu web app, ambos lendo da mesma base Airtable. Apenas esteja ciente dos limites de rate compartilhados em todos os consumidores.