Help us improve
Share bugs, ideas, or general feedback.
From contentful
Build custom Contentful App Framework apps — UI extensions that live inside the Contentful web app, surfaced at field, sidebar, entry-editor, page, dialog, home, or config locations. Covers app definitions, App SDK patterns, hosting choices, parameter design, distribution, and the canonical Slalom-flavored app patterns (custom field editors, entry-side workflow tools, taxonomy pickers, AI-assisted authoring). Use this skill when the stock editor is missing a control authors keep asking for, when integrating with a third-party system needs editor-side surface area, when the team is evaluating "build vs. install from marketplace," or when an existing app is being extended. Trigger any time the question is "can we customize the editor experience" or "let's wrap an external service inside Contentful."
npx claudepluginhub bpainter/composable-dxp-claude-marketplace --plugin contentfulHow this skill is triggered — by the user, by Claude, or both
Slash command
/contentful:contentful-app-frameworkThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
The App Framework lets you embed custom React UIs inside Contentful's web app. It's the right answer when the stock authoring experience is missing something a Slalom client keeps asking for — a smarter slug picker, an in-context preview, an AI-assist drafting widget, a taxonomy selector that talks to an external classifier, an editorial dashboard.
Guides technical evaluation of code review feedback: read fully, restate for understanding, verify against codebase, respond with reasoning or pushback before implementing.
Share bugs, ideas, or general feedback.
The App Framework lets you embed custom React UIs inside Contentful's web app. It's the right answer when the stock authoring experience is missing something a Slalom client keeps asking for — a smarter slug picker, an in-context preview, an AI-assist drafting widget, a taxonomy selector that talks to an external classifier, an editorial dashboard.
This skill owns app design and lifecycle. Pair with contentful-content-model (custom field editors imply field decisions), contentful-mcp-cli (when AI-assist apps are in scope), contentful-personalization (the Personalization UI is itself an app on the marketplace), and contentful-react-wrapper (apps often consume the same content the front end does).
Contentful's marketplace (https://www.contentful.com/marketplace/apps/) ships pre-built apps for common needs. Default to install if a marketplace app fits. Common categories:
Build only when:
See ../../references/marketplace-apps.md for the curated list with Slalom annotations and default-install sets per engagement complexity.
An app definition (org-scoped) declares which locations the app can render in, which parameters it accepts, and where its UI is hosted. A space owner installs the app definition into a space and configures the per-installation parameters; from then on, the app's React UI renders in the configured locations using the App SDK, which exposes a typed API to talk to Contentful (CMA-scoped to the current user's permissions, plus the editor context).
Pick the right surface:
| Location | What it is | Use for |
|---|---|---|
Entry field (entry-field) | Replaces or augments a field's editor | Custom slug pickers, AI-assist text fields, smart selectors, color pickers, validators |
Entry editor (entry-editor) | Replaces the whole entry editing page | Full custom workflows for a content type — rare |
Entry sidebar (entry-sidebar) | A widget in the entry sidebar | Workflow status, related-content suggestions, publish-time helpers |
Page (page) | A standalone page in the Contentful web app | Dashboards, bulk operations, reports |
Dialog (dialog) | A modal triggered by another location | Asset/entry pickers, confirmation flows |
Home (home) | Tile on the space home | Onboarding, KPIs, links into custom apps |
App config (app-config) | The configuration screen for the app itself | Where install-time parameters get set |
You can register one app for multiple locations. A "translation status" app might surface as entry-sidebar (per-entry status) and page (org-wide dashboard).
The App SDK (@contentful/app-sdk) is what your React app talks to. Key surfaces:
import { init, locations } from "@contentful/app-sdk";
init((sdk) => {
if (sdk.location.is(locations.LOCATION_ENTRY_FIELD)) {
// sdk.field — get/set value, listen to changes
// sdk.entry — sibling fields on the same entry
// sdk.contentType — schema info
// sdk.cma — CMA client scoped to current user
// sdk.parameters — install + invocation parameters
// sdk.window — resize the iframe
renderFieldApp(sdk);
}
if (sdk.location.is(locations.LOCATION_ENTRY_SIDEBAR)) {
renderSidebarApp(sdk);
}
// ...etc
});
Common patterns:
// Read the current field value
const value = sdk.field.getValue();
// Subscribe to external changes (collaborator typing, autosave)
const detach = sdk.field.onValueChanged((v) => setStateFromCMS(v));
// Write back
await sdk.field.setValue(newValue);
// Resize the iframe so the editor doesn't show scrollbars
sdk.window.startAutoResizer();
// Open a dialog (another location of the same app)
const result = await sdk.dialogs.openCurrentApp({
position: "center",
width: 800,
parameters: { mode: "picker" },
});
// Use CMA scoped to the user
const entries = await sdk.cma.entry.getMany({ query: { content_type: "article" } });
The cma is scoped to the current user's permissions — if the user can't read drafts, neither can your app. Don't try to elevate; that's not what App Framework is for.
For React, the @contentful/react-apps-toolkit gives you useSDK(), useCMA(), useFieldValue() hooks plus a <SDKProvider> that wires init for you.
Build apps with Forma 36 (@contentful/f36-components). Reasons:
import { Button, FormControl, TextInput } from "@contentful/f36-components";
The Forma 36 components are theme-aware; light/dark switches with the editor.
Two types of parameters:
Design installation parameters as if they were environment variables; instance parameters as if they were per-component props.
// app definition (CMA call when registering the app)
{
name: "Smart Slug",
src: "https://your-app-host.example.com",
locations: [
{ location: "entry-field", fieldTypes: [{ type: "Symbol" }] },
{ location: "app-config" },
],
parameters: {
installation: [
{ id: "modelEndpoint", type: "Symbol", required: true, name: "AI model endpoint" },
{ id: "apiKey", type: "Symbol", required: true, name: "API key" },
],
instance: [
{ id: "sourceField", type: "Symbol", required: true, name: "Source field id" },
{ id: "maxLength", type: "Number", default: 60, name: "Max slug length" },
],
},
}
Three options:
app-host.contentful.com. Default for first-party and marketplace apps. Lowest ops burden.src at any HTTPS URL. Lets you ship a Next.js app, host on Vercel, deploy via the same pipeline as the front end. Best when the app needs server-side endpoints (calling external APIs that can't be called from the browser).npm run create-contentful-app start runs at http://localhost:3000; the app definition's src points there during development.For Slalom delivery, hosted is the default for simple apps; self-hosted on Vercel for anything with server-side complexity.
# 1. Scaffold
pnpm create contentful-app
# Choose template (React + TS + Forma 36 is default)
# 2. Develop locally
cd my-app && pnpm install && pnpm start
# Update the app definition's src to your localhost URL while developing
# 3. Build
pnpm build
# 4. Upload (hosted) or deploy (self-hosted)
pnpm upload-ci # bundles dist/ and uploads to Contentful
# 5. Install in a space
contentful app install --app-definition <APP_DEFINITION_ID>
The CLI handles app definition creation and updates.
entry-field on Symbol types.sdk.entry.fields[id].getValue().sdk.field.setValue().entry-sidebar.sdk.ids.entry, sdk.ids.contentType, sdk.contentType.sys.id.draftMode() to render.entry-field on Symbol or Array<Symbol>.<Autocomplete> or <MultipleSelect>.page.sdk.cma to query entries by tag, contentType, or status.<Table> of entries needing review, with quick-actions to open them.The Personalization UI is itself an App Framework app, packaged by Contentful (acquired from Ninetailed). See contentful-personalization for the runtime side.
postMessage between the app and the editor. The CMA token is brokered by the SDK; you never handle it directly.src.Contentful keeps a single "current" bundle per app definition. Updates are atomic. Implications:
pnpm upload-ci; rolling forward is fast.startAutoResizer. App content gets clipped or shows scrollbars inside the editor iframe.app-config screen. The space owner has to set parameters somewhere; without a config location, they're in the JSON definition only.setValue calls. Lots of versioned writes; collaborators get jumpy. Debounce.# App: [Name]
## Problem
{what authoring pain it solves}
## Why build vs. install
{specific marketplace alternative considered, why it doesn't fit}
## Locations
- {location}: {what renders here}
## Parameters
- Installation: {list with types}
- Instance: {list with types}
## Data flow
{external services, API calls, where keys live}
## UX walkthrough
1. Author opens entry → ...
2. ...
## Hosting
- Hosted | Self-hosted (Vercel)
- Reasoning: {one line}
## Distribution
- Spaces: {list}
- Roll-forward plan: {if multi-space}
## Risks
{permissions, key handling, version drift}
contentful-content-model.contentful-mcp-cli.contentful-personalization.contentful-localization.contentful-react-wrapper.../../references/marketplace-apps.md