Utiliser Airtable comme CMS avec Astro & Next.js en 2026
Je vais être honnête : quand un client m'a demandé pour la première fois d'utiliser Airtable comme CMS en 2023, j'ai cru qu'il blaguait. Une application de tableur alimentant un site de production ? Mais après avoir construit une demi-douzaine de sites de cette façon — certains avec Astro, d'autres avec Next.js — j'ai changé d'avis. Airtable atteint un sweet spot pour certains projets que les plateformes CMS headless traditionnelles complètement manquent. Votre équipe marketing sait déjà comment l'utiliser. C'est assez flexible pour modéliser la plupart des contenus. Et l'API est extrêmement simple.
Mais ce n'est pas sans ses inconvénients. Les limites de débit, la gestion des pièces jointes, les bizarreries des données relationnelles — il y a beaucoup que les articles de blog « Airtable comme CMS » de 2023 ne vous ont jamais dit. Ce guide couvre tout ce que j'ai appris en déployant de vrais projets avec cette pile en 2026.
Table des matières
- Pourquoi Airtable comme CMS a vraiment du sens
- Quand vous NE devriez PAS utiliser Airtable comme CMS
- Configuration de votre base Airtable pour le contenu
- Connexion d'Airtable à Astro
- Connexion d'Airtable à Next.js
- Gestion des images et pièces jointes
- Cache, limites de débit et performance
- Contenu texte enrichi et Markdown
- Airtable vs options CMS headless traditionnelles
- Modèles d'architecture du monde réel
- FAQ

Pourquoi Airtable comme CMS a vraiment du sens
Le plus grand argument en faveur d'Airtable n'est pas technique — c'est humain. Vos éditeurs de contenu savent déjà comment l'utiliser. Il n'y a pas de friction lors de l'intégration, pas de nouvelle connexion à oublier, pas d'interface de modélisation de contenu à apprendre. Ils ouvrent une interface ressemblant à un tableur, tapent des choses, et ça s'affiche sur le site web.
Voici ce qui le rend véritablement bon pour certains cas d'usage :
- Zéro courbe d'apprentissage pour les éditeurs. Si quelqu'un peut utiliser Google Sheets, il peut utiliser Airtable.
- Schéma flexible. Ajouter un nouveau champ prend cinq secondes. Pas de migrations, pas de déploiements de schéma.
- Vues et filtres intégrés. Les éditeurs peuvent créer des vues filtrées, des tableaux Kanban, des galeries — tout sans aide des développeurs.
- Données relationnelles. Contrairement aux tableurs plats, Airtable supporte les enregistrements liés, les recherches et les cumuls.
- L'offre gratuite est suffisamment généreuse. 1 000 enregistrements par base et 1 000 appels API par mois dans l'offre gratuite. L'offre Team (20 $/siège/mois en 2026) vous donne 50 000 enregistrements et des limites d'API plus élevées.
J'ai utilisé Airtable comme CMS pour des sites de portfolio, des listes d'événements, des annuaires d'équipes, des catalogues de produits, des tableaux d'emploi et de petits blogs. Cela fonctionne étonnamment bien pour tous ces cas.
Quand vous NE devriez PAS utiliser Airtable comme CMS
Laissez-moi vous épargner de la douleur. N'utilisez pas Airtable comme votre CMS si :
- Vous avez plus d'environ 10 000 enregistrements de contenu. Cela devient lent, et la pagination d'API devient un vrai problème à l'échelle.
- Vous avez besoin de texte enrichi avec des composants incorporés. Les champs de texte long d'Airtable supportent le Markdown de base, mais vous ne pouvez pas intégrer de composants React ou de blocs personnalisés comme vous pouvez le faire avec Sanity ou Contentful.
- Vous avez besoin de permissions granulaires sur le contenu. Le modèle de permissions d'Airtable est par base et par table, pas par enregistrement. Si l'éditeur A ne devrait pas voir les brouillons de l'éditeur B, vous allez avoir du mal.
- Vous avez besoin d'un aperçu en temps réel. Il n'y a pas de workflow brouillon/aperçu intégré. Vous pouvez le bricoler avec des vues filtrées et un champ de statut, mais c'est bancal.
- Vous avez besoin de transformations d'images. Les URLs de pièces jointes Airtable sont temporaires (elles expirent après environ 2 heures). Vous aurez besoin d'un pipeline d'images séparé.
Pour tout ce qui dépasse un petit-à-moyen site de contenu, vous êtes probablement mieux avec un CMS headless à usage spécifique.
Configuration de votre base Airtable pour le contenu
Avant d'écrire du code, préparez correctement votre base Airtable. Voici la structure que j'utilise pour un blog typique :
Structure de base
Créez une table appelée Posts avec ces champs :
| Nom du champ | Type de champ | Notes |
|---|---|---|
| Title | Texte sur une seule ligne | Champ primaire |
| Slug | Texte sur une seule ligne | URL-safe, minuscules |
| Body | Long text (Markdown) | Activer le formatage de texte enrichi |
| Excerpt | Texte long | Texte brut, 1-2 phrases |
| Published | Case à cocher | Filtrer sur celle-ci pour la production |
| Publish Date | Date | Trier par celle-ci décroissant |
| Author | Lien vers la table Authors | Lien relationnel |
| Tags | Sélection multiple | Ou lien vers une table Tags |
| Featured Image | Pièce jointe | Image unique |
| SEO Title | Texte sur une seule ligne | Remplacement optionnel |
| SEO Description | Texte long | Description méta |
Créez une vue filtrée appelée « Published » qui ne montre que les enregistrements où Published est coché. C'est votre contenu de production.
Configuration de l'API
- Allez à airtable.com/create/tokens et créez un token d'accès personnel.
- Donnez-lui la portée
data.records:read(etdata.records:writesi vous avez besoin d'accès en écriture). - Limitez-le à la base spécifique que vous utilisez.
- Stockez le token dans votre fichier
.env. Ne le commitez jamais.
# .env
AIRTABLE_TOKEN=pat_xxxxxxxxxxxxx
AIRTABLE_BASE_ID=appXXXXXXXXXXXXXX
Vous pouvez trouver l'ID de votre base dans la documentation de l'API Airtable ou dans l'URL lors de la visualisation de votre base.

Connexion d'Airtable à Astro
Astro est mon framework préféré pour les sites alimentés par Airtable lorsque le contenu est en grande partie statique. Puisqu'Astro construit du HTML statique par défaut, vous récupérez toutes vos données Airtable au moment de la construction, ce qui signifie zéro appel API à partir de vos visiteurs et aucune inquiétude concernant les limites de débit en production.
Installer le SDK
npm install airtable
Créer un utilitaire de récupération de données
// 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[]) || [],
};
}
L'utiliser dans les pages 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>
C'est tout. Sur astro build, chaque article est récupéré d'Airtable et rendu en HTML statique. Votre site de production ne fait zéro appel API.
Connexion d'Airtable à Next.js
Next.js vous donne plus de flexibilité. Vous pouvez récupérer au moment de la construction avec generateStaticParams, au moment de la requête avec des composants serveur, ou utiliser l'ISR (Incremental Static Regeneration) pour le meilleur des deux mondes.
L'utilitaire Fetch (version Next.js)
Je préfère utiliser directement l'API REST d'Airtable avec fetch dans Next.js plutôt que le SDK. Cela vous donne un meilleur contrôle sur la mise en cache avec les extensions fetch de 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: 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'] || [],
}));
}
Page ISR avec 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>
);
}
Avec revalidate: 60, Next.js servira la page en cache et la rafraîchira en arrière-plan au maximum une fois toutes les 60 secondes. Vos éditeurs mettent à jour Airtable, et le site se met à jour en quelques minutes. Pas de configuration de webhook, pas de déclencheurs de reconstruction.
Gestion des images et pièces jointes
C'est le plus grand piège avec Airtable comme CMS. Les URLs des pièces jointes Airtable expirent. Ce sont des URLs signées qui deviennent invalides après environ 2 heures. Si vous les restituez directement en HTML, elles vont casser.
Voici vos options :
Option 1 : Télécharger au moment de la construction (Astro)
Pour les sites statiques, téléchargez les images pendant la construction et servez-les localement :
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}`;
}
Option 2 : Proxy via un CDN
Configurez un Cloudflare Worker ou une fonction Vercel Edge qui proxie les URLs d'images Airtable, les met en cache et les sert via votre propre domaine. Cela fonctionne pour Astro et Next.js.
Option 3 : Utiliser un hôte d'images séparé
Téléchargez des images vers Cloudinary, Imgix ou un bucket S3, et stockez l'URL permanente dans un champ de texte au lieu d'utiliser le champ de pièce jointe d'Airtable. C'est ce que je recommande pour les sites de production — c'est l'approche la plus fiable.
Cache, limites de débit et performance
L'API d'Airtable a des limites de débit strictes : 5 requêtes par seconde par base. Ce n'est pas beaucoup. Voici comment rester bien en dessous.
| Stratégie | Framework | Comment ça marche |
|---|---|---|
| Génération statique | Astro | Tous les appels API se font au moment de la construction. Zéro appel à l'exécution. |
| ISR | Next.js | Réponses en cache, revalidées sur un minuteur. |
| Cache en mémoire | Les deux | Mettre en cache les réponses de l'API dans une Map avec TTL. |
| Webhook + reconstruction | Les deux | L'automatisation Airtable déclenche une reconstruction Vercel/Netlify. |
| Cache Redis/KV | Next.js (Vercel) | Stocker les réponses de l'API dans Vercel KV ou Upstash Redis. |
Pour les sites Astro, l'approche au moment de la construction signifie que vous ne frappez l'API que pendant les déploiements. Pour Next.js avec ISR, vous la frappez au maximum une fois par intervalle de revalidation par page.
Si vous avez beaucoup de pages et des intervalles de revalidation courts, envisagez de récupérer tous les enregistrements à la fois et de mettre en cache l'ensemble du dataset plutôt que de faire des appels API par page.
La pagination compte
Airtable retourne un maximum de 100 enregistrements par requête. La méthode .all() dans le SDK gère automatiquement la pagination, mais si vous utilisez fetch directement, vous devez suivre le 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;
}
Contenu texte enrichi et Markdown
Les champs de texte long d'Airtable peuvent stocker du Markdown si vous activez l'option « texte enrichi ». Mais ce que vous récupérez de l'API est du texte formaté en Markdown, pas du HTML.
Vous devez le convertir. J'utilise marked pour les cas simples ou unified avec les plugins remark pour plus de contrôle :
import { marked } from 'marked';
const htmlContent = marked.parse(post.body);
Pour Astro, vous pouvez aussi utiliser le traitement Markdown intégré :
---
import { marked } from 'marked';
const html = marked.parse(post.body);
---
<article set:html={html} />
Une chose à surveiller : l'éditeur de texte enrichi d'Airtable produit sa propre variante de Markdown. Il gère bien le gras, l'italique, les liens, les en-têtes et les listes. Les blocs de code et les tableaux sont supportés mais peuvent être délicats. Si votre contenu a besoin d'un formatage complexe, envisagez de faire écrire les éditeurs en mode Markdown brut.
Airtable vs options CMS headless traditionnelles
Soyons réalistes sur les compromis. Voici comment Airtable s'empile contre les plateformes CMS headless à usage spécifique en 2026 :
| Fonctionnalité | Airtable | Sanity | Contentful | Strapi |
|---|---|---|---|---|
| Courbe d'apprentissage de l'éditeur | Très faible | Moyen | Moyen | Bon |
| Modélisation de contenu | Flexible, informelle | Excellente | Excellente | Bonne |
| Limites de débit de l'API | 5 req/s par base | Généreux (CDN) | Généreux (CDN) | Auto-hébergé |
| Gestion des images | URLs expirant | CDN intégré | CDN intégré | Auto-hébergé |
| Aperçu/brouillons | Manuel (case à cocher) | Intégré | Intégré | Intégré |
| Tarification (équipe de 5) | 100 $/mois (Team) | Niveau gratuit viable | 300 $/mois+ | Gratuit (auto-hébergé) |
| Support des webhooks | Via automations | Intégré | Intégré | Intégré |
| Qualité du texte enrichi | Markdown de base | Portable Text | Structuré | Texte enrichi |
| Contenu relationnel | Enregistrements liés | Références | Références | Relations |
Airtable gagne en expérience éditeur et flexibilité. Il perd sur la gestion des images, les workflows d'aperçu et la fiabilité de l'API à l'échelle. Pour les petits-à-moyens sites où vos éditeurs sont déjà dans Airtable ? C'est un choix solide. Pour les sites riches en contenu avec des workflows complexes ? Allez avec un vrai CMS.
Modèles d'architecture du monde réel
Voici les modèles que j'ai utilisés en production :
Modèle 1 : Complètement statique avec Astro + webhooks de reconstruction
Meilleur pour : sites marketing, portfolios, répertoires avec < 500 enregistrements.
- Astro récupère toutes les données Airtable au moment de la construction.
- L'automatisation Airtable envoie un webhook à Vercel/Netlify lors de la mise à jour d'un enregistrement.
- Le site se reconstruit en 30-60 secondes.
- Les images téléchargées au moment de la construction — pas de problèmes d'URL expirant.
Modèle 2 : ISR avec Next.js
Meilleur pour : blogs, catalogues, sites avec mises à jour fréquentes.
- Next.js génère des pages avec ISR (revalidate toutes les 60-300 secondes).
- L'API Airtable appelée une fois par revalidation par page unique.
- Les images proxifiées via Cloudinary ou téléchargées vers un CDN.
- Les éditeurs voient les mises à jour en quelques minutes sans déclencher une reconstruction complète.
Modèle 3 : Airtable + CMS supplémentaire
Meilleur pour : sites où certains contenus vivent dans Airtable et d'autres ont besoin d'une édition plus riche.
- Les données structurées (membres de l'équipe, événements, produits) restent dans Airtable.
- Le contenu long (articles de blog, études de cas) va dans Sanity ou Notion.
- Le frontend récupère des deux sources au moment de la construction ou avec ISR.
Ce modèle hybride est plus courant que vous le penseriez. Nous avons construit plusieurs sites de cette façon.
Déclencher des reconstructions à partir d'Airtable
Airtable a des automations intégrées qui peuvent déclencher des webhooks. Configurez un déclencheur sur « Quand un enregistrement est mis à jour » dans votre table Posts, puis envoyez une requête POST au hook de construction de votre plateforme de déploiement :
// Vercel deploy hook
https://api.vercel.com/v1/integrations/deploy/prj_xxxx/yyyy
// Netlify build hook
https://api.netlify.com/build_hooks/xxxxxxxxxxxx
Ajoutez un délai de 30 secondes dans l'automatisation pour regrouper les modifications rapides.
FAQ
Airtable est-il gratuit à utiliser comme CMS ?
Le plan gratuit d'Airtable inclut 1 000 enregistrements par base et 1 000 appels API par mois. C'est suffisant pour un petit site, mais vous aurez probablement besoin du plan Team (20 $/siège/mois en 2026) pour quelque chose de sérieux. Le plan Team vous donne 50 000 enregistrements et des limites d'API plus élevées.
Comment gérer les URLs d'images expirant d'Airtable ?
Les URLs de pièces jointes Airtable expirent après environ 2 heures. Pour les sites statiques construits avec Astro, téléchargez les images au moment de la construction. Pour Next.js avec ISR, soit proxifiez les images via un CDN comme Cloudinary, soit stockez les URLs d'images dans un service d'hébergement d'images séparé et référencez-les comme champs de texte dans Airtable.
Airtable peut-il gérer un blog avec des centaines d'articles ?
Oui, jusqu'à un certain point. Airtable gère bien les centaines d'enregistrements. Une fois que vous arrivez aux milliers, la pagination d'API et les temps de construction commencent à devenir remarquables. Pour un blog avec moins de 1 000 articles, c'est correct. Au-delà, envisagez un CMS headless dédié.
Airtable est-il meilleur que Notion comme CMS ?
Ils résolvent des problèmes différents. Airtable est mieux pour les données structurées (produits, événements, membres de l'équipe) en raison de son modèle de base de données relationnelle. Notion est mieux pour le contenu écrit long en raison de son éditeur basé sur des blocs. L'API d'Airtable est aussi plus mature et plus rapide que celle de Notion.
Comment configurer la fonctionnalité d'aperçu/brouillon avec Airtable ?
Ajoutez un champ de sélection unique « Status » avec des options comme « Draft », « In Review » et « Published ». Créez une vue filtrée pour chaque statut. Votre site de production récupère à partir de la vue « Published ». Pour les aperçus, créez un itinéraire d'aperçu séparé qui récupère à partir de la vue « In Review », protégé par authentification.
Dois-je utiliser le SDK Airtable ou l'API REST directement ?
Pour Astro, le package npm airtable officiel fonctionne bien puisque vous récupérez au moment de la construction. Pour Next.js, je recommande d'utiliser directement l'API REST avec fetch — cela vous donne un contrôle sur les directives de cache Next.js comme revalidate et tags. Le SDK ne comprend pas les options fetch étendues de Next.js.
Quel est le nombre maximum d'appels API qu'Airtable autorise ?
Airtable applique une limite de débit de 5 requêtes par seconde par base. Dépasser cela retourne un code de statut 429. Sur le plan Team, vous obtenez une allocation d'appels mensuels plus élevée, mais la limite de débit par seconde reste la même. La génération statique et l'ISR sont les meilleurs moyens de minimiser l'utilisation de l'API.
Puis-je utiliser Airtable avec Astro et Next.js dans le même projet ?
Pas exactement dans le même projet, mais vous pouvez avoir une base Airtable partagée alimentant plusieurs frontends. Certaines équipes utilisent Astro pour leur site marketing et Next.js pour leur application web, tous les deux lisant à partir de la même base Airtable. Soyez juste conscient des limites de débit partagées sur tous les consommateurs.