Content Fragments & GraphQL
Content Fragments are channel-neutral, structured content - text, images, data - stored independently of any page layout. Combined with GraphQL, they enable headless content delivery to any frontend: websites, mobile apps, digital signage, or anything that speaks HTTP.
Content Fragments vs pages
| Feature | Pages | Content Fragments |
|---|---|---|
| Layout | Tied to AEM templates | No layout - pure data |
| Delivery | AEM renders HTML | Client renders (any format) |
| Reuse | One page = one URL | One fragment = many consumers |
| Editing | Page editor with components | Content Fragment editor |
| API | Sling model export (.model.json) | GraphQL API |
Content Fragment Models
A Content Fragment Model defines the structure of a fragment - like a database schema. Models are created in the configuration:
Create a model
- Go to Tools > General > Content Fragment Models
- Select your configuration folder (My Site)
- Click Create
- Name: Article
- Click Open to add fields
Common field types
| Field type | Description | Example |
|---|---|---|
| Single line text | Short text | Title, author name |
| Multi line text | Long text with formatting | Body, description |
| Number | Integer or float | Rating, price |
| Boolean | True/false | Featured, published |
| Date and Time | Date/time picker | Publish date |
| Enumeration | Predefined options | Category, status |
| Tags | AEM tags | Topic tags |
| Content Reference | Link to other content | Related page |
| Fragment Reference | Link to another CF | Author fragment |
| JSON Object | Arbitrary JSON | Metadata |
| Tab Placeholder | Visual tab separator | Organize fields |
Article model example
Create these fields for an Article model:
| Field | Type | Configuration |
|---|---|---|
| Title | Single line text | Required |
| Slug | Single line text | Required, unique |
| Body | Multi line text | Rich text, required |
| Excerpt | Multi line text | Plain text, max 300 chars |
| Featured Image | Content Reference | Asset reference |
| Publish Date | Date and Time | Date only |
| Featured | Boolean | Default: false |
| Author | Fragment Reference | References Author model |
| Category | Enumeration | tech, business, design |
| Tags | Tags | - |
Author model example
Create a separate Author model:
| Field | Type |
|---|---|
| Name | Single line text (required) |
| Bio | Multi line text |
| Avatar | Content Reference |
| Single line text |
Creating Content Fragments
Via the Assets console
- Go to Assets > Files
- Navigate to your content folder (e.g.,
/content/dam/mysite/articles) - Click Create > Content Fragment
- Select the Article model
- Enter a name and fill in the fields
- Click Create
Fragment editor
The Content Fragment editor shows your model's fields in a structured form:
- Single-line fields appear as text inputs
- Multi-line fields show a rich text editor or plain text area
- Fragment references show a picker for other fragments
- Tab placeholders organize fields into tabs
Variations
Content Fragments support variations - alternate versions of the same content:
- Master - the default version
- Named variations - e.g., "Summary", "Mobile", "Newsletter"
Each variation can override specific fields while inheriting others from the master.
The GraphQL API
AEM automatically generates a GraphQL API from your Content Fragment Models. No code needed - install the model, and the API is ready.
GraphQL endpoint
GraphQL endpoints are configuration-specific (not always a single global endpoint). A common local SDK pattern is:
http://localhost:4502/content/_cq_graphql/<configuration>/endpoint.json
Replace <configuration> with your site configuration name (for example mysite). Endpoint paths can differ by setup
and AEM version, so verify in your GraphQL/endpoint UI before integrating clients.
Use GraphiQL from the AEM GraphQL tooling UI for development and schema exploration.
Field name mapping
GraphQL field names are derived from the Content Fragment Model field names. AEM converts them to camelCase -
for example, a model field labeled "Publish Date" becomes publishDate in GraphQL. Field names are case-sensitive
in queries. Use the GraphiQL IDE's schema explorer to verify exact field names if queries return null unexpectedly.
Basic queries
List all articles:
{
articleList {
items {
_path
title
slug
excerpt
publishDate
featured
category
}
}
}
Get a specific article by path:
{
articleByPath(_path: "/content/dam/mysite/articles/getting-started") {
item {
title
slug
body {
html
plaintext
}
publishDate
author {
name
bio
}
}
}
}
Filtering
{
articleList(
filter: {
category: { _expressions: [{ value: "tech", _operator: EQUALS }] }
featured: { _expressions: [{ value: true, _operator: EQUALS }] }
}
) {
items {
title
excerpt
publishDate
}
}
}
Sorting and pagination
{
articleList(
sort: "publishDate DESC"
limit: 10
offset: 0
) {
items {
title
publishDate
}
}
}
Rich text fields
Multi-line text fields with rich text can return multiple formats (available fields can vary by setup/version):
{
articleByPath(_path: "/content/dam/mysite/articles/example") {
item {
body {
html # Rendered HTML
plaintext # Plain text (stripped)
json # Structured JSON
}
}
}
}
Fragment references
When an Article references an Author:
{
articleList {
items {
title
author {
name
bio
avatar {
... on ImageRef {
_path
width
height
}
}
}
}
}
}
Persisted queries
Persisted queries are pre-defined, cached, server-side queries. They are recommended for production:
Create a persisted query
Persisted queries should be created and managed through AEM GraphQL tooling/workflow (and promoted as code/config), then executed via the persisted query endpoint.
Execute a persisted query
# On Author (local SDK) -- authentication required
curl -u admin:admin http://localhost:4502/graphql/execute.json/mysite/article-list
# On Publish -- no authentication needed for public persisted queries
curl http://localhost:4503/graphql/execute.json/mysite/article-list
Benefits of persisted queries
| Feature | Ad-hoc queries | Persisted queries |
|---|---|---|
| Caching | Not cached by Dispatcher | Cached (GET request) |
| Security | Client defines the query | Server controls the query |
| Performance | Parsed on every request | Parsed once, executed many |
| CDN | Cannot cache POST | CDN-cacheable GET |
Best practice: Always use persisted queries in production. Ad-hoc queries are for development and the GraphiQL IDE only.
Production hardening checklist (headless)
| Area | Recommendation |
|---|---|
| Persisted query naming | Use stable, versioned names (e.g., article-list-v1) |
| Caching | Prefer GET persisted queries behind Dispatcher/CDN |
| Query complexity | Keep response shape minimal; avoid unbounded list fields |
| Authorization | Expose only intended endpoints/queries at Publish |
| CORS | Allow only trusted frontend origins (configure via the com.adobe.granite.cors.impl.CORSPolicyImpl OSGi config) |
| Schema evolution | Add fields in backward-compatible ways; deprecate before removal |
| Monitoring | Track query latency, cache hit ratio, and error rates |
Headless content delivery
The typical headless flow with AEM:
Frontends consume the GraphQL API:
// Fetch from a React/Next.js frontend
const response = await fetch(
"https://publish.mysite.com/graphql/execute.json/mysite/article-list"
);
const data = await response.json();
const articles = data.data.articleList.items;
Content Fragments on pages
Content Fragments can also be rendered on AEM pages using the Content Fragment Core Component:
- Add a Content Fragment component to a page
- Select the fragment to display
- Choose which fields to render
- Optionally select a variation
This bridges traditional page-based and headless approaches.
For advanced patterns, see the Content Fragments and Headless GraphQL references.
Summary
You learned:
- Content Fragments are channel-neutral structured content
- Content Fragment Models define the schema (field types, validation)
- How to create and edit fragments in the Assets console
- The GraphQL API auto-generated from your models
- Querying - filtering, sorting, pagination, rich text formats, fragment references
- Persisted queries for production performance and caching
- The headless delivery flow from author to frontend
- Using Content Fragments on pages with the Content Fragment component
Next up: Multi-Site Manager & i18n - Blueprints, Live Copies, rollout configs, language copies, and the translation framework.