Update Fields With the setData Method

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:

  • Transform user input before saving (e.g., format phone numbers or generate slugs).
  • Sync data from external APIs into Contentstack fields.
  • Build custom field editors with validation.
  • Create bulk update tools for content teams.

Real-World example: A “Generate SEO Title” button that takes the entry title, optimizes it for SEO, and updates the SEO title field automatically.

Prerequisites

  • A Contentstack stack and an app in Developer Hub (or equivalent) installed in at least one supported UI location.
  • Node.js and npm (or yarn/pnpm) for installing @contentstack/app-sdk.
  • @contentstack/app-sdk version 2.4.0 or higher. For installation instructions, refer to Step 1: Install the SDK.
  • Familiarity with the entry editor and the corresponding content type field UIDs.

What You'll Learn

At the end of this guide, you can:

  • Obtain a Field instance from sdk.location.entry.getField("field_uid") for your UI location.
  • Execute the setData method and manage the resulting promise to perform updates. For batch operations involving several fields, use entry.setData(), as field.setData() is intended for individual field modifications.

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.

Quick Start

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.

Step 1: Install the SDK

npm install @contentstack/app-sdk

Step 2: Initialize the SDK

import ContentstackAppSdk from '@contentstack/app-sdk';

ContentstackAppSdk.init().then((sdk) => {
    if (!sdk.location) {
        throw new Error("SDK location unavailable");
    }
});

Step 3: Get field or entry reference

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"

Step 4: Update the data

// 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);

Step 5: Handle results

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.

Supported UI Locations for the setData Method

The following table outlines the supported UI locations for setData, including field access patterns and common use cases.

Additional Resources

  • See the API Reference in this guide for details on value shapes, validation, parameters, and validation rules.
  • For SDK changes across versions, see the package on npm or the App SDK releases.
UI LocationsetData AvailableField access patternUse cases
Custom FieldYesCustomField.field or getFieldField-specific tools, validators
Field ModifierYesFieldModifierLocation.field.setData("new content") for hovered field; FieldModifierLocation.entry.getField(uid).setData(value) for other fieldsField enhancements
Entry SidebarYesgetField(uid)Bulk operations, templates
Asset SidebarNoNot supportedNot 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.

Examples

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.

Basic text field update

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");
});

React component with error handling

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>
    );
}

Bulk entry update

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.

Different field types

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: "[email protected]",
    age: 30
});
await entry.getField("team_members").setData([
    { name: "John", email: "[email protected]" },
    { name: "Jane", email: "[email protected]" }
]);

Content template tool

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>
    );
}

Common Mistakes and Pitfalls

For specific error messages or symptoms, use Troubleshooting first. This section focuses on wrong vs right API and data patterns.

1. Wrong API usage

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"),
]);

2. Incorrect data format

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"
    }
]);

3. Not handling setData errors

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);
}

4. Outdated SDK version

Mistake: Using an outdated @contentstack/app-sdk release.

await field.setData(fileData);

Solution: Update to the latest SDK.

npm install @contentstack/app-sdk@latest

Best Practices

These practices complement Common Mistakes and Pitfalls (what to avoid) and Troubleshooting (symptom-first fixes).

1. Use batch updates for multiple fields

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),
]);

2. Provide user feedback

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

3. SDK version and host support

Ship against an SDK and editor combination where every API you call is actually supported end-to-end:

  • Choose and pin the SDK: 
    • Use an @contentstack/app-sdk version that supports setData on Field for the UI locations you ship (Custom Field, Sidebar, Field Modifier, etc.). 
    • Lock the SDK version in package.json to ensure reproducible installs.
  • Use the correct update paths:
    • Use getField(...).setData(...). On supported builds, getField() returns a Field instance with setData.
    • Checks like typeof field.setData are rarely needed with compatible builds. Use them only when debugging unusual bundles or mixed iframe contexts.
    • Use entry.setData(...). This sends a setEntryData request to the host and requires app-extension-component 2.7.0 or later. If the editor is older, use per-field setData until the host is upgraded.
  • Validate in the real environment: After pinning the SDK, run smoke tests in the same stack and entry editor build used by your content teams. Compatibility depends not just on the SDK version, but also on the host (editor) implementation.

4. Check SDK compatibility

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");
}

5. Use TypeScript for type safety

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 Reference

Obtaining a field instance

APIWhen to use
sdk.location.CustomField.fieldYou update only the field that hosts your Custom Field extension.
sdk.location.FieldModifierLocation.fieldYour 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:

  • To update only the mounted field, use location.*.field.
  • To update another field by UID or path, use entry.getField("<uid or path>").

Call setData on your Field to write one field; changes apply to the draft until you save the entry.

Returns:

  • A Promise that resolves to the field instance you called setData on.
  • Does NOT return the full entry. It returns only the field instance.
  • If the update fails or is rejected by the host, the promise rejects it with an error.

Note To access the entire entry data, use entry.getData() or related APIs.

Parameters

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

Validation

setData

  • setData always returns a Promise. The SDK sends your payload to the host and rejects only when the host reports failure.
  • Rejections are usually caused by payload/schema mismatch, unsupported field paths, or host-level validation errors.
  • Do not treat file, reference, group, global_field, or blocks as universally unsupported.
  • Handle rejections with try/catch. If the parent frame fails, the SDK rejects with the underlying error.

getField

  • Passing an invalid UID or path throws an error before setData runs.
  • 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.

Updating multiple fields

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(...)]);

Troubleshooting

setData called on the wrong object

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:

  • For the mounted Custom Field, obtain the field with sdk.location.CustomField?.field or entry.getField("<uid>") for that field’s UID.
  • For other fields on the entry, use entry.getField("field_uid") on the correct entry for your UI location (such as CustomField.entry, SidebarWidget.entry, or the Field Modifier entry).
  • Confirm the following:
    • You are in a supported UI location.
    • You have defined the sdk.location.
    • You have installed the latest @contentstack/app-sdk.
  • Also, ensure the field and entry exist before invoking setData.

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.

Data not appearing in UI

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:

  • Confirm the field UID (and dotted path, if any) matches the field you intend to update.
  • Confirm the data shape is allowed for that field type and is not blocked by read-only or disabled UI state.
  • Updates apply to the draft until the author saves the entry.

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.

Performance issues

Summary: Updates feel slow, the UI stutters, or many sequential setData calls delay rendering when updating several fields or on every keystroke.

Resolution:

  • For multiple fields, run updates concurrently with Promise.all and per-field getField(...).setData(...) when order does not matter.
  • Debounce rapid setData calls tied to typing or sliders.
  • Add timeouts or cancellation for long-running async work so the iframe stays responsive.

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.

Next Steps