# CMS Integration

> The CMS library in MakerKit abstracts content management from specific implementations. Use Keystatic, WordPress, or build your own CMS client.

*Canonical: https://makerkit.dev/docs/nextjs-prisma/content/cms*

---

MakerKit's CMS interface lets you fetch blog posts, documentation, and custom content from any backend - Keystatic, WordPress, or your own API - using a single, consistent API. Switch CMS providers without changing your application code. The abstraction handles collections, pagination, filtering, and content rendering while you focus on building features.

The CMS interface is a provider-agnostic abstraction layer that decouples content fetching from specific CMS implementations, enabling you to swap backends without modifying application code.

- **Use Keystatic when:** you want file-based content with Git versioning, zero external dependencies, and a built-in admin UI.
- **Use WordPress when:** you have existing WordPress content, need a familiar editing experience, or require advanced plugins.
- **Build a custom client when:** you're using Sanity, Strapi, Payload CMS, or any headless CMS not included by default.

**If unsure:** start with Keystatic in local mode - it requires no setup and works immediately.

## Available CMS Implementations

| CMS | Best For | Data Storage | Setup Complexity |
|-----|----------|--------------|------------------|
| [Keystatic](./keystatic) | Solo developers, Git workflows | Local files or GitHub | Minimal |
| [WordPress](./wordpress) | Teams with existing WP content | WordPress database | Moderate |
| [Custom Client](./creating-your-own-cms-client) | Any headless CMS | Your API | Variable |

## Quick Start

The CMS client is configured via environment variable:

```bash
# Default - Keystatic with local storage
CMS_CLIENT=keystatic

# Or use WordPress
CMS_CLIENT=wordpress
```

Fetch content using the unified API:

```typescript
import { createCmsClient } from '@kit/cms';

async function BlogPage() {
  const client = await createCmsClient();

  const { items } = await client.getContentItems({
    collection: 'posts',
    limit: 10,
  });

  return (
    <ul>
      {items.map((post) => (
        <li key={post.slug}>{post.title}</li>
      ))}
    </ul>
  );
}
```

The same code works regardless of which CMS backend you configure.

## Architecture

```
┌─────────────────────────────────────────────────────┐
│                Your Application                      │
│  (Blog pages, Documentation, Custom content pages)  │
└─────────────────────┬───────────────────────────────┘
                      │ createCmsClient()
                      ▼
┌─────────────────────────────────────────────────────┐
│              CMS Interface (@kit/cms)               │
│  getContentItems() | getContentItemBySlug()         │
│  getCategories()   | getTags()                      │
└─────────────────────┬───────────────────────────────┘
                      │ CMS_CLIENT env var
          ┌───────────┼───────────┐
          ▼           ▼           ▼
     ┌─────────┐ ┌─────────┐ ┌─────────┐
     │Keystatic│ │WordPress│ │ Custom  │
     │ Client  │ │ Client  │ │ Client  │
     └────┬────┘ └────┬────┘ └────┬────┘
          │           │           │
          ▼           ▼           ▼
     Local/GitHub  WP REST API  Your API
```

## Topics

1. **[Keystatic](./keystatic)** - File-based CMS with GitHub sync
2. **[WordPress](./wordpress)** - Traditional CMS via REST API
3. **[CMS API Reference](./cms-api)** - Full API documentation
4. **[Custom CMS Client](./creating-your-own-cms-client)** - Build your own integration

## Common Pitfalls

- **Using local mode with edge rendering**: Keystatic's local mode reads from the filesystem, which doesn't work on edge runtimes (Cloudflare Workers, Vercel Edge). Switch to GitHub mode or WordPress for edge deployments.
- **Forgetting to create collections**: The CMS API expects collections like `posts`, `documentation`, and `changelog` to exist. Keystatic creates these automatically; WordPress requires manual category setup.
- **Caching content incorrectly**: CMS content should be cached at the page level using Next.js ISR, not at the client level. Use `revalidate` in your page exports.
- **Mixing CMS implementations**: Each deployment should use one CMS backend. Don't try to combine Keystatic and WordPress in the same environment.

{% faq
   title="Frequently Asked Questions"
   items=[
     {"question": "Can I use a different CMS like Sanity or Contentful?", "answer": "Yes. Create a custom CMS client by extending the CmsClient abstract class. Implement the required methods to fetch content from your CMS API."},
     {"question": "How do I switch from Keystatic to WordPress?", "answer": "Change the CMS_CLIENT environment variable to 'wordpress', configure WORDPRESS_API_URL, and ensure your WordPress instance has matching content structure (posts, pages, categories)."},
     {"question": "Does the CMS support multiple languages?", "answer": "Yes. Pass the language parameter to getContentItems(). Implementation varies by CMS - Keystatic uses folder structure, WordPress uses tags."},
     {"question": "Can I use the CMS for custom content types?", "answer": "Yes. Create custom collections in your CMS configuration. The API supports any collection name - not just posts and documentation."},
     {"question": "Is content cached automatically?", "answer": "The CMS client doesn't cache internally. Use React's cache() function or Next.js page-level caching (revalidate) to avoid redundant fetches."}
   ]
/%}

---

**Next:** [Keystatic →](./keystatic)
