Ik zal eerlijk zijn: toen een klant me in 2023 voor het eerst vroeg om Airtable als hun CMS te gebruiken, dacht ik dat ze een grapje maakten. Een spreadsheet-app die een productiewebsite voorziet? Maar na het bouwen van een half dozijn sites op deze manier — sommige met Astro, sommige met Next.js — ben ik omgekeerd. Airtable raakt een zoetste plek voor bepaalde projecten die traditionele headless CMS-platforms volledig missen. Je marketing team kent al hoe het moet gebruiken. Het is flexibel genoeg om de meeste content te modelleren. En de API is doodvoudig.

Maar het is niet zonder scherpe kanten. Rate limits, attachment handling, relationele data quirks — er is veel wat de "Airtable als CMS"-blogposts uit 2023 je nooit hebben verteld. Deze gids behandelt alles wat ik heb geleerd bij het uitvoeren van echte projecten met deze stack in 2026.

Inhoudsopgave

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

Waarom Airtable als CMS eigenlijk zinvol is

Het grootste argument voor Airtable is niet technisch — het is menselijk. Je content editors kennen al hoe het moet gebruiken. Er is geen onboarding-wrijving, geen nieuwe login die je vergeet, geen content modeling UI om te leren. Ze openen een spreadsheet-achtige interface, typen spullen in, en het verschijnt op de website.

Hier is wat het echt goed maakt voor bepaalde use cases:

  • Nul leercurve voor editors. Als iemand Google Sheets kan gebruiken, kan hij Airtable gebruiken.
  • Flexibel schema. Een nieuw veld toevoegen duurt vijf seconden. Geen migraties, geen schema deployments.
  • Ingebouwde views en filters. Editors kunnen gefilterde views, Kanban boards, galerijen maken — allemaal zonder developerhelp.
  • Relationele gegevens. In tegenstelling tot platte spreadsheets ondersteunt Airtable gekoppelde records, lookups en rollups.
  • De gratis tier is royaal genoeg. 1.000 records per base en 1.000 API-oproepen per maand in het gratis plan. Het Team plan ($20/seat/maand in 2026) geeft je 50.000 records en hogere API-limieten.

Ik heb Airtable als CMS gebruikt voor portfolio sites, event listings, team directories, productcatalogi, job boards en kleine blogs. Het werkt verrassend goed voor al deze dingen.

Wanneer je Airtable NIET als CMS moet gebruiken

Laat me je wat pijn besparen. Gebruik Airtable niet als je CMS als:

  • Je meer dan ~10.000 content records hebt. Het wordt traag, en de API pagination wordt echt een kopzorg op schaal.
  • Je rich text met ingebedde componenten nodig hebt. Airtable's lange tekstvelden ondersteunen basis-Markdown, maar je kunt geen React-componenten of custom blokken insluiten zoals je kan met Sanity of Contentful.
  • Je granulaire machtigingen op content nodig hebt. Airtable's permission model is per-base en per-table, niet per-record. Als editor A het ontwerpen van editor B niet mag zien, heb je een slechte tijd.
  • Je echte-tijd preview nodig hebt. Er is geen ingebouwde concept/preview workflow. Je kunt het hacken met gefilterde views en een statusveld, maar het voelt kluderig.
  • Je beeldtransformaties nodig hebt. Airtable attachment URLs zijn tijdelijk (ze verlopen na ongeveer 2 uur). Je hebt een aparte image pipeline nodig.

Voor alles voorbij een kleine-tot-middelgrote content site ben je waarschijnlijk beter af met een speciaal gebouwde headless CMS. We behandelen dat in ons headless CMS development work.

Je Airtable Base instellen voor content

Voordat je code schrijft, zet je Airtable base goed in. Hier is de structuur die ik gebruik voor een typische blog:

Base-structuur

Maak een tabel "Posts" met deze velden:

Veldnaam Veldtype Notities
Titel Enkele regel text Primair veld
Slug Enkele regel text URL-safe, lowercase
Body Lange text (Markdown) Rich text formatting inschakelen
Excerpt Lange text Platte text, 1-2 zinnen
Gepubliceerd Checkbox Filter hierop voor productie
Publicatiedatum Datum Sorteer hierop aflopend
Auteur Link naar Authors-tabel Relationele link
Tags Meervoudig select Of link naar Tags-tabel
Aanbevolen afbeelding Bijlage Enkele afbeelding
SEO Titel Enkele regel text Optionele override
SEO Beschrijving Lange text Meta beschrijving

Maak een gefilterde view "Gepubliceerd" die alleen records toont waar "Gepubliceerd" is ingeschakeld. Dit is je productiecontent.

API-instelling

  1. Ga naar airtable.com/create/tokens en maak een persoonlijk access token.
  2. Geef het data.records:read scope (en data.records:write als je schrijftoegang nodig hebt).
  3. Scope het naar je specifieke base.
  4. Sla het token op in je .env bestand. Commit het nooit.
# .env
AIRTABLE_TOKEN=pat_xxxxxxxxxxxxx
AIRTABLE_BASE_ID=appXXXXXXXXXXXXXX

Je base ID vind je in de Airtable API-docs of in de URL wanneer je je base bekijkt.

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

Airtable verbinden met Astro

Astro is mijn voorkeur framework voor Airtable-aangedreven sites wanneer de content meestal statisch is. Omdat Astro standaard naar statische HTML bouwt, haal je alle je Airtable gegevens op build-time, wat betekent nul API-oproepen van je bezoekers en geen rate limit-zorgen in productie.

Als je Astro voor je volgende project verkent, hebben we diepe ervaring ermee — check out onze Astro development services.

Install de SDK

npm install airtable

Maak een data-ophaal utility

// 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[]) || [],
  };
}

Gebruik het in Astro-pagina's

---
// 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>

Dat is het. Bij astro build wordt elk bericht opgehaald uit Airtable en gerenderd naar statische HTML. Je productiesite maakt nul API-oproepen.

Airtable verbinden met Next.js

Next.js geeft je meer flexibiliteit. Je kunt ophalen bij build time met generateStaticParams, bij request time met server components, of ISR (Incremental Static Regeneration) gebruiken voor het beste van beide werelden.

We bouwen veel Next.js sites — het is onze broodnodig. Zie onze Next.js development capabilities.

De fetch utility (Next.js versie)

Ik geef de voorkeur aan het rechtstreeks gebruiken van de Airtable REST API met fetch in Next.js in plaats van de SDK. Het geeft je beter controle over caching met Next.js's extended fetch.

// 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: revalidate every 60 seconds
  });

  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'] || [],
  }));
}

ISR-pagina met 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>
  );
}

Met revalidate: 60 zal Next.js de cached pagina serveren en deze op de achtergrond vernieuwen op maximaal eens per 60 seconden. Je editors updaten Airtable, en de site wordt binnen een minuut bijgewerkt. Geen webhook-instelling, geen rebuild triggers.

Afbeeldingen en bijlagen verwerken

Dit is de grootste gotcha met Airtable als CMS. Airtable attachment URLs verlopen. Het zijn ondertekende URLs die na ongeveer 2 uur ongeldig worden. Als je ze rechtstreeks in je HTML rendert, breken ze.

Hier zijn je opties:

Optie 1: Download bij build time (Astro)

Voor statische sites downloads u afbeeldingen tijdens de build en serveer ze lokaal:

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}`;
}

Optie 2: Proxy via een CDN

Stel een Cloudflare Worker of Vercel Edge Function in die Airtable-afbeeldings-URLs proxyt, cacht en via je eigen domein serveert. Dit werkt voor zowel Astro als Next.js.

Optie 3: Gebruik een aparte Image Host

Upload afbeeldingen naar Cloudinary, Imgix of een S3 bucket, en bewaar de permanente URL in een tekstveld in plaats van Airtable's attachment field. Dit is wat ik aanbeveel voor productiesites — het is de meest betrouwbare benadering.

Caching, rate limits en performance

Airtable's API heeft strikte rate limits: 5 requests per seconde per base. Dat is niet veel. Hier is hoe je er goed onder blijft.

Strategie Framework Hoe het werkt
Statische generatie Astro Alle API-oproepen gebeuren op build time. Nul runtime-oproepen.
ISR Next.js Cached responses, revalidated op een timer.
In-memory cache Beide Cache API responses in een Map met TTL.
Webhook + rebuild Beide Airtable automation triggert een Vercel/Netlify rebuild.
Redis/KV cache Next.js (Vercel) Sla API responses op in Vercel KV of Upstash Redis.

Voor Astro sites betekent de build-time aanpak dat je de API alleen hit tijdens deployments. Voor Next.js met ISR hit je het op maximaal eens per revalidatie interval per pagina.

Als je veel pagina's hebt en korte revalidatie intervallen, overweeg dan om alle records tegelijk op te halen en de gehele dataset in cache te zetten in plaats van per-pagina API-oproepen.

Paginering doet ertoe

Airtable retourneert maximaal 100 records per verzoek. De .all() methode in de SDK handelt paginering automatisch af, maar als je rechtstreeks fetch gebruikt, moet je het offset token volgen:

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;
}

Rich text en Markdown content

Airtable's lange tekstvelden kunnen Markdown opslaan als je de "rich text" optie inschakelt. Maar wat je terug krijgt van de API is Markdown-geformatteerde text, niet HTML.

Je moet het converteren. Ik gebruik marked voor eenvoudige gevallen of unified met remark plugins voor meer controle:

import { marked } from 'marked';

const htmlContent = marked.parse(post.body);

Voor Astro kun je ook de ingebouwde Markdown-verwerking gebruiken:

---
import { marked } from 'marked';
const html = marked.parse(post.body);
---
<article set:html={html} />

Een ding om op te letten: Airtable's rich text editor produceert zijn eigen Markdown-variant. Het behandelt vet, cursief, links, koppen en lijsten goed. Code blocks en tabellen worden ondersteund maar kunnen lastig zijn. Als je content complexe opmaak nodig heeft, overweeg dan om editors in plain Markdown-modus te laten schrijven.

Airtable versus traditionele headless CMS-opties

Laten we eerlijk zijn over de trade-offs. Hier is hoe Airtable zich verhoudt tot speciaal gebouwde headless CMS-platforms in 2026:

Feature Airtable Sanity Contentful Strapi
Editor leercurve Zeer laag Gemiddeld Gemiddeld Gemiddeld
Content modellering Flexibel, informeel Uitstekend Uitstekend Goed
API rate limits 5 req/s per base Royaal (CDN) Royaal (CDN) Zelf-gehost
Afbeeldingsverwerking URLs verlopen Ingebouwde CDN Ingebouwde CDN Zelf-gehost
Preview/drafts Handmatig (checkbox) Ingebouwd Ingebouwd Ingebouwd
Pricing (team van 5) $100/ma (Team) Gratis tier bruikbaar $300/ma+ Gratis (zelf-host)
Webhook ondersteuning Via automations Ingebouwd Ingebouwd Ingebouwd
Rich text kwaliteit Basis Markdown Portable Text Gestructureerd Rich text
Relationele content Gekoppelde records Referenties Referenties Relaties

Airtable wint op editor-ervarink en flexibiliteit. Het verliest op afbeeldingsverwerking, preview workflows en API-betrouwbaarheid op schaal. Voor kleine-tot-middelgrote sites waar je editors al in Airtable zitten? Het is een solide keuze. Voor content-zware sites met complexe workflows? Ga met een echte CMS.

Real-world architecture patterns

Hier zijn de patronen die ik in productie heb gebruikt:

Pattern 1: Volledig statisch met Astro + rebuild webhooks

Best voor: marketingsites, portfolio's, directories met < 500 records.

  1. Astro haalt alle Airtable-gegevens op build time op.
  2. Airtable automation stuurt een webhook naar Vercel/Netlify op record update.
  3. Site bouwt in 30-60 seconden.
  4. Afbeeldingen gedownload op build time — geen problemen met verlopen URLs.

Pattern 2: ISR met Next.js

Best voor: blogs, catalogi, sites met frequente updates.

  1. Next.js genereert pagina's met ISR (revalidate om de 60-300 seconden).
  2. Airtable API aangeroepen eenmaal per revalidatie per unieke pagina.
  3. Afbeeldingen geproxyd via Cloudinary of gedownload naar een CDN.
  4. Editors zien updates binnen minuten zonder volledige rebuild te triggeren.

Pattern 3: Airtable + aanvullende CMS

Best voor: sites waar sommige content in Airtable leeft en ander content rijkere bewerking nodig heeft.

  1. Gestructureerde gegevens (teamleden, events, producten) blijven in Airtable.
  2. Lange-vorm content (blog posts, case studies) gaat naar Sanity of Notion.
  3. Frontend haalt bij build time of met ISR uit beide bronnen op.

Dit hybride approach is meer gangbaar dan je zou denken. We hebben meerdere sites op deze manier gebouwd — als je iets soortgelijks overweegt, laten we erover praten.

Rebuilds triggeren vanuit Airtable

Airtable heeft ingebouwde automations die webhooks kunnen afvuren. Stel een trigger in op "When a record is updated" in je Posts-tabel, stuur dan een POST-verzoek naar je deployment platform's build hook:

// Vercel deploy hook
https://api.vercel.com/v1/integrations/deploy/prj_xxxx/yyyy

// Netlify build hook
https://api.netlify.com/build_hooks/xxxxxxxxxxxx

Voeg een 30-seconden delay toe in de automation om snelle edits te bundelen.

Veelgestelde vragen

Is Airtable gratis te gebruiken als CMS? Airtable's gratis plan bevat 1.000 records per base en 1.000 API-oproepen per maand. Dat is genoeg voor een kleine site, maar je zult waarschijnlijk het Team plan ($20/seat/maand in 2026) nodig hebben voor alles serieus. Het Team plan geeft je 50.000 records en hogere API-limieten.

Hoe ga ik om met Airtable's verlopen afbeeldings-URLs? Airtable attachment URLs verlopen na ongeveer 2 uur. Voor statische sites gebouwd met Astro, download afbeeldingen op build time. Voor Next.js met ISR, proxy afbeeldingen via een CDN zoals Cloudinary, of sla afbeeldings-URLs op in een aparte image hosting-service en referentie ze als tekstvelden in Airtable.

Kan Airtable een blog met honderden posts aan? Ja, tot op zekere hoogte. Airtable voert honderden records goed uit. Als je in de duizenden komt, beginnen API paginering en build times merkbaar te worden. Voor een blog met onder de 1.000 posts werkt het prima. Voorbij dat, overweeg een speciaal headless CMS.

Is Airtable beter dan Notion als CMS? Ze lossen verschillende problemen op. Airtable is beter voor gestructureerde data (producten, events, teamleden) vanwege zijn relationele database model. Notion is beter voor lange-vorm geschreven content vanwege zijn block-gebaseerde editor. Airtable's API is ook rijper en sneller dan Notion's.

Hoe stel ik preview/concept-functionaliteit in met Airtable? Voeg een "Status" single-select veld toe met opties zoals "Draft", "In Review" en "Published". Maak een gefilterde view voor elk status. Je productiesite haalt uit de "Published" view. Voor previews, maak een aparte preview route die uit de "In Review" view haalt, beveiligd door authenticatie.

Moet ik de Airtable SDK of de REST API rechtstreeks gebruiken? Voor Astro werkt het officiële airtable npm-pakket goed omdat je op build time ophaalt. Voor Next.js, aanbeveel ik fetch rechtstreeks met de REST API — het geeft je controle over Next.js cache directives zoals revalidate en tags. De SDK begrijpt Next.js's extended fetch opties niet.

Wat is het maximum aantal API-oproepen dat Airtable toestaat? Airtable handhaaft een rate limit van 5 requests per seconde per base. Overschrijding hiervan retourneert een 429 status code. In het Team plan krijg je een hogere maandelijkse call-allowance, maar de per-seconde rate limit blijft hetzelfde. Statische generatie en ISR zijn de beste manieren om API-gebruik te minimaliseren.

Kan ik Airtable gebruiken met zowel Astro als Next.js in hetzelfde project? Niet exact in hetzelfde project, maar je kunt een gedeelde Airtable base hebben die meerdere frontends voorziet. Sommige teams gebruiken Astro voor hun marketingsite en Next.js voor hun web app, beide lezend uit dezelfde Airtable base. Wees gewoon voorzichtig met de gedeelde rate limits over alle consumenten.