> 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.