cmx-sdk
v0.2.14
Published
CMX SDK - Official SDK for building content-driven websites with CMX
Maintainers
Readme
cmx-sdk
Official SDK for building content-driven websites with CMX (Content Management Experience).
Installation
npm install cmx-sdk
# or
pnpm add cmx-sdk
# or
yarn add cmx-sdkFeatures
- Type-Safe API Client - Fetch content from CMX Admin with full TypeScript support
- MDX Rendering - Server-side MDX compilation with reference resolution
- MDX Components - Pre-built React components (Callout, Embed, BlogCard, Image, etc.)
- Next.js Optimized - Built-in support for ISR, caching, and revalidation
- Customizable - Inject your own custom components into MDX rendering
Quick Start
1. Set up environment variables
Create a .env.local file:
CMX_API_URL=https://your-cmx-admin.example.com
CMX_API_KEY=your_api_key_here2. Fetch content
import { getCollectionContents, getCollectionContentDetail } from 'cmx-sdk';
// List contents in a collection
const { collection, contents } = await getCollectionContents('blog');
// Get a specific content with references
const { collection, content, references } = await getCollectionContentDetail('blog', 'my-post');3. Render MDX content
import { renderMdx } from 'cmx-sdk';
// Render MDX with reference resolution (BlogCard, Image, etc.)
const { rendered } = await renderMdx(content.mdx, references);
return <article>{rendered}</article>;
// With custom components
import { FeatureCard } from './components/custom';
const { rendered } = await renderMdx(content.mdx, references, {
additionalComponents: { FeatureCard },
});API Reference
Content Fetching
import {
getCollectionContents,
getCollectionContentDetail,
getDataEntries,
getDataEntry,
getPreviewByToken,
} from 'cmx-sdk';
// Get contents from a collection
const { contents } = await getCollectionContents('blog');
// Get a specific content with resolved references
const { content, references } = await getCollectionContentDetail('blog', 'my-post');
// Get data entries (custom data types)
const { items } = await getDataEntries('team-members');
// Get data entries with options
const { items } = await getDataEntries('team-members', {
sortBy: 'createdAt',
sortOrder: 'desc',
limit: 10,
});
// Get a specific data entry
const entry = await getDataEntry('team-members', 'entry-id');
// Get preview content by token (no auth required)
const preview = await getPreviewByToken(token);
if (preview) {
const { content } = await renderMdx(preview.content.mdx, preview.references);
}MDX Rendering
import { renderMdx, renderMdxPreview } from 'cmx-sdk';
// Full rendering with reference resolution
const { content, references } = await renderMdx(item.mdx, item.references);
// With custom components injected
const { content } = await renderMdx(item.mdx, item.references, {
additionalComponents: { FeatureCard, PricingTable },
});
// Preview rendering (no reference resolution)
const element = await renderMdxPreview(mdxString);Cache Tags
Use Next.js cache tags for on-demand revalidation:
import { CACHE_TAGS, sdkFetchWithTags } from 'cmx-sdk';
// Built-in cache tags used by convenience functions:
// CACHE_TAGS.collections - all collections
// CACHE_TAGS.collection(slug) - specific collection
// CACHE_TAGS.content(col, content) - specific content
// CACHE_TAGS.data - all data types
// CACHE_TAGS.dataType(slug) - specific data type
// Revalidate by tag
import { revalidateTag } from 'next/cache';
revalidateTag(CACHE_TAGS.collection('blog'));
// Low-level: fetch with custom cache tags
const data = await sdkFetchWithTags<T>(
'/collections/blog/contents',
[CACHE_TAGS.collections, CACHE_TAGS.collection('blog')]
);Preview Mode
import { getPreviewByToken, renderMdx } from 'cmx-sdk';
const preview = await getPreviewByToken(token);
if (preview) {
const { content } = await renderMdx(preview.content.mdx, preview.references);
return <article>{content}</article>;
}Components
The SDK includes MDX components that are automatically available in renderMdx:
Standard Components
Callout- Highlighted information boxes (info, warning, error, success)Embed- External content embedding (YouTube, Twitter, etc.)Button- Interactive buttons
Reference-Resolving Components
These components automatically resolve references from the API response:
BlogCard- Linked content cards (resolves content title, slug, description)Image- Asset images with variants (resolves URL, alt text, dimensions)
Component Catalog
Access component metadata and validation:
import {
componentCatalog,
isValidComponent,
validateComponentProps,
validateMdx,
} from 'cmx-sdk';
// Check if a component is valid
isValidComponent('Callout'); // true
// Validate component props
const result = validateComponentProps('Callout', {
type: 'info',
title: 'Note',
});
// Validate entire MDX content
const validation = validateMdx(mdxString);Code Generation (CLI)
Generate typed functions and interfaces from your CMX data types and collections.
Usage
npx cmx-sdk codegen typesThis fetches your workspace's schema from the CMX API and generates TypeScript files in cmx/generated/.
Options
npx cmx-sdk codegen types --output src/cmx/generated
npx cmx-sdk codegen pages --template layered --app-dir src/app --features-dir src/features
npx cmx-sdk codegen check
npx cmx-sdk codegen allMDX Tooling
npx cmx-sdk mdx validate --dir src
npx cmx-sdk mdx validate --file src/content/post.mdx
npx cmx-sdk mdx doctorAsset Upload (CLI)
# Basic upload
npx cmx-sdk upload-asset --file ./public/images/logo.png
# With metadata
npx cmx-sdk upload-asset \
--file ./public/images/hero.webp \
--alt "トップページヒーロー画像" \
--description "2026 Spring campaign" \
--group landing-page \
--tags lp,hero,spring
# List assets
npx cmx-sdk list-assets --group landing-page --tags lp,hero --query "hero"
# Update asset metadata
npx cmx-sdk update-asset \
--id 123e4567-e89b-12d3-a456-426614174000 \
--alt "更新後の代替テキスト" \
--description "更新後の説明" \
--group landing-page \
--tags lp,hero,updated
# Clear group/tags
npx cmx-sdk update-asset \
--id 123e4567-e89b-12d3-a456-426614174000 \
--clear-group \
--clear-tagsupload-asset / list-assets / update-asset は MCP Assets API (/api/mcp/assets*) を使用します。
list-assets:assets:readupload-asset,update-asset:assets:write
Starter Kit Maintenance
# Replace .cmx/studio with the latest distributed Studio app
npx cmx-sdk update-studio
# Skip confirmation prompt when replacing Studio
npx cmx-sdk update-studio --force
# Update cmx-sdk dependency in your starter kit root
npx cmx-sdk update-sdk
# Pin to a specific version or tag
npx cmx-sdk update-sdk --version 0.2.11Requirements
Set CMX_API_URL and CMX_API_KEY in your .env or .env.local:
CMX_API_URL=https://your-cmx-admin.example.com
CMX_API_KEY=your_api_key_hereGenerated Output
cmx/generated/
├── index.ts
├── collections/
│ ├── index.ts
│ ├── blog.ts # getBlogContents(), getBlogContentDetail()
│ └── docs.ts # getDocsContents(), getDocsContentDetail()
└── data-types/
├── index.ts
├── team-members.ts # TeamMember type, getTeamMembers(), getTeamMember()
└── faq.ts # Faq type, getFaqs(), getFaq()Using Generated Code
// Before (generic, no type safety)
import { getDataEntries } from "cmx-sdk"
const { items } = await getDataEntries("team-members")
const name = items[0].name // ❌ type is unknown
// After (typed, auto-generated)
import { getTeamMembers } from "./cmx/generated"
const { items } = await getTeamMembers()
const name = items[0].name // ✅ type is stringData type fields are mapped to TypeScript types:
| Field Type | TypeScript Type |
|---|---|
| text, textarea, richtext, url, email | string |
| number | number |
| boolean | boolean |
| date, datetime | string |
| select (with choices) | "value1" \| "value2" |
| multiselect | string[] or union array |
| image, file | string |
| relation | string (or string[] if multiple) |
| json | unknown |
Non-required fields are typed as T | null.
TypeScript
The SDK is fully typed. Import types as needed:
import type {
// API response types
CollectionContentsResponse,
CollectionContentDetailResponse,
DataListResponse,
DataEntryItem,
PreviewResponse,
References,
ContentReference,
AssetReference,
// Rendering types
RenderResult,
RenderOptions,
ResolvedReferences,
// Component types
ComponentDefinition,
ValidationResult,
} from 'cmx-sdk';Advanced Usage
Low-Level Generated Functions
For advanced use cases, the SDK also exports the raw Orval-generated endpoint functions:
import {
getCollectionsSlugContents,
getCollectionsSlugContentsContentSlug,
getDataTypeSlug,
getPreviewToken,
} from 'cmx-sdk';
// These return { data, status, headers } instead of just the data
const response = await getCollectionsSlugContents('blog');
if (response.status === 200) {
const contents = response.data;
}Direct Fetcher Access
import {
sdkFetcher,
sdkFetchWithTags,
getSdkApiBaseUrl,
getSdkApiToken,
} from 'cmx-sdk';
// Use the fetcher directly for custom endpoints
const data = await sdkFetchWithTags<MyType>('/custom/endpoint', ['my-tag']);Environment Variables
| Variable | Description | Required |
|----------|-------------|----------|
| CMX_API_URL | CMX Admin API base URL | Yes |
| CMX_API_KEY | API key for authentication | Yes |
Next.js Integration
The SDK is optimized for Next.js App Router (Server Components):
// app/blog/[slug]/page.tsx
import { getCollectionContents, getCollectionContentDetail, renderMdx } from 'cmx-sdk';
export default async function BlogContent({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const { content, references } = await getCollectionContentDetail('blog', slug);
const { content: rendered } = await renderMdx(content.mdx, references);
return (
<article>
<h1>{content.title}</h1>
<p>{content.description}</p>
<div className="prose">{rendered}</div>
</article>
);
}
export async function generateStaticParams() {
const { contents } = await getCollectionContents('blog');
return contents.map((item) => ({ slug: item.slug }));
}
export async function generateMetadata({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const { content } = await getCollectionContentDetail('blog', slug);
return {
title: content.title,
description: content.description,
};
}Examples
Check out the CMX Starter Kit for a complete example of using the SDK.
License
MIT
