Start a blog with Next.js, AgilityCMS and TailwindCSS.

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
Clone the repository
git clone https://github.com/agility/agilitycms-nextjs-starter.gitcd agilitycms-nextjs-starter
Install dependencies
npm install# oryarn install
Configure environment variables
cp .env.local.example .env.local
Get your API keys from Agility CMS
Update .env.local with your credentials:
AGILITY_GUID=your-guid-hereAGILITY_API_FETCH_KEY=your-live-api-keyAGILITY_API_PREVIEW_KEY=your-preview-api-keyAGILITY_SECURITY_KEY=your-security-keyAGILITY_LOCALES=en-usAGILITY_SITEMAP=websiteAGILITY_FETCH_CACHE_DURATION=120AGILITY_PATH_REVALIDATE_DURATION=10
Run the development server
npm run dev# oryarn dev
Open your browser to http://localhost:3000
npm run buildnpm 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.tsconst 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
AGILITY_FETCH_CACHE_DURATION (default: 120 seconds)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 pagesPOST /api/revalidate{"contentID": 123,"languageCode": "en-us"}
# Cache content objects for 120 secondsAGILITY_FETCH_CACHE_DURATION=120# Revalidate page paths every 10 secondsAGILITY_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):
// Validates request and enables draft modeexport async function GET(request: Request) {const {agilitypreviewkey, ContentID, slug} = searchParams// Validate preview keyif (agilitypreviewkey !== process.env.AGILITY_SECURITY_KEY) {return new Response("Invalid token", {status: 401})}// Enable draft modedraftMode().enable()// Redirect to preview pageredirect(slug)}
Middleware (middleware.ts):
// Intercepts preview requests before they reach pagesexport function middleware(request: NextRequest) {const {pathname, searchParams} = request.nextUrlif (searchParams.has("agilitypreviewkey")) {// Rewrite to preview API for validationreturn NextResponse.rewrite(new URL("/api/preview", request.url))}}
Preview Bar (components/common/PreviewBar.tsx):
Set your security key in .env.local:
AGILITY_SECURITY_KEY=your-security-key-from-agility
This key must match the one configured in Agility CMS webhook settings.
Vercel provides the best Next.js experience with zero configuration:
.env.local valuesEnvironment Variables to Set:
AGILITY_GUIDAGILITY_API_FETCH_KEYAGILITY_API_PREVIEW_KEYAGILITY_SECURITY_KEYAGILITY_LOCALESAGILITY_SITEMAPAGILITY_FETCH_CACHE_DURATIONAGILITY_PATH_REVALIDATE_DURATION
Configure Webhooks in Agility CMS:
https://your-site.vercel.app/api/revalidatenpm run build.next.env.local valuesThis starter can deploy to any platform supporting Next.js:
Ensure your platform supports:
This starter is designed to work seamlessly with AI coding assistants like Claude Code, GitHub Copilot, Cursor, and ChatGPT for rapid development.
"Vibe coding" is the practice of using AI assistants to help you build features by describing what you want in natural language, rather than writing every line of code manually. This starter's comprehensive documentation makes it perfect for AI-assisted development.
Create a new component:
Create a "TeamGrid" component that:- Fetches team members from a "teammembers" container- Displays them in a responsive 3-column grid- Shows name, title, photo, and bio- Supports dark mode- Follows the same patterns as PostsListing
Add a feature:
Add search functionality to the PostsListing component:- Add a search input at the top- Filter posts by title/content as user types- Maintain infinite scroll behavior- Use client component for interactivity
Extend existing code:
Looking at TextBlockWithImage component, create a similar"ImageGallery" component that shows multiple images in a gridwith lightbox functionality.
This project includes the Agility CMS MCP Server configuration in .vscode/mcp.json, which gives AI coding assistants (like Claude Code) direct access to your CMS instance through the Model Context Protocol.
What is MCP?
MCP (Model Context Protocol) is a standard that allows AI assistants to connect to external services. The Agility MCP Server acts as a bridge between your AI assistant and your Agility CMS instance, providing real-time access to your content structure and data.
What AI can do with MCP:
Example Workflows with MCP:
Creating a new component:
Use the Agility MCP server to get the "Products" content model,then create:1. A TypeScript interface (IProduct) with all fields2. A ProductGrid component with category filtering3. A domain helper (getProductListing) for data fetching4. Register the component in the index
Validating existing code:
Query the Agility MCP server for the "TeamMembers" model andverify that the ITeamMember interface in lib/types/ has allthe correct fields with proper types.
Discovering opportunities:
Use MCP to list all content models in my instance, thensuggest 5 new components I could build based on unused models.
Why MCP Makes AI Development Better:
| Without MCP | With MCP |
|---|---|
| AI guesses field names | AI sees exact field names |
| AI assumes field types | AI knows actual field types |
| Trial and error fixing typos | Works first time |
| Generic component templates | CMS-specific, accurate code |
| Manual schema documentation | Direct CMS inspection |
Getting Started with MCP:
.vscode/mcp.json exists - Already configured in this projectMCP Server Configuration:
The project includes a pre-configured MCP connection:
// .vscode/mcp.json{"servers": {"Agility CMS": {"url": "https://mcp.agilitycms.com/api/mcp","type": "http"}}}
This connects to the public Agility MCP server, which requires your API keys (from .env.local) to access your specific instance.
Learn More:
For more advanced examples including AI integration, personalization, and complex patterns, see:
This repository includes:
Use it as a reference when asking AI to build advanced features:
Looking at the nextjs-demo-site-2025 repo, implement a similarAI search modal for this project.
Reference Documentation - Point AI to specific docs:
Provide Context - Share your CMS structure:
Request Tests - Ask AI to validate:
Iterate - Refine in steps:
Use Type Safety - Let AI leverage TypeScript:
1. Describe Feature β AI generates componentβ2. Review Code β AI refines based on feedbackβ3. Create CMS Model β AI generates TypeScript interfaceβ4. Register Component β AI updates index fileβ5. Test & Iterate β AI fixes issues
This documentation-first approach makes AI assistants highly effective at extending this starter with new features, components, and integrations.
Detailed documentation for specific topics:
AI Assistant Configuration:
.cursorrules - Rules for Cursor, Claude Code, and other AI tools (auto-loaded)Shows latest content in real-time (uses Preview API Key):
npm run dev
Shows published content (uses Live API Key):
npm run buildnpm run start
This project uses TypeScript with strict mode. Type definitions for CMS content are in lib/types/.
npm run lint
next/core-web-vitals.prettierrc for consistent formattingContributions are welcome! Please feel free to submit a Pull Request.
If you have feedback or questions about this starter:
This project is licensed under the MIT License.
Made with β€οΈ by Agility CMS