What is AEM Headless?
In traditional AEM, content and presentation are tightly coupled—AEM renders HTML pages using HTL templates. AEM Headless decouples these concerns: AEM manages content while external applications handle rendering.
With headless, AEM becomes a content API. You query structured content via GraphQL or REST, then render it in React, Next.js, Vue, mobile apps, digital signage—any channel that can consume an API.
Core Concepts
Content Fragment Models
Schema definitions that specify what fields a content type has—text, numbers, dates, references, JSON, etc. Think of them as content blueprints.
Content Fragments
Instances of Content Fragment Models. Structured content pieces that authors create and edit. They're channel-agnostic and API-ready.
GraphQL API
AEM automatically generates a GraphQL schema from your Content Fragment Models. Query exactly the fields you need with standard GraphQL syntax.
Persisted Queries
Pre-saved GraphQL queries stored in AEM. They're cached at the CDN level and execute faster than ad-hoc queries.
Content Fragment Models
A Content Fragment Model defines the structure of your content. Here's an example model for a blog article:
- Title (Single-line text, required)
- Slug (Single-line text, required)
- Author (Fragment reference → Author model)
- Publish Date (Date and time)
- Hero Image (Content reference → DAM asset)
- Body (Multi-line text, Rich text enabled)
- Tags (Enumeration, multi-value)
- Related Articles (Fragment references, multi-value)
Models are created in AEM's Tools console. Each model generates a corresponding GraphQL type.
Design models for reusability. Instead of putting author info directly on Article, create a separate Author model and reference it. This allows the same author to be linked across multiple articles.
Creating Content Fragments
Authors create Content Fragments in the Assets console (or Experience Fragments console in newer versions). The editing experience is form-based, following the model's field definitions.
Content Fragments support:
- Variations — Multiple versions of content (e.g., short, long, regional)
- Metadata — Tags, descriptions, and custom properties
- Versioning — Full version history with compare and restore
- Workflows — Approval processes before publishing
Querying with GraphQL
AEM's GraphQL endpoint automatically exposes your Content Fragment Models. Here's a sample query:
{
articleList(
filter: { slug: { _expressions: [{ value: "getting-started-aem" }] } }
) {
items {
title
publishDate
heroImage {
... on ImageRef {
_path
_dynamicUrl
}
}
author {
name
bio
avatar {
... on ImageRef {
_path
}
}
}
body {
html
}
tags
}
}
}
GraphQL Endpoints
AEM provides GraphQL endpoints per configuration folder:
- Endpoint URL:
/content/cq:graphql/[config]/endpoint.json - GraphiQL IDE:
/content/cq:graphql/[config]/explorer.html
The GraphiQL IDE is invaluable for exploring your schema, testing queries, and viewing documentation.
Filtering & Pagination
GraphQL supports powerful filtering:
{
articleList(
filter: {
tags: { _expressions: [{ values: ["tech", "cloud"], _operator: CONTAINS }] }
publishDate: { _expressions: [{ value: "2025-01-01", _operator: GREATER }] }
}
_locale: "en"
sort: "publishDate DESC"
offset: 0
limit: 10
) {
items {
title
slug
publishDate
}
}
}
Persisted Queries
For production, use persisted queries instead of sending raw GraphQL:
- Queries are stored in AEM and given a path (e.g.,
/my-project/articles) - CDN caches responses at the edge
- Clients call the path, not the full query
- Better security—no arbitrary queries from clients
// Instead of:
fetch('/content/cq:graphql/mysite/endpoint.json', {
method: 'POST',
body: JSON.stringify({ query: '...' })
})
// Use:
fetch('/graphql/execute.json/mysite/articles;slug=getting-started')
Frontend Integration
React / Next.js
Adobe provides the AEM Headless Client library for JavaScript frameworks:
import { AEMHeadless } from '@adobe/aem-headless-client-js';
const aemHeadless = new AEMHeadless({
serviceURL: 'https://publish-xxx-yyy.adobeaemcloud.com',
endpoint: '/content/cq:graphql/mysite/endpoint.json'
});
async function getArticle(slug) {
const response = await aemHeadless.runPersistedQuery(
'mysite/article-by-slug',
{ slug }
);
return response.data.articleList.items[0];
}
// In your component
function Article({ slug }) {
const [article, setArticle] = useState(null);
useEffect(() => {
getArticle(slug).then(setArticle);
}, [slug]);
if (!article) return <Loading />;
return (
<article>
<h1>{article.title}</h1>
<div dangerouslySetInnerHTML={{ __html: article.body.html }} />
</article>
);
}
Handling Rich Text
Multi-line text fields can return content as:
- plaintext — Raw text without formatting
- html — Rendered HTML
- markdown — Markdown format
- json — Structured JSON (for custom rendering)
For React, you'll typically use html with dangerouslySetInnerHTML
or json for more control.
Use Cases for AEM Headless
- Single Page Applications (SPAs) — React/Vue apps that need CMS content
- Static Site Generation — Next.js, Gatsby pulling content at build time
- Mobile Apps — iOS/Android apps consuming content API
- Multi-channel Publishing — Same content across web, app, kiosk
- Hybrid Architecture — Some pages traditional AEM, some headless
When NOT to Use Headless
Headless isn't always the answer:
- Simple marketing sites — Traditional AEM Sites may be faster to build
- Heavy personalization — AEM's targeting works best with server-rendered pages
- In-context editing — Authors lose visual editing in pure headless
- Existing AEM Sites investment — Don't rebuild if traditional approach works
Many organizations use a hybrid approach: traditional AEM Sites for marketing pages (with in-context editing) and headless Content Fragments for structured content like product data, FAQs, or article content that feeds multiple channels.
Getting Started
- Enable GraphQL — Ensure GraphQL endpoint is configured
- Create a Content Fragment Model — Start simple (title, body, image)
- Create Content Fragments — Add sample content
- Test in GraphiQL — Query your content, explore the schema
- Build Frontend — Start with a simple React component
- Create Persisted Queries — Move queries to AEM for production
Related Topics
Need Help with AEM Headless?
Our team at WegWorks (Weg Works) has built headless AEM solutions for enterprise clients. Let us help architect your decoupled content strategy.
View Our AEM Services