> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://contentful.com/developers/docs/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://contentful.com/_mcp/server.
# Work with apps in Release context
> Learn how to make your apps compatible with Contentful Timelines and implement opt-out mechanisms for release-scoped entries.
This guide explains how to ensure your apps work correctly when entries are being edited within a release context, and how to implement opt-out mechanisms when your app is not compatible with releases.
## Overview
Contentful's **Timeline** feature allows content editors to manage, preview, and schedule changes to a batch of entries. When an entry is being edited within a release context, some apps may encounter compatibility issues or need to adjust their behavior.
This guide provides practical solutions for handling release contexts in your apps, including opt-out mechanisms for apps that cannot function properly within releases.
> **Info**
>
> **NOTE:** Apps in entry-related locations (Entry Field, Entry Sidebar, Entry Editor) are always affected by releases. Dialog apps inherit the release context when opened from entry-related locations but are unaffected when opened from other locations. Apps in Page, Home, and App Configuration locations are never affected by release contexts.
## Understanding Release Context
When a content editor works with entries in a release, they're editing a release version of the entry that exists separately from the main entry. This has several implications for apps:
* All entry data and references (including `sdk.entry`) are scoped to the release context, not the base entries
* Some SDK methods like `publish()` and `unpublish()` are not available because the release itself manages the publishing state of all included entries
* The `sdk.release` attribute contains information about the current release
## Detecting Release Context
The primary way to detect whether your app is running in a release context is by checking the `sdk.release` attribute:
```javascript
import { init } from '@contentful/app-sdk';
init((sdk) => {
if (sdk.release) {
// App is running in release context
console.log('Release title:', sdk.release.title);
} else {
// App is running in normal (base entry) context
console.log('App is running in base entry context');
}
});
```
### Release Object Structure
When in release context, `sdk.release` contains the following information:
```javascript
{
"sys": {
"type": "Release",
"id": "release-id",
"schemaVersion": "Release.v2",
"version": 1,
"space": {
"sys": { "type": "Link", "linkType": "Space", "id": "space-id" }
},
"environment": {
"sys": { "type": "Link", "linkType": "Environment", "id": "master" }
},
"createdAt": "2024-11-29T12:00:00.000Z",
"updatedAt": "2024-11-29T12:00:00.000Z",
"createdBy": { "sys": { "type": "Link", "linkType": "User", "id": "user-id" } },
"updatedBy": { "sys": { "type": "Link", "linkType": "User", "id": "user-id" } }
},
"title": "Black Friday Campaign",
"description": "Campaign for Black Friday promotion",
"entities": {
"sys": { "type": "Array" },
"items": [
{
"entity": {
"sys": {
"type": "Link",
"linkType": "Entry", // or "Asset"
"id": "entry-id"
}
},
"action": "publish", // or "unpublish"
}
]
}
}
```
When not in release context, `sdk.release` will be `undefined`.
## Opt-out patterns
There are several approaches you can take when your app encounters a release context:
### Complete opt-out
The simplest approach is to render nothing when in release context:
```jsx
import React from 'react';
import { useSDK } from '@contentful/react-apps-toolkit';
const Sidebar = () => {
const sdk = useSDK();
// Complete opt-out: render nothing in release context
if (sdk.release) {
return null;
}
// Normal app functionality for base entries
return (
My App
This app works with base entries only.
);
};
export default Sidebar;
```
### Partial opt-out with fallback UI
Show a user-friendly message explaining why the app is not available:
```jsx
import React from 'react';
import { Paragraph, Note } from '@contentful/f36-components';
import { useSDK } from '@contentful/react-apps-toolkit';
const FieldEditor = () => {
const sdk = useSDK();
// Partial opt-out: show explanatory message
if (sdk.release) {
return (
This app is not compatible with release entries. Please edit this
field in the base entry or contact your administrator.
);
}
// Normal field editor functionality
return
{/* Your field editor implementation */}
;
};
export default FieldEditor;
```
### Conditional feature disabling
Disable specific features while keeping the app partially functional:
```jsx
import React, { useState } from 'react';
import { Button, Stack, Paragraph } from '@contentful/f36-components';
import { useSDK } from '@contentful/react-apps-toolkit';
const EntryEditor = () => {
const sdk = useSDK();
const [isPublishing, setIsPublishing] = useState(false);
const isInReleaseContext = !!sdk.release;
const handlePublish = async () => {
if (isInReleaseContext) {
sdk.notifier.error('Publishing is not available in release context');
return;
}
setIsPublishing(true);
try {
await sdk.entry.publish();
sdk.notifier.success('Entry published successfully');
} catch (error) {
sdk.notifier.error('Failed to publish entry');
} finally {
setIsPublishing(false);
}
};
return (
{isInReleaseContext && (
You are editing this entry in release context: "{sdk.release.title}"
)}
{/* Other app functionality that works in both contexts */}
);
};
export default EntryEditor;
```
## Affected App Locations
Apps in the following locations are affected by release contexts:
| Location | Constant | Impact |
| ------------- | ------------------------ | --------------------------------------------------------------------------- |
| Entry Field | `LOCATION_ENTRY_FIELD` | Field values are release-scoped |
| Entry Sidebar | `LOCATION_ENTRY_SIDEBAR` | Entry data is release-scoped |
| Entry Editor | `LOCATION_ENTRY_EDITOR` | Entire entry is release-scoped |
| Dialog | `LOCATION_DIALOG` | **Inherits release context** when opened from entry-related locations above |
Apps in these locations are **not affected** by releases:
* Page (`LOCATION_PAGE`)
* Home (`LOCATION_HOME`)
* App Configuration (`LOCATION_APP_CONFIG`)
### Dialog Context Inheritance
When a dialog is opened from a release-aware location (Entry Field, Entry Sidebar, or Entry Editor), it **inherits** the parent's release context. This means:
* `sdk.release` will be defined in the dialog app
* Entity selector dialogs (`selectSingleEntry`, `selectMultipleEntries`, etc.) will show release-scoped entities
* The dialog app should implement the same release-aware patterns as other entry-related apps
However, if the same dialog is opened from a non-release location (Page, Home, App Configuration), it will not have release context.
## Unsupported Features in Release Context
The following SDK methods and features are not available when working with release-scoped entries:
### Entry methods
* `sdk.entry.publish()` - Throws error: "Publish entry method is not supported in release context"
* `sdk.entry.unpublish()` - Throws error: "Unpublish entry method is not supported in release context"
## Code examples
### Entry field app with release awareness
```jsx
import React, { useState, useEffect } from 'react';
import { TextInput, FormControl, HelpText } from '@contentful/f36-components';
import { useSDK } from '@contentful/react-apps-toolkit';
const CustomFieldEditor = () => {
const sdk = useSDK();
const [value, setValue] = useState(sdk.field.getValue() || '');
// Handle release context
if (sdk.release) {
return (
Custom Field (Release Mode) {
setValue(e.target.value);
sdk.field.setValue(e.target.value);
}}
/>
Editing in release: {sdk.release.title}
);
}
// Normal field editor
return (
Custom Field {
setValue(e.target.value);
sdk.field.setValue(e.target.value);
}}
/>
);
};
export default CustomFieldEditor;
```
### Entry sidebar app with conditional features
```jsx
import React from 'react';
import { Card, Paragraph, Button, Stack } from '@contentful/f36-components';
import { useSDK } from '@contentful/react-apps-toolkit';
const EntrySidebar = () => {
const sdk = useSDK();
const handleExternalSync = () => {
if (sdk.release) {
sdk.notifier.warning(
'External sync is not available for release entries'
);
return;
}
// Perform external sync
};
return (
{sdk.release ? `Release: ${sdk.release.title}` : 'Base Entry'}
{sdk.release && (
Some features are disabled in release context
)}
);
};
export default EntrySidebar;
```
> Learn how to make your apps compatible with Contentful Timelines and implement opt-out mechanisms for release-scoped entries.