The setData method lets your Contentstack marketplace app programmatically update field values in the entry editor. Use it to transform data, sync with external services, or create interactive field editors.
When to use setData:
Real-World example: A “Generate SEO Title” button that takes the entry title, optimizes it for SEO, and updates the SEO title field automatically.
At the end of this guide, you can:
Note: When calling setData on a group field or modular block, the entire nested structure is replaced. Pass all sub-fields to avoid unintentional data loss.
Copy this code to get started immediately:
import ContentstackAppSdk from '@contentstack/app-sdk';
ContentstackAppSdk.init().then(async (sdk) => {
const entry = sdk.location.CustomField.entry;
const field = entry.getField("seo"); // assuming this is a global field
const seo = field.getData();
seo.title = "new content";
try {
await field.setData(seo);
} catch (error) {
throw new Error(error.message);
}
});This example works in Custom Field locations. Replace "seo" with your global field UID if different. If the update fails or the error is unclear, refer to the Troubleshooting section.
Step-by-Step Usage
This section guides you through installation, initialization, field access, updates, and error handling for using setData to update entry fields (e.g., SEO Title) from a custom extension or widget.
The example assumes SEO is a global field that has SEO Title, SEO Description, and SEO Meta, and we want to update only the SEO Title.
npm install @contentstack/app-sdk
import ContentstackAppSdk from '@contentstack/app-sdk';
ContentstackAppSdk.init().then((sdk) => {
if (!sdk.location) {
throw new Error("SDK location unavailable");
}
});const customEntry = sdk.location.CustomField.entry;
const customField = customEntry.getField("seo");
const seo = customField.getData(); seo.title = "new content";
const sidebarEntry = sdk.location.SidebarWidget.entry;
const sidebarField = sidebarEntry.getField("seo");
const seo = sidebarField.getData(); seo.title = "new content"// Option 1 (Custom Field): update via entry.setData using an entry-shaped payload
await customEntry.setData({ ...customEntry.getData(), seo });
// Option 2 (Sidebar): update the seo field directly
await sidebarField.setData(seo);try {
await field.setData("New value");
} catch (error) {
throw new Error(error.message);
}If the error persists after you fix your code and data shape, see Troubleshooting.
The following table outlines the supported UI locations for setData, including field access patterns and common use cases.
Additional Resources:
| UI Location | setData Available | Field access pattern | Use cases |
|---|---|---|---|
| Custom Field | Yes | CustomField.field or getField | Field-specific tools, validators |
| Field Modifier | Yes | FieldModifierLocation.field.setData("new content") for hovered field; FieldModifierLocation.entry.getField(uid).setData(value) for other fields | Field enhancements |
| Entry Sidebar | Yes | getField(uid) | Bulk operations, templates |
| Asset Sidebar | No | Not supported | Not applicable |
Note: The setData method behavior also depends on field data_type and the SDK version. These affect the value shape accepted by setData and supported locations.
The following samples assume that your app is running in a supported location, as described in the Supported UI Locations for the SetData Method section.
The following example loads the SDK at a Custom Field location, retrieves a field by UID, and updates its text using setData.
import ContentstackAppSdk from '@contentstack/app-sdk';
ContentstackAppSdk.init().then(async (sdk) => {
const entry = sdk.location.CustomField.entry;
const field = entry.getField("title");
await field.setData("Updated text content");
});The following example shows a React form that initializes the SDK, writes user input to a specific field, and provides success or error feedback within the UI.
import React, { useState } from 'react';
import ContentstackAppSdk from '@contentstack/app-sdk';
export function FieldUpdater() {
const [sdk, setSdk] = useState(null);
const [value, setValue] = useState('');
const [loading, setLoading] = useState(false);
const [message, setMessage] = useState('');
React.useEffect(() => {
ContentstackAppSdk.init().then(setSdk);
}, []);
const handleUpdate = async () => {
if (!sdk?.location?.CustomField?.entry) return;
const entry = sdk.location.CustomField.entry;
const field = entry.getField("title");
setLoading(true);
setMessage('');
try {
await field.setData(value);
setMessage('Field updated successfully.');
} catch (error) {
setMessage(`Error: ${error.message}`);
} finally {
setLoading(false);
}
};
return (
<div style={{ padding: '16px' }}>
<input
type="text"
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder="Enter new value"
disabled={loading}
/>
<button
onClick={handleUpdate}
disabled={loading || !value.trim()}
style={{ marginLeft: '8px' }}
>
{loading ? 'Updating...' : 'Update Field'}
</button>
{message && <div style={{ marginTop: '8px' }}>{message}</div>}
</div>
);
}The following example updates several top-level entry fields from the sidebar in one call using entry.setData, merging the current entry payload with an updates object.
const updateBlogPost = async (sdk) => {
const entry = sdk.location.SidebarWidget.entry;
const updates = {
title: "My Updated Blog Post",
status: "published",
featured: true,
publish_date: new Date().toISOString(),
tags: ["blog", "featured", "new"],
};
try {
const entryData = {...entry.getData(), ...updates}
await entry.setData(entryData);
} catch (error) {
throw new Error(error.message);
}
};Note: The { ...entry.getData(), ...updates } is a shallow merge. If you need to change nested objects (e.g., a group or global field), merge those nested shapes explicitly instead of relying on spread alone.
The following examples demonstrate typical setData payloads for common field types within an entry:
const entry = sdk.location.CustomField.entry;
await entry.getField("title").setData("Sample text");
await entry.getField("price").setData(42);
await entry.getField("featured").setData(true);
// Date fields typically expect an ISO-8601 string.
await entry.getField("publish_date").setData("2026–04–13T10:30:00.000Z");
await entry.getField("image").setData("blt1234567890abcdef");
await entry.getField("gallery").setData(["blt1111111111111111", "blt2222222222222222"]);
// Reference fields typically require objects with a UID and content type.
await entry.getField("related_posts").setData([
{
uid: "blt1234567890abcdef",
_content_type_uid: "blog_post"
}
]);
await entry.getField("author_info").setData({
name: "John Doe",
email: "john@example.com",
age: 30
});
await entry.getField("team_members").setData([
{ name: "John", email: "john@example.com" },
{ name: "Jane", email: "jane@example.com" }
]);The following example applies preset field bundles from the sidebar: choose a template and set multiple fields with repeated setData.
import React, { useState } from 'react';
const templates = {
blog_draft: {
title: "Draft: New Blog Post",
status: "draft",
featured: false,
tags: ["draft"]
},
product_featured: {
featured: true,
priority: "high",
promotion_active: true
}
};
export function TemplateApplier({ sdk }) {
const [applying, setApplying] = useState(false);
const applyTemplate = async (templateKey) => {
setApplying(true);
try {
const entry = sdk.location.SidebarWidget.entry;
const data = templates[templateKey];
await Promise.all(
Object.entries(data).map(([uid, value]) =>
entry.getField(uid).setData(value)
)
);
alert('Template applied successfully!');
} catch (error) {
alert('Failed to apply template: ' + error.message);
} finally {
setApplying(false);
}
};
return (
<div>
<h3>Apply Template</h3>
{Object.keys(templates).map(key => (
<button
key={key}
onClick={() => applyTemplate(key)}
disabled={applying}
style={{ display: 'block', margin: '8px 0' }}
>
{key.replace('_', ' ')}
</button>
))}
</div>
);
}For specific error messages or symptoms, use Troubleshooting first. This section focuses on wrong vs right API and data patterns.
Mistake: Treating sdk.location.CustomField.field as the only way to obtain a Field. It updates the mounted field only; other UIDs need entry.getField(...).
const field = sdk.location.CustomField.field;
await field.setData("value");Solution: Use the field reference appropriate for the update as follows:
const field = sdk.location.CustomField.field;
await field.setData("value");
const entry = sdk.location.CustomField.entry;
await entry.getField("seo_title").setData("value");
await Promise.all([
entry.getField("seo_title").setData("one"),
entry.getField("summary").setData("two"),
]);Mistake: Sending the wrong data type for the field.
await numberField.setData("123");
await referenceField.setData("blt123");Solution: Use correct data types.
await numberField.setData(123);
await referenceField.setData([
{
uid: "blt1234567890abcdef",
_content_type_uid: "blog_post"
}
]);Mistake: Calling setData without handling promise rejection. Validation failures, invalid payloads, and other editor-side failures reject the promise—it does not fail silently.
await field.setData(value);Solution: Use try/catch. Treat VALIDATION_ERROR separately when you want field-specific messaging. Handle everything else as a general failure.
try {
await field.setData(value);
} catch (error) {
if (error.code === 'VALIDATION_ERROR') {
throw new Error(`Validation failed: ${error.message}`);
}
throw new Error(error.message);
}Mistake: Using an outdated @contentstack/app-sdk release.
await field.setData(fileData);Solution: Update to the latest SDK.
npm install @contentstack/app-sdk@latest
These practices complement Common Mistakes and Pitfalls (what to avoid) and Troubleshooting (symptom-first fixes).
When updating several unrelated fields, prefer concurrent setData calls with Promise.all instead of awaiting each update one after another:
const entry = sdk.location.CustomField.entry;
// Use Promise.all when field updates are independent.
await Promise.all([
entry.getField("seo_title").setData(value1),
entry.getField("summary").setData(value2),
entry.getField("featured").setData(value3),
]);Surface loading, success, and error states in your UI so authors know whether an update is in progress or failed:
const [status, setStatus] = useState('idle');
const handleUpdate = async () => {
setStatus('loading');
try {
await field.setData(value);
setStatus('success');
setTimeout(() => setStatus('idle'), 2000);
} catch (error) {
setStatus('error');
}
};
{status === 'loading' && <div>Updating...</div>}
{status === 'success' && <div>Updated successfully.</div>}
{status === 'error' && <div>Update failed.</div>}Ship against an SDK and editor combination where every API you call is actually supported end-to-end:
You should meet the Prerequisites SDK version before shipping; at runtime, you can guard calls when older builds might lack setData:
const entry = sdk.location.CustomField.entry;
const field = entry.getField("seo_title");
const hasSetData = typeof field.setData === 'function';
if (!hasSetData) {
throw new Error("setData is not available in this SDK version");
}Model your content shape with interfaces and type the fields you pass to getField / setData so refactors and batch updates stay consistent:
interface BlogPost {
title: string;
content: string;
featured: boolean;
tags: string[];
author: Array<{
uid: string;
_content_type_uid: string;
}>;
}
const updateBlogPost = async (
entry: { getField: (uid: string) => { setData: (v: unknown) => Promise<unknown> } },
updates: Partial<BlogPost>
) => {
await Promise.all(
(Object.keys(updates) as (keyof BlogPost)[]).map((uid) =>
entry.getField(uid as string).setData(updates[uid] as unknown)
)
);
};| API | When to use |
|---|---|
| sdk.location.CustomField.field | You update only the field that hosts your Custom Field extension. |
| sdk.location.FieldModifierLocation.field | Your app runs in Field Modifier, and you need the Field for the entry field being modified. |
| entry.getField(fieldUid) | You update another field on the entry (by UID or dotted path). Use with CustomField.entry, SidebarWidget.entry, or Field Modifier entry. |
Usage:
Call setData on your Field to write one field; changes apply to the draft until you save the entry.
Returns:
Note: To access the entire entry data, use entry.getData() or related APIs.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| data | any | Yes | none | Value for this field. Shape must match the field data_type and content type schema. |
Payload shape: Primitives or structured JSON are valid when they match the schema (e.g., strings, numbers, booleans, or custom-field payloads).
You must pass data on every call. Omitting it triggers an error; the SDK supplies no default. Refer to the runnable samples in Quick Start and Examples.
setData
getField
For user-visible validation failures and editor behavior, see Troubleshooting.
Note: Whether null, empty strings, or empty collections clear a field depends on the field type and your content type schema. Verify behavior in the entry editor.
setData updates one field per call. To update several fields, call entry.getField("<uid>") and setData on each handle, for example:
await Promise.all([fieldA.setData(...), fieldB.setData(...)]);Typical error: setData is not a function
Summary: Your code calls setData on something that is not a Field instance (e.g., undefined, the wrong location, or an object missing the method), often because the SDK path or UI location does not match where the app runs.
Resolution:
Verification: Fix the payload, call setData again, and confirm the promise resolves. If validation still fails, compare your payload to working examples in Examples and to the field definition in the stack.
Summary: setData succeeds or fails silently from the author’s perspective: the draft entry does not show the new value, or the wrong widget appears unchanged.
Resolution:
Verification: Identify the field in the entry editor, trigger your update, and refresh the visible field value. Use field.getData() after setData where appropriate to confirm the in-memory value matches what you set.
Summary: Updates feel slow, the UI stutters, or many sequential setData calls delay rendering when updating several fields or on every keystroke.
Resolution:
Verification: Measure before and after: fewer sequential awaits, fewer setData calls per second during typing, and smoother UI with the same end state after debounce settles.