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 to | Use |
|---|---|
| Connect the SDK to your stack | studioSdk.init |
| Render a template on a client-rendered route | useCompositionData with <StudioComponent /> |
| Render a template on a server-rendered route | sdk.fetchCompositionData with <StudioComponent /> |
| Render sections on your Canvas URL route | <StudioCanvas /> |
| Render a composition against data you already hold | sdk.fetchComposition with <StudioComposition /> |
| Offer authors a way to create a template from a missing route in Visual Editor | <VisualEditorCreateCompositionButton /> |
| Make your components available in Studio | registerComponents, registerComponent, registerLazyComponent |
| Give Studio your design tokens, classes and breakpoints | registerDesignTokens, registerDesignClasses, registerBreakpoints |
| Render rich text and embedded entries | registerRTERenderer, useStudioRichText |
| Share state between components | useStudioState, studioState, registerComponentStateVariable |
| Keep scripts out of the Studio canvas | isStudioCanvas, isStudioEditorMode |
| Put a server-rendered composition's CSS and meta tags in the head | getSSRStyleTags, 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.
| Option | Type | Default | What it does |
|---|---|---|---|
stackSdk | Delivery SDK stack | None | The 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. |
locale | string | The stack's locale | The language to fetch in when nothing more specific sets one. See How the language is chosen. |
cslp.appendTags | boolean | false | Adds edit tags to bound values, so Visual Editor can edit them. Your components receive them as $-prefixed props, such as $title. |
openInStudioButton.enable | boolean | true | Shows the Open in Studio button on compositions when your site is open in Visual Editor. |
openInStudioButton.position | string | "bottom" | Where that button sits: top, top-left, top-right, top-center, bottom, bottom-left, bottom-right or bottom-center. |
contentTypeUid | string | "compositions" | The content type Studio stores compositions in. |
debug | boolean | false | Logs 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. |
state | object | None | Where 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(...).
| Method | Returns | Use 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() | object | Reading 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 pass | What 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
| Option | Type | What it does |
|---|---|---|
locale | string | The language for this fetch. It overrides the locale you passed to init. Contentstack's language fallback applies. |
variantAlias | string | Fetches the entry's content for that Personalize variant. Inside Studio, the variant the author picked wins. |
templateEntryUid | string | The 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.
- The language picked in Studio, when the page is open in Studio.
- The
{{locale}}variable in the template's URL. - The
localeoption on the fetch. - The
localepassed toinit. - The stack's own locale.
What a fetch returns
StudioComponentSpecOptions is what fetchCompositionData resolves to, and what useCompositionData returns as specOptions.
| Field | What it holds |
|---|---|
spec | The composition and its data, as a StudioSpec. |
fetchOptions | What <StudioComponent /> needs to fetch again on the client. Pass it through without reading or changing it. |
seo | The template's SEO values, with bound fields filled in: pageTitle, pageDescription, openGraphImage and metaProps. It's null when the composition has none. |
connectedContentTypeUid | The 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.
| Field | What it holds |
|---|---|
code | What went wrong, such as COMPOSITION_UID_NOT_FOUND or STACK_SDK_MISSING. |
errorClass | One of SETUP_INCOMPLETE, SETUP_INVALID, NETWORK_ERROR, SERVER_ERROR or NOT_FOUND. |
causes | What 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
| Hook | Returns | What it's for |
|---|---|---|
useCompositionData(query, options?) | { specOptions, isLoading, error, refetchData, refetchSpec } | Fetching a composition on the client. |
useSelected() | boolean | Inside 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 node | Rendering 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.
| State | specOptions | isLoading | error |
|---|---|---|---|
| Loading | null | true | null |
| Failed | null | false | The error. It's typed unknown. Pass it to isCompositionNotFound to decide between a 404 and a 500. |
| Loaded | The spec options | false | null |
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
| Component | Props | What it renders |
|---|---|---|
<StudioComponent /> | specOptions, data? | A template, on your site's routes. Inside Studio and Visual Editor it adds editing. |
<StudioCanvas /> | None | Sections, 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?, children | A slot value inside your component, with data the components dropped into it can bind. See StudioComposition and Slot props. |
<VisualEditorCreateCompositionButton /> | error?, alwaysShow?, children | Your 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.
| Value | What it holds |
|---|---|
shouldShow | true when the page is open in Visual Editor and error is set, or when alwaysShow is true. |
handleCreateComposition | Call it to ask Visual Editor to open Studio and create a connected template. |
isLoading | true while that request runs. |
isInsideVB | true 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
| Function | What 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.
| Option | What it does |
|---|---|
strict | true throws on a duplicate type instead of skipping it. |
overrideDefaultComponents | true lets your component replace one already registered with the same type. |
Design tokens, classes and breakpoints
| Function | Returns | What it does |
|---|---|---|
registerDesignTokens(tokens, options?) | The full token set | Gives 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 name | Gives Studio named classes authors can apply. Declare the list as const to get the names typed. |
registerBreakpoints(breakpoints) | Nothing | Sets the breakpoints on the canvas. The first must have the id default and no query. |
getDesignTokens() | The token set | Reads 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
| Export | What 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
| Function | Returns | What it's for |
|---|---|---|
getSSRStyleTags(specs, options?) | A <style> tag, as a string | Adding a server-rendered composition's CSS to the head. |
getSSRStyles(specs, options?) | CSS, as a string | The same CSS without the tag. |
extractStyles(specs) | CSS, as a string | The 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 string | Writing 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
| Export | What 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
| Type | What it describes |
|---|---|
StudioComponentSpecOptions | What fetchCompositionData returns. See What a fetch returns. |
StudioSpec | A composition: its layout, its entry, the sections it uses and their data. |
SearchQueryInput | The searchQuery field the fetch methods take. |
StudioCompositionProps, SlotProps | The 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, CompositionMetadata | The seo field of a fetch, and what getCompositionMetadata returns. |
DesignTokens, DesignTokensInput | What registerDesignTokens returns and takes. |
UserConfig | The config init takes. |
ComposableStudioError | The error a fetch rejects with. See When a fetch fails. |
CompositionNotFoundError | The 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
- Install the SDK and call
init: Install the Studio SDK. - Render templates on client-rendered routes: Render on the client.
- Render templates on server-rendered routes: Render on the server.
- Look up the props of the bring-your-own-data components: StudioComposition and Slot props.