Terminal.skills
Skills/contentful
>

contentful

You are an expert in Contentful, the API-first content platform for enterprise teams. You help developers integrate Contentful's Content Delivery API (CDN-backed, read), Content Management API (write), and Content Preview API (draft content) into websites and apps — using typed content models, localization, rich text rendering, image transformations, and webhooks for build triggers.

#headless-cms#saas#content-api#cdn#enterprise#graphql
terminal-skillsv1.0.0
Works with:claude-codeopenai-codexgemini-clicursor
Source

Usage

$
✓ Installed contentful v1.0.0

Getting Started

  1. Install the skill using the command above
  2. Open your AI coding agent (Claude Code, Codex, Gemini CLI, or Cursor)
  3. Reference the skill in your prompt
  4. The AI will use the skill's capabilities automatically

Example Prompts

  • "Review the open pull requests and summarize what needs attention"
  • "Generate a changelog from the last 20 commits on the main branch"

Information

Version
1.0.0
Author
terminal-skills
Category
Content Management
License
Apache-2.0

Documentation

You are an expert in Contentful, the API-first content platform for enterprise teams. You help developers integrate Contentful's Content Delivery API (CDN-backed, read), Content Management API (write), and Content Preview API (draft content) into websites and apps — using typed content models, localization, rich text rendering, image transformations, and webhooks for build triggers.

Core Capabilities

Content Delivery API

typescript
// src/lib/contentful.ts — Contentful client setup
import { createClient, type Entry, type Asset } from "contentful";

const client = createClient({
  space: process.env.CONTENTFUL_SPACE_ID!,
  accessToken: process.env.CONTENTFUL_DELIVERY_TOKEN!,
  // For draft preview:
  // accessToken: process.env.CONTENTFUL_PREVIEW_TOKEN,
  // host: "preview.contentful.com",
});

// TypeScript interfaces matching content model
interface BlogPostFields {
  title: string;
  slug: string;
  excerpt: string;
  body: Document;                         // Rich Text
  featuredImage: Asset;
  author: Entry<AuthorFields>;
  tags: string[];
  publishDate: string;
}

// Fetch entries with type safety
async function getBlogPosts(limit = 10): Promise<Entry<BlogPostFields>[]> {
  const response = await client.getEntries<BlogPostFields>({
    content_type: "blogPost",
    order: ["-fields.publishDate"],
    limit,
    include: 2,                           // Resolve 2 levels of linked entries
    "fields.slug[exists]": true,          // Only entries with slug
  });
  return response.items;
}

async function getBlogPostBySlug(slug: string) {
  const response = await client.getEntries<BlogPostFields>({
    content_type: "blogPost",
    "fields.slug": slug,
    include: 3,
    limit: 1,
  });
  return response.items[0] || null;
}

Rich Text Rendering

tsx
// src/components/RichTextRenderer.tsx — Render Contentful rich text
import { documentToReactComponents, Options } from "@contentful/rich-text-react-renderer";
import { BLOCKS, INLINES, Document } from "@contentful/rich-text-types";
import Image from "next/image";

const renderOptions: Options = {
  renderNode: {
    [BLOCKS.EMBEDDED_ASSET]: (node) => {
      const { title, file } = node.data.target.fields;
      return (
        <Image
          src={`https:${file.url}`}
          alt={title}
          width={file.details.image.width}
          height={file.details.image.height}
          className="rounded-lg my-6"
        />
      );
    },
    [BLOCKS.EMBEDDED_ENTRY]: (node) => {
      const entry = node.data.target;
      if (entry.sys.contentType.sys.id === "codeBlock") {
        return (
          <pre className="bg-gray-900 p-4 rounded-lg overflow-x-auto">
            <code className={`language-${entry.fields.language}`}>
              {entry.fields.code}
            </code>
          </pre>
        );
      }
      return null;
    },
    [INLINES.HYPERLINK]: (node, children) => (
      <a href={node.data.uri} className="text-blue-600 underline" target="_blank" rel="noopener">
        {children}
      </a>
    ),
  },
};

export function RichText({ document }: { document: Document }) {
  return <div className="prose max-w-none">{documentToReactComponents(document, renderOptions)}</div>;
}

Image Transformations

tsx
// Contentful Images API — resize, crop, format on the fly
function contentfulImageUrl(asset: Asset, options?: {
  width?: number;
  height?: number;
  quality?: number;
  format?: "webp" | "avif" | "jpg" | "png";
  fit?: "pad" | "fill" | "scale" | "crop" | "thumb";
  focus?: "face" | "center" | "top" | "bottom";
}) {
  const url = new URL(`https:${asset.fields.file.url}`);
  if (options?.width) url.searchParams.set("w", String(options.width));
  if (options?.height) url.searchParams.set("h", String(options.height));
  if (options?.quality) url.searchParams.set("q", String(options.quality));
  if (options?.format) url.searchParams.set("fm", options.format);
  if (options?.fit) url.searchParams.set("fit", options.fit);
  if (options?.focus) url.searchParams.set("f", options.focus);
  return url.toString();
}

// Usage: auto-optimize for web
<Image
  src={contentfulImageUrl(post.fields.featuredImage, {
    width: 800,
    format: "webp",
    quality: 80,
  })}
  alt={post.fields.title}
/>

Installation

bash
npm install contentful                     # Delivery SDK
npm install @contentful/rich-text-react-renderer  # Rich text → React
npm install contentful-management          # Management API (write)

Best Practices

  1. Delivery API for reads — Use CDN-backed Delivery API for production; Preview API only for draft preview
  2. Type generation — Use contentful-typescript-codegen to generate TypeScript types from your content model
  3. Rich text renderer — Always use @contentful/rich-text-react-renderer with custom renderers for embedded assets and entries
  4. Image API — Transform images on the fly with URL params; serve WebP with quality 80 for 70% size reduction
  5. Webhooks for builds — Configure webhooks to trigger Vercel/Netlify builds on publish; ISR for immediate updates
  6. Localization — Set up locales in Contentful; fetch with locale: "de" or locale: "*" for all locales
  7. Include depth — Set include: 2-3 to resolve linked entries; avoids N+1 queries for related content
  8. Environment branching — Use Contentful Environments (like Git branches) for content model changes; merge to master when ready