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

Using Airtable as a CMS with Astro & Next.js in 2026

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

  1. Vá para airtable.com/create/tokens e crie um token de acesso pessoal.
  2. Dê a ele escopo data.records:read (e data.records:write se precisar acesso de escrita).
  3. Escope-o para a base específica que está usando.
  4. 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.

Using Airtable as a CMS with Astro & Next.js in 2026 - architecture

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.

  1. Astro busca todos os dados do Airtable em tempo de construção.
  2. Automação Airtable envia um webhook para Vercel/Netlify em atualização de registro.
  3. Site se reconstrói em 30-60 segundos.
  4. 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.

  1. Next.js gera páginas com ISR (revalidar a cada 60-300 segundos).
  2. API Airtable chamada no máximo uma vez por revalidação por página única.
  3. Imagens fazem proxy através de Cloudinary ou são baixadas para um CDN.
  4. 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.

  1. Dados estruturados (membros de time, eventos, produtos) permanecem no Airtable.
  2. Conteúdo de forma longa (posts de blog, case studies) vai para Sanity ou Notion.
  3. 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.