A modern, production-ready starter for building content-managed websites with Agility CMS and Next.js 15.
New to Agility CMS? Sign up for a FREE account
bash
git clone https://github.com/agility/agilitycms-nextjs-starter.git
cd agilitycms-nextjs-starter
bash
npm install
# or
yarn install
bash
cp .env.local.example .env.local
Get your API keys from Agility CMS
Log into Agility CMS
Copy your:
Update .env.local with your credentials:
env
AGILITY_GUID=your-guid-here
AGILITY_API_FETCH_KEY=your-live-api-key
AGILITY_API_PREVIEW_KEY=your-preview-api-key
AGILITY_SECURITY_KEY=your-security-key
AGILITY_LOCALES=en-us
AGILITY_SITEMAP=website
AGILITY_FETCH_CACHE_DURATION=120
AGILITY_PATH_REVALIDATE_DURATION=10
bash
npm run dev
# or
yarn dev
npm run build
npm run start
agilitycms-nextjs-starter/
├── app/ # Next.js App Router
│ ├── layout.tsx # Root layout with header/footer
│ ├── page.tsx # Home page (delegates to [...slug])
│ ├── [...slug]/ # Dynamic catch-all route
│ │ ├── page.tsx # Main page component with SSG
│ │ ├── error.tsx # Error boundary
│ │ └── not-found.tsx # 404 page
│ └── api/ # API routes
│ ├── preview/ # Preview mode endpoints
│ ├── preview/exit/ # Exit preview mode
│ ├── revalidate/ # Webhook for cache invalidation
│ └── dynamic-redirect/ # ContentID-based redirects
├── components/ # React components
│ ├── agility-components/ # CMS component modules
│ │ ├── FeaturedPost.tsx # Featured post display
│ │ ├── PostDetails.tsx # Dynamic post detail view
│ │ ├── PostsListing/ # Infinite scroll post list
│ │ ├── TextBlockWithImage.tsx # Flexible layout component
│ │ ├── RichTextArea.tsx # HTML content display
│ │ ├── Heading.tsx # Typography component
│ │ └── index.ts # Component registry
│ ├── agility-pages/ # Page templates
│ │ ├── MainTemplate.tsx # Main page template
│ │ └── index.ts # Template registry
│ └── common/ # Shared components
│ ├── SiteHeader.tsx # Responsive header with dark mode
│ ├── SiteFooter.tsx # Footer with social links
│ ├── PreviewBar.tsx # Preview/Live mode toggle
│ └── InlineError.tsx # Error display
├── lib/ # Utilities and helpers
│ ├── cms/ # CMS data fetching
│ │ ├── getAgilityContext.ts # Mode detection (preview/live)
│ │ ├── getAgilitySDK.ts # SDK initialization
│ │ ├── getAgilityPage.ts # Fetch pages with layout
│ │ ├── getContentItem.ts # Fetch single content item
│ │ ├── getContentList.ts # Fetch content lists
│ │ ├── getSitemapFlat.ts # Flat sitemap retrieval
│ │ └── getSitemapNested.ts # Nested sitemap retrieval
│ ├── cms-content/ # Domain-specific queries
│ │ ├── getPostListing.ts # Blog posts with URLs
│ │ ├── getHeaderContent.ts # Header navigation data
│ │ ├── getPageMetaData.ts # Page SEO metadata
│ │ └── resolveAgilityMetaData.ts # Advanced metadata
│ └── types/ # TypeScript interfaces
│ └── (IPost, IAuthor, ICategory, etc.)
├── styles/
│ └── globals.css # Tailwind imports & global styles
├── middleware.ts # Next.js middleware for routing
├── .env.local.example # Environment template
├── tailwind.config.js # Tailwind configuration
├── next.config.js # Next.js configuration
└── tsconfig.json # TypeScript configuration
This starter uses Next.js App Router with a catch-all dynamic route [...slug] that maps to Agility CMS pages.
How it works:
generateStaticParams() pre-renders all pages at build timeSee ARCHITECTURE.md for detailed explanation.
CMS components (modules) are mapped to React components via a registry pattern:
// components/agility-components/index.ts
const allModules = [
{name: "TextBlockWithImage", module: TextBlockWithImage},
{name: "Heading", module: Heading},
{name: "FeaturedPost", module: FeaturedPost},
{name: "PostsListing", module: PostsListing},
{name: "PostDetails", module: PostDetails},
{name: "RichTextArea", module: RichTextArea},
]
When Agility CMS returns a page with a "TextBlockWithImage" module, the system automatically renders the corresponding React component.
This starter uses React Server Components for optimal performance:
Example:
// Server Component (default)
export default async function PostDetails({module, page}) {
const post = await getContentItem({contentID: page.contentID})
return <article>...</article>
}
The Agility CMS instance includes the following content models:
See CONTENT-MODELS.md for complete schema documentation.
See COMPONENTS.md for complete component API documentation.
Located in lib/cms/, these utilities handle all Agility CMS interactions:
getAgilityContext()Determines the current mode (preview vs. production):
const context = await getAgilityContext()
// Returns: { isPreview: boolean, locale: string, sitemap: string }
getContentItem(contentID, languageCode)Fetches a single content item with cache tags:
const post = await getContentItem({
contentID: 123,
languageCode: "en-us",
})
getContentList(referenceName, languageCode, options)Fetches content lists with pagination and filtering:
const posts = await getContentList({
referenceName: "posts",
languageCode: "en-us",
take: 10,
skip: 0,
sort: "fields.date",
direction: "desc",
})
getAgilityPage(slug, locale, sitemap)Fetches a complete page with layout and content zones:
const page = await getAgilityPage({
slug: "/blog",
locale: "en-us",
sitemap: "website",
})
Located in lib/cms-content/, these build on the CMS utilities for specific use cases:
getPostListing() - Blog posts with category filtering and URLsgetHeaderContent() - Navigation structure and brandinggetPageMetaData() - SEO metadata for pagesSee AGILITY-CMS-GUIDE.md for complete data fetching patterns.
This starter implements a sophisticated caching strategy for optimal performance:
SDK Object Cache - Agility Fetch SDK caches content items
Controlled by AGILITY_FETCH_CACHE_DURATION (default: 120 seconds)
Works best with on-demand revalidation
Next.js Route Cache - Next.js caches rendered pages
AGILITY_PATH_REVALIDATE_DURATION (default: 10 seconds)Content fetches use cache tags for granular invalidation:
// Automatically tagged as: agility-content-{contentID}-{locale}
const post = await getContentItem({contentID: 123})
When content is published in Agility CMS, a webhook triggers revalidation:
The /api/revalidate endpoint handles webhook callbacks from Agility CMS:
// Revalidates specific content items and their dependent pages
POST /api/revalidate
{
"contentID": 123,
"languageCode": "en-us"
}
# Cache content objects for 120 seconds
AGILITY_FETCH_CACHE_DURATION=120
# Revalidate page paths every 10 seconds
AGILITY_PATH_REVALIDATE_DURATION=10
Best Practices:
0 without webhooks for faster content updatesPreview mode allows content editors to see draft content before publishing.
Preview Endpoint (app/api/preview/route.ts):
```typescript // Validates request and enables draft mode export async function GET(request: Request) { const {agilitypreviewkey, ContentID, slug} = searchParams
// Validate preview key
if (agilitypreviewkey !== process.env.AGILITY_SECURITY_KEY) {
return new Response("Invalid token", {status: 401})
}
// Enable draft mode
draftMode().enable()
// Redirect to pre
$ claude mcp add agilitycms-nextjs-starter \
-- python -m otcore.mcp_server <graph>