← Retour aux articles
Ecosystem SanityCMSReact

Sanity : le CMS headless pensé pour les développeurs

· 4 min de lecture

La plupart des CMS imposent aux développeurs un modèle de contenu rigide et un éditeur WYSIWYG qui produit du HTML imprévisible. Sanity prend l’approche inverse. Le contenu est structuré, stocké en JSON, et requêté via GROQ, un langage de requête dédié. L’interface d’édition est une application React qu’on possède et personnalise. Le content lake est un backend API-first qui fonctionne avec n’importe quel framework frontend, de Next.js à Astro en passant par les apps mobiles.

Modélisation de contenu pilotée par le schema

Les types de contenu dans Sanity sont définis en TypeScript comme des objets schema. Pas de panneau admin pour assembler des champs au clic. Le schema vit dans le codebase, versionné avec le code de l’application.

import { defineType, defineField } from 'sanity';

export const project = defineType({
  name: 'project',
  title: 'Project',
  type: 'document',
  fields: [
    defineField({
      name: 'title',
      type: 'string',
      validation: (rule) => rule.required().max(100),
    }),
    defineField({
      name: 'slug',
      type: 'slug',
      options: { source: 'title', maxLength: 96 },
    }),
    defineField({
      name: 'description',
      type: 'text',
      rows: 3,
    }),
    defineField({
      name: 'technologies',
      type: 'array',
      of: [{ type: 'string' }],
    }),
    defineField({
      name: 'coverImage',
      type: 'image',
      options: { hotspot: true },
    }),
  ],
});

defineType et defineField fournissent l’autocomplétion TypeScript complète. Les règles de validation sont déclarées inline. L’option hotspot sur les images permet aux éditeurs de définir un point focal que les recadrages responsives respectent automatiquement. Chaque champ correspond à une structure JSON, donc le contenu est portable et agnostique du framework.

GROQ : requêter le contenu sans la complexité de GraphQL

GROQ est le langage de requête de Sanity. Il se lit comme un pipeline de filtres et renvoie exactement la forme demandée. Pas de sur-fetching, pas de fonctions resolver, pas de schema stitching.

import { createClient } from '@sanity/client';

const client = createClient({
  projectId: process.env.SANITY_PROJECT_ID!,
  dataset: 'production',
  apiVersion: '2024-09-01',
  useCdn: true,
});

export async function getProjects() {
  return client.fetch(`
    *[_type == "project"] | order(_createdAt desc) {
      title,
      "slug": slug.current,
      description,
      technologies,
      "imageUrl": coverImage.asset->url
    }
  `);
}

La requête filtre les documents par type, les ordonne, et projette uniquement les champs nécessaires. L’opérateur -> déréférence les documents liés inline. useCdn: true sert les réponses cachées depuis un CDN global, rendant les lectures assez rapides pour la génération statique et l’ISR dans les applications Next.js.

Personnaliser le Studio

Le Sanity Studio est une application React. Chaque partie, de la liste de documents aux inputs de formulaire en passant par les panneaux de preview, est personnalisable via des composants React et des plugins.

import { defineConfig } from 'sanity';
import { structureTool } from 'sanity/structure';
import { visionTool } from '@sanity/vision';
import { schemaTypes } from './schemas';

export default defineConfig({
  name: 'portfolio',
  title: 'Portfolio Studio',
  projectId: process.env.SANITY_PROJECT_ID!,
  dataset: 'production',
  plugins: [structureTool(), visionTool()],
  schema: { types: schemaTypes },
});

Le plugin visionTool ajoute un playground GROQ directement dans le studio pour tester les requêtes sur les données live. structureTool gère l’interface d’édition des documents. Les inputs custom, les cartes de preview et les actions sur documents sont tous des composants React classiques qui se branchent dans la configuration.

Portable Text pour le contenu riche

Au lieu de stocker le contenu riche en HTML, Sanity utilise Portable Text, un format JSON qui sépare le contenu de la présentation. Ce qui signifie que le même contenu se rend différemment sur un site web, une app mobile et un email sans aucun parsing HTML.

import { PortableText } from '@portabletext/react';

const components = {
  types: {
    image: ({ value }: { value: SanityImage }) => (
      <figure>
        <img src={urlFor(value).width(800).url()} alt={value.alt} />
        {value.caption ? <figcaption>{value.caption}</figcaption> : null}
      </figure>
    ),
    code: ({ value }: { value: { code: string; language: string } }) => (
      <pre data-language={value.language}>
        <code>{value.code}</code>
      </pre>
    ),
  },
};

export function RichText({ content }: { content: PortableTextBlock[] }) {
  return <PortableText value={content} components={components} />;
}

Les types de blocs custom comme les snippets de code, les vidéos embarquées ou les callouts sont définis dans le schema et rendus via la component map. Le contenu reste structuré et la logique de rendu reste dans le frontend là où elle doit être.

Conclusion

Sanity traite le contenu comme de la donnée, pas du markup. La modélisation pilotée par le schema garde la structure de contenu prévisible, les requêtes GROQ renvoient exactement ce dont le frontend a besoin, et le Studio React est aussi personnalisable que n’importe quelle librairie de composants. Pour les développeurs qui construisent avec Next.js ou tout framework moderne, c’est un CMS qui s’intègre dans le workflow de développement au lieu de travailler contre.