Skip to main content

SDK API

Look up anything @contentstack/studio-react exports: how to initialize it, how to fetch a composition, the hooks and components that render one, and the functions that register your components, design tokens and state. For the props of <StudioComposition /> and <Slot>, see StudioComposition and Slot props.

The example on this page is a Blog post content type, UID blog_post, whose entries live under /blog. Its template has the Template UID blog_post, and its entry AI 101 has the URL /blog/ai-101. The names come from the team blog.

Which export to use​

You want toUse
Connect the SDK to your stackstudioSdk.init
Render a template on a client-rendered routeuseCompositionData with <StudioComponent />
Render a template on a server-rendered routesdk.fetchCompositionData with <StudioComponent />
Render sections on your Canvas URL route<StudioCanvas />
Render a composition against data you already holdsdk.fetchComposition with <StudioComposition />
Offer authors a way to create a template from a missing route in Visual Editor<VisualEditorCreateCompositionButton />
Make your components available in StudioregisterComponents, registerComponent, registerLazyComponent
Give Studio your design tokens, classes and breakpointsregisterDesignTokens, registerDesignClasses, registerBreakpoints
Render rich text and embedded entriesregisterRTERenderer, useStudioRichText
Share state between componentsuseStudioState, studioState, registerComponentStateVariable
Keep scripts out of the Studio canvasisStudioCanvas, isStudioEditorMode
Put a server-rendered composition's CSS and meta tags in the headgetSSRStyleTags, getCompositionMetadata

Initialize the SDK​

studioSdk.init(config: UserConfig): StudioSdk

Call it once, before you register anything or fetch a composition. It returns the SDK object whose methods are listed under Fetch a composition. Call it a second time and it logs Studio SDK already initialized and returns the same object.

import contentstack from "@contentstack/delivery-sdk";
import { studioSdk } from "@contentstack/studio-react";

const stack = contentstack.stack({
apiKey: process.env.CONTENTSTACK_API_KEY,
deliveryToken: process.env.CONTENTSTACK_DELIVERY_TOKEN,
environment: process.env.CONTENTSTACK_ENVIRONMENT,
live_preview: {
enable: true,
preview_token: process.env.CONTENTSTACK_PREVIEW_TOKEN,
host: process.env.CONTENTSTACK_PREVIEW_HOST,
},
});

export const sdk = studioSdk.init({ stackSdk: stack });

When live_preview.enable is true, the Delivery SDK also needs host, the preview host for your region. Without it the SDK sends no request, and Studio shows a card that does not name the cause.

OptionTypeDefaultWhat it does
stackSdkDelivery SDK stackNoneThe stack from @contentstack/delivery-sdk. Studio reads every composition and entry through it. init doesn't check it. Without it, the first fetch rejects with STACK_SDK_MISSING.
localestringThe stack's localeThe language to fetch in when nothing more specific sets one. See How the language is chosen.
cslp.appendTagsbooleanfalseAdds edit tags to bound values, so Visual Editor can edit them. Your components receive them as $-prefixed props, such as $title.
openInStudioButton.enablebooleantrueShows the Open in Studio button on compositions when your site is open in Visual Editor.
openInStudioButton.positionstring"bottom"Where that button sits: top, top-left, top-right, top-center, bottom, bottom-left, bottom-right or bottom-center.
contentTypeUidstring"compositions"The content type Studio stores compositions in.
debugbooleanfalseLogs warnings in production about content that didn't load, such as a binding to a field that isn't there. The SDK logs them in development already. Each one is listed in the Error-message index, under Warnings about content that didn't load.
stateobjectNoneWhere shared component state lives, and which state variables authors can bind. Its keys are storage ("session", "local" or "custom"), storageKey, getState, setState, subscribe and variables. See Share state between components.

Studio checks the config when you call init. A wrong value throws a TypeError that starts with The Config passed is invalid. and lists each problem.

Fetch a composition​

The object studioSdk.init returns has three methods. There's no .public namespace on it, so call sdk.fetchCompositionData(...), not sdk.public.fetchCompositionData(...).

MethodReturnsUse it for
sdk.fetchCompositionData(query, options?)Promise<StudioComponentSpecOptions>Fetching a composition and all the data it's bound to, on the server or anywhere outside React. Pass the result to <StudioComponent />.
sdk.fetchComposition(query, options?)Promise<StudioSpec>Fetching only the composition's structure. spec.data comes back empty, and you supply the data to <StudioComposition />.
sdk.getConfig()objectReading back openInStudioButton, contentTypeUid and cslp as the SDK resolved them.

fetchCompositionData returns the spec options object itself, not an object wrapping it.

const specOptions = await sdk.fetchCompositionData({
url: "/blog/ai-101",
searchQuery: request.nextUrl.searchParams.toString(),
});

Which composition to fetch​

useCompositionData takes these fields. The two fetch methods take the same fields plus searchQuery.

You passWhat Studio does
{ compositionUid: "blog_post" }Fetches that composition. This is the fastest lookup. For a connected template, it renders the first entry of the connected content type unless you also pass templateEntryUid in the options.
{ url: "/blog/ai-101" }Finds the template whose URL matches, then the entry for that URL. This is slower, because Studio compares the URL against every template. See URL variables for how matching works.
{ compositionUid: "blog_post", url: "/blog/ai-101" }Fetches that composition and finds the entry for that URL.
{ url: "/blog/ai-101", templateContentTypeUid: "blog_post" }Fetches the first template connected to that content type and ignores url.
{ templateContentTypeUid: "blog_post" }Fetches the first template connected to that content type, with its first entry unless you pass templateEntryUid.

To choose which one a route passes, see Fetch the template on the client or the server.

searchQuery is required on fetchCompositionData and fetchComposition. Pass the incoming request's query string, as a string, a URLSearchParams or a plain object. It carries the live preview hash. It also carries the language, variant and preview entry an author picked in Studio. The server can't read these from the browser. Pass "" when there's no request to forward.

Options that change the fetch​

OptionTypeWhat it does
localestringThe language for this fetch. It overrides the locale you passed to init. Contentstack's language fallback applies.
variantAliasstringFetches the entry's content for that Personalize variant. Inside Studio, the variant the author picked wins.
templateEntryUidstringThe entry of the connected content type to render. It wins over the URL and over the first-entry default. Inside Studio, the entry the author picked to preview wins.

How the language is chosen​

Studio uses the first of these that's set.

  1. The language picked in Studio, when the page is open in Studio.
  2. The {{locale}} variable in the template's URL.
  3. The locale option on the fetch.
  4. The locale passed to init.
  5. The stack's own locale.

What a fetch returns​

StudioComponentSpecOptions is what fetchCompositionData resolves to, and what useCompositionData returns as specOptions.

FieldWhat it holds
specThe composition and its data, as a StudioSpec.
fetchOptionsWhat <StudioComponent /> needs to fetch again on the client. Pass it through without reading or changing it.
seoThe template's SEO values, with bound fields filled in: pageTitle, pageDescription, openGraphImage and metaProps. It's null when the composition has none.
connectedContentTypeUidThe UID of the content type the template is connected to, or null for a freeform template. fetchCompositionData sets it, and useCompositionData leaves it out. It doesn't tell you whether the page exists, so don't decide a 404 from it.

To tell a connected template from a freeform one, read connectedContentTypeUid. With useCompositionData, read spec.compositionEntry.connected_content_type instead. It's empty or missing on a freeform template.

When a fetch fails​

A fetch that can't return a composition rejects instead of returning an empty result. useCompositionData puts the same error in error. The error is a ComposableStudioError, and its message starts with [Contentstack Studio] and the code.

FieldWhat it holds
codeWhat went wrong, such as COMPOSITION_UID_NOT_FOUND or STACK_SDK_MISSING.
errorClassOne of SETUP_INCOMPLETE, SETUP_INVALID, NETWORK_ERROR, SERVER_ERROR or NOT_FOUND.
causesWhat to check, most likely first. Each has a cause and a fix.

Only the NOT_FOUND class means the content isn't there. Call isCompositionNotFound(error) to decide your status code. It's true for the NOT_FOUND codes, so answer with a 404. For every other error, answer with a 500, not a 404. The check reads error.code, so it works even when your app ends up with two copies of the SDK. instanceof CompositionNotFoundError doesn't.

import { isCompositionNotFound } from "@contentstack/studio-react";

let specOptions;
try {
specOptions = await sdk.fetchCompositionData({ url: "/blog/ai-101", searchQuery: "" });
} catch (error) {
if (isCompositionNotFound(error)) return notFound(); // your framework's 404
throw error; // your framework's 500
}

For every code, what it means and how to fix it, see the Error-message index.

Hooks​

HookReturnsWhat it's for
useCompositionData(query, options?){ specOptions, isLoading, error, refetchData, refetchSpec }Fetching a composition on the client.
useSelected()booleanInside your component, whether it or anything inside it is selected on the canvas. Always false outside Studio. Use it to keep a tooltip or menu open while an author edits its contents.
useHiddenElementNotification(){ openHiddenElementNotification, closeHiddenElementNotification }Inside your component, opening and closing Studio's notice about content an author can't reach on the canvas, such as a closed modal.
useStudioRichText(text, $text?, entry?, embeddedItems?)A React nodeRendering a JSON RTE, HTML RTE, Markdown or plain string value in your own component, with embedded entries and assets resolved. See Render rich text and embedded entries.
useStudioState(key)[value, setValue]Reading and writing one shared state key. Every component that uses the same key sees the same value. See Share state between components.

useCompositionData​

useCompositionData(query, options?)

query takes the fields in Which composition to fetch, and options takes those in Options that change the fetch. The SDK doesn't export types for them, so pass object literals.

The result is always in one of three states. Check isLoading first, then error. After both checks, TypeScript knows specOptions isn't null.

StatespecOptionsisLoadingerror
Loadingnulltruenull
FailednullfalseThe error. It's typed unknown. Pass it to isCompositionNotFound to decide between a 404 and a 500.
LoadedThe spec optionsfalsenull

refetchData fetches the bound data again. Call it from your onEntryChange callback: a live edit changes the data, and that is all it needs to pull back. It does nothing on a freeform template.

refetchSpec runs the whole fetch again, not only the bound data. A call while a fetch is already running does nothing.

A query needs at least one of compositionUid, url or templateContentTypeUid. Without any of them, it fails straight away with the error Either compositionUid, url, or templateContentTypeUid is required.

const { specOptions, isLoading, error } = useCompositionData({ url: "/blog/ai-101" });

if (isLoading) return <Spinner />;
if (isCompositionNotFound(error)) return <NotFound />;
if (error) return <ErrorPage />;
return <StudioComponent specOptions={specOptions} />;

Components​

ComponentPropsWhat it renders
<StudioComponent />specOptions, data?A template, on your site's routes. Inside Studio and Visual Editor it adds editing.
<StudioCanvas />NoneSections, on the route your Canvas URL points at. It finds the section from the URL Studio loads and fetches it itself. Outside Studio it renders nothing.
<StudioComposition />context, spec?, compositionUid?, loadingFallback?, errorFallback?A composition against data you already hold. See StudioComposition and Slot props.
<Slot>data?, childrenA slot value inside your component, with data the components dropped into it can bind. See StudioComposition and Slot props.
<VisualEditorCreateCompositionButton />error?, alwaysShow?, childrenYour own button that creates a template for a route with none.

<StudioComponent /> and <StudioCanvas /> aren't interchangeable. <StudioComponent /> goes on template routes such as /blog/[slug]. <StudioCanvas /> goes on one route of its own, never a catch-all route. See Mount the section preview route.

StudioComponent data​

data passes values from your app that aren't in Contentstack, such as a view count from your analytics service. Authors bind them from Component Data when they bind a prop. See Bring your own data.

<StudioComponent specOptions={specOptions} data={{ viewCount: 1204 }} />

VisualEditorCreateCompositionButton​

It renders nothing itself. Pass a function as its child, and it calls that function with these values.

ValueWhat it holds
shouldShowtrue when the page is open in Visual Editor and error is set, or when alwaysShow is true.
handleCreateCompositionCall it to ask Visual Editor to open Studio and create a connected template.
isLoadingtrue while that request runs.
isInsideVBtrue when the page is open in Visual Editor.
<VisualEditorCreateCompositionButton error={error}>
{({ shouldShow, handleCreateComposition, isLoading }) =>
shouldShow ? (
<button onClick={handleCreateComposition} disabled={isLoading}>
Create a template for this page
</button>
) : null
}
</VisualEditorCreateCompositionButton>

Register components​

FunctionWhat it does
registerComponents(components, options?)Registers a list of components.
registerComponent(component)Registers one component.
registerLazyComponent(config, loader)Registers a component whose code loads the first time a composition uses it. loader returns a promise of the component. See Lazy-load components.
registerRTERenderer(config)Sets how rich text renders: embeddedEntry and embeddedAsset functions that return HTML, plus the JSON RTE serializer options such as customElementTypes. Each call replaces the whole config.

If you register a type that's already registered, Studio skips it. So it's safe to run the registration twice, such as on a hot reload. For what goes in each component's config, see Component schema: props, types and defaults.

registerComponents takes these options.

OptionWhat it does
stricttrue throws on a duplicate type instead of skipping it.
overrideDefaultComponentstrue lets your component replace one already registered with the same type.

Design tokens, classes and breakpoints​

FunctionReturnsWhat it does
registerDesignTokens(tokens, options?)The full token setGives Studio your colors, typography and spacing. Studio's default tokens are merged in unless you pass { allowDefaultDesignTokens: false }.
registerDesignClasses(classes)The class names, keyed by nameGives Studio named classes authors can apply. Declare the list as const to get the names typed.
registerBreakpoints(breakpoints)NothingSets the breakpoints on the canvas. The first must have the id default and no query.
getDesignTokens()The token setReads the registered tokens. Each value is the var(--...) reference Studio generated, which you can't work out by hand.

See Register design tokens and Register breakpoints.

Shared state​

ExportWhat it does
registerComponentStateVariable(key, definition)Declares a state variable next to the component that uses it, so authors can bind it. A declaration here wins over the same key declared in init.
registerGlobalStateVariables(variables)Declares several variables at the same level as init's state.variables. Import it early on the client, so the browser uses the same variables the server rendered with.
studioState.get(key), .set(key, next), .subscribe(key, listener), .reset()Reads and writes shared state outside React.

A state variable's type is string, number, boolean, choice, array or object. See Share state between components.

Server rendering helpers​

FunctionReturnsWhat it's for
getSSRStyleTags(specs, options?)A <style> tag, as a stringAdding a server-rendered composition's CSS to the head.
getSSRStyles(specs, options?)CSS, as a stringThe same CSS without the tag.
extractStyles(specs)CSS, as a stringThe component CSS only, without the design-token variables.
getCompositionMetadata(specOptions, options?){ title, description, ogImage, canonical, tags }Turning a template's SEO values into meta tags for your framework's head. Pass { baseUrl: "https://www.example.com" } to get canonical.
renderMetadataToHtml(metadata)HTML, as a stringWriting those meta tags and the canonical link as HTML. It leaves out <title>.

For getSSRStyleTags and getSSRStyles, specs is one StudioSpec, such as specOptions.spec, or a list of them. extractStyles takes only a list, such as [specOptions.spec]. getSSRStyles and getSSRStyleTags include the design-token variables unless you pass { includeDesignTokens: false }. See SEO and metadata.

Detect Studio​

ExportWhat it tells you
isStudioCanvas(searchQuery?)true when the page is on the Studio canvas or in Visual Editor.
isStudioEditorMode(searchQuery?)true on the canvas or in a live preview.
detectStudioMode(searchQuery?)"canvas", "preview" or "visitor".
markStudioMode()Records the mode on the page. The SDK calls it when it loads, so call it yourself only in scripts that run before the SDK.
STUDIO_CANVAS_QUERY_PARAM"cs-composable-studio", the query parameter on every URL the Studio canvas loads.
STUDIO_MODE_HTML_CLASS"cs-studio-canvas", the class the SDK adds to <html> on the canvas.

On the server, these return false or "visitor" unless you pass the request's query string. See Keep third-party scripts out of the canvas.

Types​

TypeWhat it describes
StudioComponentSpecOptionsWhat fetchCompositionData returns. See What a fetch returns.
StudioSpecA composition: its layout, its entry, the sections it uses and their data.
SearchQueryInputThe searchQuery field the fetch methods take.
StudioCompositionProps, SlotPropsThe props of <StudioComposition /> and <Slot>.
Cslptag{ "data-cslp": string } | undefined, the edit tag on a $-prefixed prop. Spread it on the element that shows the value. See Make bound values editable in Visual Editor.
StudioAttributes{ studioAttributes? }, the attributes Studio passes to a component registered with wrap: false. It's set only inside Studio.
SeoMetadata, CompositionMetadataThe seo field of a fetch, and what getCompositionMetadata returns.
DesignTokens, DesignTokensInputWhat registerDesignTokens returns and takes.
UserConfigThe config init takes.
ComposableStudioErrorThe error a fetch rejects with. See When a fetch fails.
CompositionNotFoundErrorThe subclass of ComposableStudioError for the NOT_FOUND codes. Use isCompositionNotFound rather than instanceof.
isCompositionNotFound(error)A function, not a type. true when the error means the content isn't there, so you answer with a 404.

Where to go next​

Report a problem with this page