What is your developer-dependent CMS actually costing you? Calculate your costs and get a full report
What is your developer-dependent CMS actually costing you? Calculate your costs and get a full report
Basic Starter
Get started quickly using a simple blog template built with best practices.
A complete guide to setting up and running your Agility CMS + Next.js website locally and deploying to production.
Before you begin, ensure you have the following installed:
The fastest way to get started is to deploy first, then clone and work locally:
Log in to your Agility CMS instance
Navigate to Settings > Deployment
Click "Setup Deployment" for Vercel
Follow the automated deployment wizard
Clone your repository locally
git clone https://github.com/YOUR-USERNAME/YOUR-REPO-NAME.git
cd YOUR-REPO-NAME
npm install
Copy environment variables from Vercel
.env.local tab.env.local (see below)If you prefer to start with local development:
Clone this repository
git clone https://github.com/agility/agilitycms-nextjs-starter.git
cd agilitycms-nextjs-starter
Install dependencies
npm install
Get your Agility CMS credentials
Create your environment file
Copy .env.local.example to .env.local:
cp .env.local.example .env.local
Then edit .env.local with your credentials:
# Your Agility CMS Instance GUID
AGILITY_GUID=your-guid-here
# API Keys (from Settings > API Keys)
AGILITY_API_FETCH_KEY=your-live-api-key
AGILITY_API_PREVIEW_KEY=your-preview-api-key
# Security Key (for webhooks and preview mode)
AGILITY_SECURITY_KEY=your-security-key
# Locales (comma-separated list, first is default)
AGILITY_LOCALES=en-us
# Sitemap reference name (usually 'website')
AGILITY_SITEMAP=website
# Cache durations (in seconds)
AGILITY_FETCH_CACHE_DURATION=120
AGILITY_PATH_REVALIDATE_DURATION=60
Run the development server
npm run dev
Open your browser
Navigate to http://localhost:3000
You should see your site with the sample content from your Agility instance!
Start the Next.js development server:
npm run dev
Features in dev mode:
Test a production build locally:
# Build the site
npm run build
# Start the production server
npm start
The build process will:
.next/ directoryagilitycms-nextjs-starter/
├── app/ # Next.js App Router
│ ├── layout.tsx # Root layout (header, footer)
│ ├── page.tsx # Homepage
│ ├── [...slug]/ # Dynamic catch-all route
│ │ ├── page.tsx # Page renderer
│ │ ├── error.tsx # Error boundary
│ │ └── not-found.tsx # 404 page
│ └── api/ # API routes
│ ├── preview/ # Preview mode activation
│ ├── revalidate/ # Webhook handler
│ └── dynamic-redirect/ # ContentID redirects
│
├── components/
│ ├── agility-components/ # CMS modules (registered)
│ │ ├── index.ts # Module registry
│ │ ├── Heading.tsx
│ │ ├── RichTextArea.tsx
│ │ ├── PostsListing/
│ │ └── ...
│ ├── agility-pages/ # Page templates
│ │ ├── index.ts # Template registry
│ │ └── MainTemplate.tsx
│ └── common/ # Shared UI components
│ ├── SiteHeader.tsx
│ ├── SiteFooter.tsx
│ ├── PreviewBar.tsx
│ └── ...
│
├── lib/
│ ├── cms/ # Generic CMS utilities
│ │ ├── getAgilitySDK.ts
│ │ ├── getContentItem.ts
│ │ ├── getContentList.ts
│ │ └── ...
│ ├── cms-content/ # Domain-specific queries
│ │ ├── getPostListing.ts
│ │ ├── getHeaderContent.ts
│ │ └── ...
│ └── types/ # TypeScript interfaces
│ ├── IPost.ts
│ ├── IAuthor.ts
│ └── ...
│
├── docs/ # Documentation
├── public/ # Static assets
├── styles/ # Global styles
└── proxy.ts # Proxy — preview mode & redirects
| File | Purpose |
|---|---|
app/[...slug]/page.tsx | Renders all content pages dynamically |
components/agility-components/index.ts | Registers CMS modules → React components |
components/agility-pages/index.ts | Registers page templates |
lib/cms/getAgilityPage.ts | Fetches complete page with layout |
proxy.ts | Handles preview mode and redirects (renamed from middleware.ts in Next.js 16) |
.env.local | Environment variables (not committed) |
Agility CMS manages your sitemap
Build time: Static Generation
// app/[...slug]/page.tsx
export async function generateStaticParams() {
// Fetches all pages from Agility CMS
const sitemap = await getSitemapFlat({ languageCode: "en-us" });
// Returns paths: ['/', '/about', '/blog', '/blog/post-1', ...]
return sitemap.map((node) => ({
slug: node.pagePath.split("/").filter(Boolean),
}));
}
Next.js generates HTML for each path
/ → index.html/about → about.html/blog/post-1 → blog/post-1.htmlRuntime: Page Rendering
export default async function Page({ params }) {
// Get page data from Agility
const page = await getAgilityPage({
slug: params.slug.join("/")
});
// Render appropriate template
const Template = getPageTemplate(page.templateName);
return <Template page={page} />;
}
In Agility CMS:
In Next.js:
components/agility-components/Heading.tsxcomponents/agility-components/index.tsExample:
// components/agility-components/Heading.tsx
import { UnloadedModuleProps } from "@agility/nextjs";
interface IHeadingModule {
title: string;
subtitle?: string;
}
export default async function Heading({
module
}: UnloadedModuleProps) {
const { fields } = module as { fields: IHeadingModule };
return (
<section className="py-12">
<h1 className="text-5xl font-bold dark:text-white">
{fields.title}
</h1>
{fields.subtitle && (
<p className="text-xl text-gray-600 dark:text-gray-400">
{fields.subtitle}
</p>
)}
</section>
);
}
This starter uses a three-tier architecture:
Component
↓
Domain Helper (lib/cms-content/)
↓
CMS Utility (lib/cms/)
↓
Agility SDK
Example:
// Component
const posts = await getPostListing({ take: 10 });
// Domain Helper (lib/cms-content/getPostListing.ts)
export async function getPostListing({ take, skip }) {
const posts = await getContentList({
referenceName: "posts",
take,
skip
});
// Add computed fields (URLs, etc.)
return { posts: postsWithUrls };
}
// CMS Utility (lib/cms/getContentList.ts)
export async function getContentList({ referenceName }) {
const api = getAgilitySDK({ isPreview });
return await api.getContentList({ referenceName });
}
Why Vercel?
Deploy via Agility Integration:
Manual Vercel Deployment:
AGILITY_GUIDAGILITY_API_FETCH_KEYAGILITY_API_PREVIEW_KEYAGILITY_SECURITY_KEYAGILITY_LOCALESAGILITY_SITEMAPSetup Webhooks:
https://your-site.vercel.app/api/revalidatex-agility-webhook-secret: YOUR_AGILITY_SECURITY_KEYThis starter includes a GitHub Actions workflow for Azure Static Web Apps.
Deployment Steps:
Create Azure Static Web App
.nextConfigure Build
The included workflow at .github/workflows/azure-static-web-apps-wonderful-meadow-008797210.yml handles:
npm run build-swaSet Repository Secrets
In GitHub: Settings > Secrets and variables > Actions
Add:
AZURE_STATIC_WEB_APPS_API_TOKEN_WONDERFUL_MEADOW_008797210 (from Azure)AGILITY_API_FETCH_KEYAnd Variables:
AGILITY_GUIDAGILITY_LOCALESAGILITY_SITEMAPSetup Webhooks
In Agility CMS: Settings > Webhooks
https://your-site.azurestaticapps.net/api/revalidateDeploy to Netlify:
npm run build.nextSetup Webhooks: Same process as Vercel, using your Netlify URL.
You can deploy to any Node.js hosting:
npm run build
npm start
Requires:
Preview mode allows editors to see draft content before publishing.
Settings > Deployment:
https://your-site.vercel.apphttps://your-site.com (or Vercel URL)Editor clicks "Preview" in CMS
↓
Request: /page?agilitypreviewkey=SECRET&ContentID=123
↓
Proxy intercepts → /api/preview
↓
API validates key, enables draft mode
↓
Redirects to actual page URL
↓
Page renders with draft content
↓
Preview bar appears at top
Start dev server: npm run dev
Get a preview URL from any page in Agility CMS
Replace the domain:
https://your-site.com/about?agilitypreviewkey=...&ContentID=123
becomes
http://localhost:3000/about?agilitypreviewkey=...&ContentID=123
You should see:
Click "Exit Preview" in the preview bar, or visit:
http://localhost:3000/api/preview/exit
Add a new component:
# 1. Create component
touch components/agility-components/MyComponent.tsx
# 2. Register component
# Edit components/agility-components/index.ts
# 3. Create component model in Agility CMS
# Match the reference name to "MyComponent"
Add a new content model:
// 1. Define interface
// lib/types/IMyModel.ts
export interface IMyModel {
contentID: number;
title: string;
// ... fields
}
// 2. Create helper
// lib/cms-content/getMyData.ts
export async function getMyData() {
return await getContentList<IMyModel>({
referenceName: "mymodel"
});
}
Customize styling:
styles/globals.css for global stylestailwind.config.js for theme customizationError: "Missing environment variables"
.env.local file existsError: "Invalid API Key"
Error: "Module not found"
npm install to ensure dependencies are installedrm -rf .nextnpm run buildPreview not working:
AGILITY_SECURITY_KEY is set correctlyagilitypreviewkey paramCan't exit preview mode:
/api/preview/exit directlyChanges not appearing:
npm run dev.env.localOld content still showing:
/api/revalidate (POST)revalidate duration to expireSlow build times:
Slow page loads:
<AgilityPic>)npm run build (see output)/docs folderReady to build? Start by creating your first component! See COMPONENTS.md for a step-by-step guide.