React Studio SDK API Reference
The Studio React SDK lets developers integrate and render Contentstack Studio compositions within React applications. It connects the application to Contentstack’s Delivery SDK, enabling Live Preview and synchronization between Studio and the deployed front end.
The SDK supports initializing Studio, loading compositions, and rendering structured layouts in React. It also includes utilities to manage component registration, design tokens, responsive breakpoints, and Studio-level interactions for building modular and composable front-end experiences.
Additional Resources:
To learn more about Studio and its integration, refer to our About Studio and Get Started with Studio React SDK documents.
Hooks
Contentstack provides a set of custom React Hooks that align with standard React patterns. These Hooks help developers access the data and context needed to manage component behavior and data flow effectively.
useCompositionData
The useCompositionData React hook fetches composition data and specifications. Use this as the primary method to load a composition along with its SEO and meta information.
Returns
| Name | Type | Description |
| specOptions | StudioComponentSpecOptions | The fetched composition specification. |
| seo | SeoMetadata | SEO metadata for the composition. |
| hasSpec | boolean | Indicates whether the composition returned a UI spec. Used to determine 404 handling. |
| hasTemplate | boolean | Indicates whether the composition has an associated template. Used to determine 404 handling. |
| spec | StudioSpec | The fetched composition specification, representing the UI of the composition. |
| refetchSpec | () => void | Function to refetch the specification. |
| isLoading | boolean | Indicates whether the request is in progress. |
Parameters:
| Name | Type | Description |
| compositionQuery | CompositionQuery | Query object for fetching composition data. |
| options | CompositionQueryOptions | Optional configuration options to fetch linked data. |
import { useCompositionData } from '@contentstack/studio-react';
const { spec, refetchSpec, isLoading, error } = useCompositionData(compositionQuery,options);useSelected
The useSelected React hook identifies whether the current component instance is selected within Studio. This is useful for displaying inline tooltips, dialogs, or other contextual UI elements.
import { useSelected } from '@contentstack/studio-react';
const isSelected = useSelected();
useHiddenElementNotification
The useHiddenElementNotification React hook enables components to show or hide modals, tooltips, dialogs, or other hidden elements that require external control. It notifies Studio when the component should be opened for editing.
When to Use
- Modals/Dialogs: When you need to show or hide modal content.
- Tooltips: For displaying contextual information.
- Dropdowns: For expandable content sections.
- Hidden Sections: For collapsible content areas.
- Any Interactive Element: That needs to be controlled from outside the canvas.
Note: When using this hook, register the component in registerComponents with wrap: false. The component will receive a studioAttributes prop. Destructure it to ensure proper behavior, and expose open and close via useImperativeHandle.
| Name | Type | Description |
| openHiddenElementNotification | () => void | Function to signal that the hidden element notification should be opened in Studio. |
| closeHiddenElementNotification | () => void | Function to signal that the hidden element notification should be closed in Studio. |
import {
StudioAttributes,
useHiddenElementNotification,
useSelected,
} from "@contentstack/studio-react";
import { useEffect, useImperativeHandle, useRef, useState } from "react";
interface HiddenElementNotificationProps extends ComposableAttributes {}
export const HiddenElementNotification = ({
studioAttributes,
}: HiddenElementNotificationProps) => {
const isSelected = useSelected();
const ref = useRef<HTMLDivElement>(null);
const { openHiddenElementNotification } = useHiddenElementNotification();
const [showHiddenSection, setShowHiddenSection] = useState(false);
function toggleHiddenSection() {
setShowHiddenSection((prev) => !prev);
}
useEffect(() => {
if (isSelected) {
openHiddenElementNotification(); // Sends message to Studio
setShowHiddenSection(true); // Shows hidden section locally
}
}, [isSelected, openHiddenElementNotification]);
// Expose methods for external control from Studio
useImperativeHandle(
studioAttributes?.["ref"],
() => ({
open() {
setShowHiddenSection(true);
},
close() {
setShowHiddenSection(false);
},
element: ref.current,
}),
[]
// Called when Studio sends "open" event
// Called when Studio sends "close" event
);
return (
<div {...studioAttributes} ref={ref}>
<p>This is a hidden element notification</p>
<button onClick={toggleHiddenSection}>Toggle Hidden section visibility</button>
{showHiddenSection && (
<div>
<p>This is a hidden section</p>
</div>
)}
</div>
);
};
Utilities
Contentstack’s SDK provides utilities that help developers detect page and component states, such as loading or selection. These utilities determine how components render and behave, ensuring consistent and predictable interactions across the application.
clientRendererModeUtil
The clientRendererModeUtilutility determines the renderer mode and environment. It provides methods to check the current rendering context and adjust component behavior accordingly.
import { clientRendererModeUtil } from '@contentstack/studio-react';
// Check environment
const isInStudio = clientRendererModeUtil.isInsideStudioFrame();
const isInVisualBuilder = clientRendererModeUtil.isInsideVisualBuilderFrame();
const isInIframe = clientRendererModeUtil.isInsideIframe();
// Check specific composition state
const isEditing = clientRendererModeUtil.isEditingCurrentComposition('my-composition-uid');
// Get current renderer mode
const mode = clientRendererModeUtil.getMode('my-composition-uid');
isInsideStudioFrame
The isInsideStudioFrame() method checks whether the app is running inside Studio to adapt its behavior specifically for the Studio environment.
Note: Use this method to enable Studio-specific behavior only when the application runs inside Studio.
isInsideIframe
The isInsideIframe() method checks whether the app is running inside an iframe, which can affect rendering and communication with parent contexts.
Note: Implement cross-frame communication or adjust the UI when running inside an iframe.
isInsideVisualBuilderFrame()
The isInsideVisualBuilderFrame() method checks whether the app is running inside the Visual Builder frame, which provides additional editing capabilities.
Note: Enable enhanced editing features when in the Visual Builder environment.
isEditingCurrentComposition
The isEditingCurrentComposition() method verifies whether a specific composition, identified by its UID, is currently being edited. This ensures that interactive or editable features activate only when the targeted composition is in edit mode.
Use this method when multiple compositions are displayed on a single page and you need to identify which one is currently active for editing.
Parameters:
| Name | Type | Description |
| compositionUidcompositionUid | string | The UID of the composition used to check if it is currently in edit mode. |
import { isCompositionBeingEdited } from '@contentstack/studio-react';
const isEdited = isCompositionBeingEdited();
getMode
The getMode() method determines the current renderer mode for a given composition, identified by its UID. It allows components to conditionally render UI elements based on whether the composition is in edit, edit-button, or preview mode.
| edit | The composition is currently being edited. |
| show_edit_button | The environment should display an Edit button (inside Visual Builder or when the composition is inactive in Studio). |
| preview | The composition is in default preview mode. |
Parameters:
| Name | Type | Description |
| compositionUid | string | The UID of the composition used to check its current renderer mode. |
// Use mode to conditionally render components
if (mode === "edit") {
// Show editing interface
} else if (mode === "show_edit_button") {
// Show edit button
} else {
// Show preview mode
}
// Check if in editing mode for specific composition
if (clientRendererModeUtil.isEditingCurrentComposition('my-composition-uid')) {
// Enable editing features
}
// Adapt behavior based on environment
if (clientRendererModeUtil.isInsideVisualBuilderFrame()) {
// Show enhanced editing capabilities
}
Component Registry
registerComponents
The registerComponents method registers custom React components in Studio, making them available in the Visual Builder and Studio interface. This is the primary method for integrating reusable UI components into the composition workflow.
Parameters
| Name | Type | Description |
| components | RegisterComponentOptionsInput<C>[] | Array of component configuration objects to register. |
| options | RegisterComponentOptions (optional) | Optional configuration options. Controls registry behavior such as built-in component availability. |
Import Example
import { registerComponents } from '@contentstack/studio-react';
registerComponents([
{
type: "my-custom-component",
component: MyCustomComponent,
displayName: "My Custom Component",
description: "A custom component for special functionality",
sections: ["Custom Components"],
wrap: false,
props: {
title: {
type: "string",
displayName: "Title",
defaultValue: "Default Title",
placeholder: "Enter title here"
},
isVisible: {
type: "boolean",
displayName: "Visible",
defaultValue: true
}
},
styles: {
default: {
styleSections: ["size", "spacing", "background"],
defaultClasses: ["my-component"],
displayName: "Default Styles"
}
}
}
]);Component Configuration Options
| Parameter | Type | Description |
| type (required) | string | Unique identifier for the component. Used internally. |
| component (required) | React.Component | The React component to register. Can be a function or class component. |
| wrap | boolean | string | Controls whether the component is wrapped in a container element.
|
| displayName | string | Name shown in the component library. Defaults to type if not specified. |
| description | string | Description shown in the component library for authors. |
| sections | string[] | string | Determines where the component appears in the left panel. Built-in sections include:
|
| thumbnailUrl | string | Thumbnail image displayed in the component library. |
| props | object | Defines configurable props (see below). |
| styles | object | Defines style groups, default classes, and available style sections. |
Note: When using useHiddenElementNotification, set wrap: false and destructure the studioAttributes prop in your component.
Props Configuration
The props object defines configurable properties for your component, with each prop offering options that enhance the editing experience in Studio.
Common prop options include:
- type: Data type of the prop (required).
- displayName: Label shown in the editor.
- defaultValue: Initial value when the component is first added.
- helpText: Helper text to guide content creators.
Supported Prop Types
- String Prop ("string")
The string prop defines a text input field for capturing short to long text content.
Options
- control: "default" (single-line), "large" (multi-line textarea), "markdown" (markdown editor)
- placeholder: Hint text when empty.
- defaultValue: Initial text value.
- helpText: Guidance for authors.
Use Cases: Titles, descriptions, names, URLs, or custom text fields.
Example
- Boolean Prop ("boolean")
The boolean prop defines a toggle switch for capturing true or false values.
Options
- defaultValue: Initial state (true or false).
- helpText: Guidance for authors.
Use Cases: Show/hide elements, enable/disable features, toggle states.
Example
showHeader: {
type: "boolean",
displayName: "Show Header",
defaultValue: true,
helpText: "Toggle the visibility of the page header"
}
- Number Prop ("number")
The number prop defines a numeric input field for capturing integers and decimal values.
Options
- defaultValue: Initial numeric value.
- helpText: Guidance for authors.
- control: "default" (number field) or "slider" (number slider).
- max: The maximum allowed value.
- min: The minimum allowed value.
- step: The increment or decrement step size when adjusting the value.
Use Cases: Counts, dimensions, ratings, percentages, or quantity limits.
Example
maxItems: {
type: "number",
displayName: "Maximum Items",
defaultValue: 10,
helpText: "Maximum number of items to display"
}
- Choice Prop ("choice")
The choice prop defines a selection input from a predefined list of options.
Options
- options: An array of available options.
- defaultValue: Selected option(s) (must be an array).
- multiSelect: Controls whether multiple values can be selected.
- control: "radio" or "dropdown".
Use Cases: Variants, themes, sizes, categories, or status fields.
Example
buttonStyle: {
type: "choice",
displayName: "Button Style",
options: ["primary", "secondary", "outline", "ghost"],
defaultValue: ["primary"],
control: "radio",
helpText: "Choose the visual style of the button"
}
- Date String Prop ("datestring")
The datestring prop defines a picker input for selecting date and time values.
Options
- defaultValue: ISO date string.
- helpText: Guidance for authors.
Use Cases: Publication dates, event dates, expirations, or timestamps.
Example
publishDate: {
type: "datestring",
displayName: "Publication Date",
defaultValue: new Date().toISOString(),
helpText: "When this content should be published"
}
- Href Prop ("href")
The href prop defines a URL input field for navigation and linking purposes.
Options
- defaultValue: Initial URL value.
- helpText: Guidance for authors.
Use Cases: Navigation links, external resources, or internal routing.
Example
learnMoreUrl: {
type: "href",
displayName: "Learn More Link",
defaultValue: "https://example.com/learn-more",
helpText: "URL for the learn more page"
}
- Image URL Prop ("imageurl")
The imageurl prop defines a specialized URL input field for specifying image sources.
Options
- defaultValue: Initial image URL.
- helpText: Guidance for authors.
Use Cases: Image sources, backgrounds, banners, or icons.
Example
heroImage: {
type: "imageurl",
displayName: "Hero Image",
defaultValue: "https://example.com/hero.jpg",
helpText: "Main image for the hero section"
}
- Object Prop ("object")
The object prop defines a nested configuration object based on a custom schema.
Options
- defaultValue: Initial object.
- properties: Schema definition for the object’s structure.
Use Cases: Theme settings, configuration objects, or grouped options.
Example
themeSettings: {
type: "object",
displayName: "Theme Settings",
defaultValue: {
color: "blue",
fontSize: "16px"
},
properties: {
color: {
type: "string",
displayName: "Primary Color"
},
fontSize: {
type: "string",
displayName: "Font Size"
}
}
}
- Array Prop ("array")
The array prop defines a collection of values or objects.
Options
- defaultValue: Initial array.
- items: Defines the schema of each item in the array.
- helpText: Guidance for authors.
Use Cases: Lists, tags, categories, or repeated items.
Example
tagList: {
type: "array",
displayName: "Tags",
items: {
type: "string"
},
defaultValue: ["1", "2", "3"]
}
- Slot Prop ("slot")
The slot prop defines a container placeholder for child components or dynamic content.
Options
- displayName: Label shown in the editor.
- helpText: Guidance for authors.
Use Cases: Flexible layouts, drag-and-drop child components.
Example
mainSlot: {
type: "slot",
displayName: "Main Slot",
helpText: "Container for nested components"
}
- JSON RTE Prop ("json_rte")
The json_rte prop defines a Rich Text Editor field for structured and formatted content.
Options
- defaultValue: JSON document structure.
- helpText: Guidance for authors.
Use Cases: Formatted text, complex content blocks, long-form descriptions.
Example
content: {
type: "json_rte",
displayName: "Main Content",
defaultValue: {
type: "doc",
content: [
{
type: "paragraph",
content: [{ type: "text", text: "Enter your content here..." }]
}
]
},
helpText: "Rich text content with formatting options"
}
- Any Prop ("any")
The any prop defines a flexible data type for dynamic or miscellaneous content. When you mark a prop as any, you can link any data to the field from Contentstack.
Options
- defaultValue: Initial value (any type).
- helpText: Guidance for authors.
Use Cases: Custom data, experimental fields, or dynamic inputs not covered by other prop types.
Example
customData: {
type: "any",
displayName: "Custom Data",
defaultValue: null,
helpText: "Store any additional data for this component"
}
Options Configuration
- allowedBuiltInComponents
The allowedBuiltInComponents option controls the built-in components available in the registry.
Type: boolean | string[]
Values
- true (default): Enable all built-in components.
- false: Disable all built-in components.
- string[]: Enable specific built-in components.
Built-in Component Categories
- Basic Components: page, symbol, fragment, text, header, button, link, link-container, json-rte, collapsible-text.
- Media Components: video, embed, image.
- Container Components: section, box, hstack (columns), vstack (rows), repeater, condition-block.
Example
// Enable all built-in components registerComponents(components, { allowedBuiltInComponents: true }); // Disable all built-in components registerComponents(components, { allowedBuiltInComponents: false }); // Enable only specific built-in components registerComponents(components, { allowedBuiltInComponents: [ "button", "text", "image", "section", "box" ] }); - overrideDefaultComponents
The overrideDefaultComponents option controls whether user components can override built-in components with the same type name.
Type: boolean
Values
- false (default): Built-in components cannot be overridden.
- true: User components can override built-in components.
Example
// Allow overriding built-in components
registerComponents(components, {
overrideDefaultComponents: true
});
// Prevent overriding (safer option)
registerComponents(components, {
overrideDefaultComponents: false
});
Error Handling
When overrideDefaultComponents is false, attempting to register a component with the same type as a built-in component will throw an error:
try {
registerComponents(
[
{
type: "button", // This conflicts with built-in button
component: CustomButton
}
],
{
overrideDefaultComponents: false
}
);
} catch (error) {
// Error: "Component with type 'button' is already registered."
console.error("Registration failed:", error.message);
}
JSON RTE Registry
registerJsonRte
The registerJsonRte method registers custom element types (blocks) and text wrappers (inline marks) for JSON RTE content.
Use Case: Extend the default rich-text rendering with branded components, custom formatting, and visual effects.
Parameters:
| Name | Type | Description |
| config | IJsonToHtmlOptions | Configuration object for custom JSON RTE rendering. |
import { registerJsonRte } from '@contentstack/studio-react';
registerJsonRte({
customElementTypes: {
// Custom block-level elements
infoBox: (attrs, child, jsonBlock, extraProps) => {
return `<div class="info-box" style="padding: 1rem; background-color: #e3f2fd; border-left: 4px solid #2196f3;">${child}</div>`;
},
callout: (attrs, child, jsonBlock, extraProps) => {
const type = jsonBlock.attrs?.type || 'info';
const colors = {
info: '#e3f2fd',
warning: '#fff3e0',
error: '#ffebee',
success: '#e8f5e9'
};
return `<div class="callout callout-${type}" style="padding: 1rem; background-color: ${colors[type]}; border-radius: 4px; margin: 1rem 0;">${child}</div>`;
}
},
customTextWrapper: {
// Custom inline text formatting
highlight: (child, value) => {
return `<span class="highlight" style="background-color: #ffeb3b; padding: 2px 4px;">${child}</span>`;
},
color: (child, value) => {
return `<span class="text-color" style="color: ${value};">${child}</span>`;
},
code: (child, value) => {
return `<code class="inline-code" style="background-color: #f5f5f5; padding: 2px 4px; border-radius: 3px; font-family: monospace;">${child}</code>`;
}
},
allowNonStandardTypes: true
});Configuration Options
The configuration options let developers control how JSON RTE content is converted to HTML. You can define custom elements, text wrappers, and allow non-standard types to extend rendering behavior.
customElementTypes (IJsonToHtmlElementTags)
The customElementTypes option defines the block-level elements you want to support in rich text.
Function Signature
Parameters
| Name | Type | Description |
| attrs | string | HTML attributes represented as a string. |
| child | string | The rendered inner HTML content of the element. |
| jsonBlock | object | The full JSON block object. Use jsonBlock.attrs to access attributes. |
| extraProps | object (optional) | Additional context props passed from the rendering environment. |
customElementTypes: {
// Custom info box component
infoBox: (attrs, child, jsonBlock) => {
const title = jsonBlock.attrs?.title || 'Information';
const icon = jsonBlock.attrs?.icon || 'ℹ️';
return `
<div class="info-box" style="border: 1px solid #ddd; border-radius: 8px; padding: 1rem; margin: 1rem 0;">
<div style="display: flex; align-items: center; margin-bottom: 0.5rem;">
<span style="font-size: 1.2rem; margin-right: 0.5rem;">${icon}</span>
<strong>${title}</strong>
</div>
<div>${child}</div>
</div>
`;
},
// Custom quote component
quote: (attrs, child, jsonBlock) => {
const author = jsonBlock.attrs?.author || 'Unknown';
const source = jsonBlock.attrs?.source || '';
return `
<blockquote class="custom-quote" style="border-left: 4px solid #007bff; padding-left: 1rem; margin: 1rem 0; font-style: italic;">
<div>${child}</div>
<footer style="margin-top: 0.5rem; font-size: 0.9rem; color: #666;">
— ${author}${source ? `, ${source}` : ''}
</footer>
</blockquote>
`;
}
}
customTextWrapper (IJsonToHtmlTextTags)
The customTextWrapper option defines inline text wrappers or marks used for applying formatting and effects to text within JSON RTE content.
Use Case: Enables custom styles such as highlights, keyboard inputs, or branded inline elements in rich-text fields.
Function Signature
(child: any, value: any) => string
Parameters
| Name | Type | Description |
| child | string | The text content to wrap. |
| value | any | The attribute value applied to the wrapper (for example, a color code, style, or effect). |
Example
customTextWrapper: {
// Highlight text with custom colors
highlight: (child, value) => {
const colors = {
yellow: '#ffeb3b',
green: '#4caf50',
blue: '#2196f3',
red: '#f44336'
};
const color = colors[value] || value;
return `<span class="highlight" style="background-color: ${color}; padding: 2px 4px; border-radius: 3px;">${child}</span>`;
},
// Custom text styling
style: (child, value) => {
const styles = {
bold: 'font-weight: bold;',
italic: 'font-style: italic;',
underline: 'text-decoration: underline;',
strikethrough: 'text-decoration: line-through;'
};
const style = styles[value] || '';
return `<span style="${style}">${child}</span>`;
},
// Custom font sizes
fontSize: (child, value) => {
return `<span style="font-size: ${value}px;">${child}</span>`;
}
}
allowNonStandardTypes (boolean)
The allowNonStandardTypes option enables the use of non-standard element types in JSON RTE content.
Use Case:
- Custom Components: Add branded elements such as info boxes, callouts, or product cards.
- Enhanced Text: Format text with highlights, colors, or special fonts.
- Interactive Elements: Insert interactive components, like buttons or widgets, into rich text.
- Brand Consistency: Ensure rich text content aligns with your design system and styling standards.
Real World Example
import { registerJsonRte } from '@contentstack/studio-react';
// Register custom JSON RTE components for a marketing website
registerJsonRte({
customElementTypes: {
// Product showcase within rich text
productShowcase: (attrs, child, jsonBlock) => {
const product = jsonBlock.attrs;
return `
<div class="product-showcase" style="
border: 2px solid #e0e0e0;
border-radius: 12px;
padding: 1.5rem;
margin: 1.5rem 0;
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
">
<div style="display: flex; align-items: center; gap: 1rem;">
<img src="${product.image || '/placeholder.jpg'}"
alt="${product.name || 'Product'}"
style="width: 80px; height: 80px; border-radius: 8px; object-fit: cover;">
<div>
<h4 style="margin: 0 0 0.5rem 0; color: #2c3e50;">${product.name || 'Product Name'}</h4>
<p style="margin: 0 0 0.5rem 0; color: #7f8c8d;">${product.description || 'Product description'}</p>
<div style="font-size: 1.25rem; font-weight: bold; color: #e74c3c;">
$${product.price || '0.00'}
</div>
</div>
</div>
${child}
</div>
`;
},
// Testimonial block
testimonial: (attrs, child, jsonBlock) => {
const testimonial = jsonBlock.attrs;
return `
<div class="testimonial" style="
background-color: #f8f9fa;
border-left: 4px solid #007bff;
padding: 1.5rem;
margin: 1.5rem 0;
border-radius: 0 8px 8px 0;
">
<div style="font-style: italic; margin-bottom: 1rem; color: #495057;">
"${child}"
</div>
<div style="display: flex; align-items: center; gap: 0.5rem;">
<img src="${testimonial.avatar || '/default-avatar.jpg'}"
alt="${testimonial.author || 'Author'}"
style="width: 40px; height: 40px; border-radius: 50%; object-fit: cover;">
<div>
<strong style="color: #2c3e50;">${testimonial.author || 'Author Name'}</strong>
${testimonial.title ? `<br><span style="color: #6c757d; font-size: 0.9rem;">${testimonial.title}</span>` : ''}
</div>
</div>
</div>
`;
},
// Code block with syntax highlighting
codeBlock: (attrs, child, jsonBlock) => {
const language = jsonBlock.attrs?.language || 'text';
return `
<div class="code-block" style="
background-color: #2d3748;
border-radius: 8px;
padding: 1rem;
margin: 1rem 0;
overflow-x: auto;
">
<div style="
color: #e2e8f0;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 0.9rem;
line-height: 1.5;
">
<div style="
color: #a0aec0;
border-bottom: 1px solid #4a5568;
padding-bottom: 0.5rem;
margin-bottom: 1rem;
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.05em;
">
${language}
</div>
<pre style="margin: 0; white-space: pre-wrap;">${child}</pre>
</div>
</div>
`;
}
},
customTextWrapper: {
// Brand colors for text
brandColor: (child, value) => {
const brandColors = {
primary: '#007bff',
secondary: '#6c757d',
success: '#28a745',
danger: '#dc3545',
warning: '#ffc107',
info: '#17a2b8'
};
const color = brandColors[value] || value;
return `<span style="color: ${color}; font-weight: 500;">${child}</span>`;
},
// Custom text effects
textEffect: (child, value) => {
const effects = {
glow: 'text-shadow: 0 0 10px currentColor;',
shadow: 'text-shadow: 2px 2px 4px rgba(0,0,0,0.3);',
outline: 'text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;'
};
const effect = effects[value] || '';
return `<span style="${effect}">${child}</span>`;
}
},
allowNonStandardTypes: true
});
Conditional Props
Conditional props are component props whose values depend on the values of other props. They enable components to change behavior or configuration dynamically, based on user selections or predefined logic.
Use Case: Show or modify a prop’s value only when another related prop meets a certain condition — for example, displaying a "link URL" field only when "link enabled" is set to true.
Example
props: {
showAdvanced: {
type: "boolean",
displayName: "Show Advanced Options",
defaultValue: false
},
advancedConfig: {
type: "object",
displayName: "Advanced Configuration",
defaultValue: {},
// This prop will only be shown when showAdvanced is true
// (handled automatically by the Studio)
}
}
Binding Support
Binding Support allows all component props to connect to external data sources using data binding.
Use Case: Dynamically populate component values such as text, images, or links by binding them to CMS entries, API responses, or environment variables within Studio.
Styles Configuration
The styles object defines the styling options available for your component, enabling customization of its visual appearance.
Example
styles: {
// Default style group
default: {
styleSections: ["size", "spacing", "background", "border"],
defaultClasses: ["my-component", "rounded"],
defaultStyles: { padding: "1rem" },
displayName: "Default Styles"
},
// Custom style group
custom: {
styleSections: ["typography", "transform"],
defaultClasses: ["custom-text"],
defaultStyles: { fontSize: "16px" },
displayName: "Custom Styles"
}
}
Available Style Sections
| Section | Description |
| class | Custom CSS classes. |
| size | Width, height, min/max dimensions. |
| spacing | Margin and padding. |
| position | Position and z-index. |
| visibility | Display and opacity. |
| layout | Flexbox and grid properties. |
| typography | Font properties and text alignment. |
| transform | Transform properties. |
| media | Media query styles. |
| background | Background properties. |
| shadow | Box and text shadow. |
| effect | Filter and backdrop-filter. |
| overflow | Overflow properties. |
| border | Border properties. |
| responsive | Responsive design utilities. |
Style Section Categories
| Category | Description |
| block | All block-level styling options. |
| text | Typography-focused styling options. |
Registry Options
The registerComponents configuration object controls built-in component availability, user overrides, and debug logging behavior.
allowedBuiltInComponents
The allowedBuiltInComponents option controls the built-in components available in the registry.
Type: boolean | string[]
Values:
- true: Enable all built-ins (default)
- false: Disable all built-ins
- string[]: Enable only specific built-in types
overrideDefaultComponents
The overrideDefaultComponents option determines if user components override built-in components with the same type identifier.
Type: boolean
Values:
- false: Prevent overriding built-ins (default)
- true: Allow user components to override
debug
The debug option enables debug logging for troubleshooting during component registration.
Type: boolean
Values:
- false: Disable logging (default)
- true: Enable logging
Usage Example
// Basic component registration
registerComponents([
{
type: "hero-section",
component: HeroSection,
displayName: "Hero Section",
description: "A prominent section for main content",
sections: ["Custom Components"]
}
]);
// Advanced component with full configuration
registerComponents([
{
type: "product-card",
component: ProductCard,
displayName: "Product Card",
description: "Display product information in a card format",
sections: ["Custom Components", "E-commerce"],
wrap: false,
thumbnailUrl: "/images/product-card-thumbnail.png",
hideFromContentCreators: false,
hideFromComponentList: false,
props: {
productId: {
type: "string",
displayName: "Product ID",
placeholder: "Enter product ID"
},
showPrice: {
type: "boolean",
displayName: "Show Price",
defaultValue: true
}
},
styles: {
default: {
styleSections: ["size", "spacing", "background", "border"],
defaultClasses: ["product-card"],
displayName: "Card Styles"
},
content: {
styleSections: ["typography"],
defaultClasses: ["product-content"],
displayName: "Content Styles"
}
}
}
]);
// Component with validation
registerComponents([
{
type: "contact-form",
component: ContactForm,
displayName: "Contact Form",
description: "A form for collecting contact information",
sections: ["Forms"],
props: {
email: {
type: "string",
displayName: "Email",
placeholder: "Enter email address"
}
}
}
]);
registerComponents
The registerComponents method makes custom React components available in Studio. Once registered, these components can be added, configured, and styled by content creators within the Studio interface alongside built-in components.
Returns
Type void
Parameters
| Name | Type | Description |
| componentConfig (required) | Component configuration object | One or more component configurations to register. |
| options | Optional registry configuration | Controls built-ins, overrides, and debugging. |
Example
import { registerComponents } from '@contentstack/studio-react';
registerComponents(componentConfig, options);
Best Practices
- Performance: Keep custom element renders lightweight and avoid complex DOM manipulation.
- Accessibility: Use semantic HTML and include appropriate ARIA attributes to ensure custom elements are usable with assistive technologies.
- Responsiveness: Apply CSS and layout techniques that adapt well across devices.
- Consistency: Align custom elements with your design system to maintain a cohesive appearance across content.
- Testing: Validate custom elements with varied content scenarios, edge cases, and real-world usage to ensure stability and reliability.
Note:
- Unique type: Each component requires a unique identifier.
- System protection: Built-in components cannot be overridden.
- Validation: Props can include custom validation rules.
- Styling: Components have access to design tokens and CSS-in-JS.
- Interactivity: Use useHiddenElementNotification with wrap: false for interactive components.
- Performance: Components are registered once and cached for efficiency.
Design Registry
The Design Registry manages shared, reusable styling primitives for your project. It centralizes design tokens and design classes to deliver consistent styling across components. It supports validation and automatic exposure of tokens as CSS custom properties on :root.
registerDesignTokens
The registerDesignTokens method registers design tokens and exposes them as CSS variables. These tokens define consistent visual attributes such as color palettes, spacing scales, and typography settings across components and interfaces.
Parameters
| Name | Type | Description |
| designTokens (required) | DesignTokensInput | Object containing your token groups and values. |
| options | Partial<DesignTokensOptionsInput> | Optional configuration object for advanced token registration behavior. |
Returns
Type DesignTokens
A normalized object reflecting the registered token groups and their corresponding CSS variables.
Below is an outline of supported tokens:
Tokens Configuration
Below is an outline of supported tokens:
Global Tokens
Global tokens define high-level, project-wide primitives that serve as foundational values for other token categories.
Example
{
colorTokens: {
primary: "#007bff",
secondary: "#6c757d",
success: "#28a745",
danger: "#dc3545",
warning: "#ffc107",
info: "#17a2b8"
},
spaceTokens: {
xs: "0.25rem",
sm: "0.5rem",
md: "1rem",
lg: "1.5rem",
xl: "3rem"
}
}
Size Tokens
Size tokens define base sizing primitives and common dimensional presets for consistent layout and spacing.
Example
{
size: {
tokens: {
xs: "4px",
sm: "8px",
md: "16px",
lg: "32px",
xl: "64px"
},
width: {
"w-full": "100%",
"w-auto": "auto",
"w-screen": "100vw"
},
height: {
"h-full": "100%",
"h-auto": "auto",
"h-screen": "100vh"
},
minWidth: {
"min-w-0": "0px",
"min-w-full": "100%"
},
minHeight: {
"min-h-0": "0px",
"min-h-full": "100%"
},
maxWidth: {
"max-w-xs": "20rem",
"max-w-sm": "24rem",
"max-w-md": "28rem"
},
maxHeight: {
"max-h-32": "8rem",
"max-h-64": "16rem"
}
}
}
Spacing Tokens
Spacing tokens define values for gaps, margins, paddings, and standardized spacing styles to ensure consistent layout structure.
Example
{
spacing: {
tokens: {
"space-0": "0",
"space-1": "0.25rem",
"space-2": "0.5rem",
"space-4": "1rem",
"space-8": "2rem"
},
margin: {
"m-0": "0",
"m-1": "0.25rem",
"m-auto": "auto"
},
padding: {
"p-0": "0",
"p-1": "0.25rem",
"p-4": "1rem"
},
style: {
card: {
margin: "1rem",
padding: "1.5rem"
}
}
}
}
Typography Tokens
Typography tokens define text-related properties such as color, weight, size, line height, letter spacing, and typographic styles used across the interface.
Example
{
typography: {
color: {
"text-primary": "#007bff",
"text-secondary": "#6c757d",
"text-muted": "#6c757d"
},
fontWeight: {
"font-light": 300,
"font-normal": 400,
"font-medium": 500,
"font-semibold": 600,
"font-bold": 700
},
fontSize: {
"text-xs": "0.75rem",
"text-sm": "0.875rem",
"text-base": "1rem",
"text-lg": "1.125rem",
"text-xl": "1.25rem"
},
lineHeight: {
"leading-none": 1,
"leading-tight": 1.25,
"leading-normal": 1.5,
"leading-relaxed": 1.75
},
letterSpacing: {
"tracking-tighter": "-0.05em",
"tracking-tight": "-0.025em",
"tracking-normal": "0em",
"tracking-wide": "0.025em"
},
style: {
heading: {
fontFamily: "Inter, sans-serif",
fontWeight: "600",
fontSize: "1.5rem",
lineHeight: 1.2,
letterSpacing: "-0.025em"
}
}
}
}
Background Tokens
Background tokens define background colors and reusable background styles to ensure visual consistency across components and sections.
Example
{
background: {
color: {
"bg-primary": "#007bff",
"bg-secondary": "#6c757d",
"bg-light": "#f8f9fa"
},
style: {
"gradient-primary": {
type: "linear-gradient",
angle: 135,
stops: ["#667eea", "#764ba2"]
},
"gradient-radial": {
type: "radial-gradient",
position: "center",
stops: ["#f093fb", "#f5576c"]
},
"hero-image": {
type: "image",
url: "/images/hero-bg.jpg",
size: "cover",
repeat: "no-repeat",
align: "center"
}
}
}
}
Border Tokens
Border tokens define border colors, radii, and border style configurations for consistent component framing.
Example
{
border: {
color: {
"border-primary": "#007bff",
"border-secondary": "#6c757d",
"border-light": "#dee2e6"
},
radius: {
"rounded-none": "0",
"rounded-sm": "0.125rem",
"rounded": "0.25rem",
"rounded-md": "0.375rem",
"rounded-lg": "0.5rem",
"rounded-full": "9999px"
},
style: {
button: {
style: "solid",
width: "2px",
color: "#007bff",
radius: "0.375rem"
},
card: {
style: "solid",
width: "1px",
color: "#dee2e6",
radius: "0.5rem"
}
}
}
}
Shadow Tokens
Shadow tokens define shadow colors and predefined shadow presets to create depth and elevation in the interface.
Example
{
shadow: {
color: {
"shadow-light": "rgba(0, 0, 0, 0.1)",
"shadow-medium": "rgba(0, 0, 0, 0.15)",
"shadow-dark": "rgba(0, 0, 0, 0.25)"
},
style: {
sm: {
x: "0",
y: "1px",
blur: "2px",
spread: "0",
color: "rgba(0, 0, 0, 0.05)"
},
md: {
x: "0",
y: "4px",
blur: "6px",
spread: "-1px",
color: "rgba(0, 0, 0, 0.1)"
},
lg: {
x: "0",
y: "10px",
blur: "15px",
spread: "-3px",
color: "rgba(0, 0, 0, 0.1)"
}
}
}
}
Transform Tokens
Transform tokens define reusable transform configurations, such as scaling, rotation, and translation, to maintain consistent motion and layout behavior.
Example
{
transform: {
style: {
"hover-lift": {
translateY: "-2px",
scaleX: "1.02",
scaleY: "1.02"
},
"rotate-45": {
rotateZ: "45deg"
},
"skew-x": {
skewX: "10deg"
}
}
}
}
Effects Tokens
Effects tokens define reusable visual effect configurations, such as filters and blurs, to enhance the appearance of UI elements consistently.
Example
{
effects: {
filter: {
"blur-sm": "blur(4px)",
"blur": "blur(8px)",
"blur-md": "blur(12px)",
"brightness-75": "brightness(0.75)",
"brightness-90": "brightness(0.9)",
"contrast-125": "contrast(1.25)"
}
}
}
Position and Layout Tokens
Position and layout tokens define layering properties and spatial configurations to control element stacking and structural layout across the interface.
Example
{
position: {
zIndex: {
"z-0": "0",
"z-10": "10",
"z-20": "20",
"z-30": "30",
"z-40": "40",
"z-50": "50"
},
position: {
"static": "static",
"relative": "relative",
"absolute": "absolute",
"fixed": "fixed",
"sticky": "sticky"
}
},
layout: {
gap: {
"gap-0": "0",
"gap-1": "0.25rem",
"gap-2": "0.5rem",
"gap-4": "1rem",
"gap-8": "2rem"
}
}
}
Visibility Tokens
Visibility tokens define opacity and visibility levels to manage element transparency and display behavior consistently.
Example
{
visibility: {
opacity: {
"opacity-0": "0%",
"opacity-25": "25%",
"opacity-50": "50%",
"opacity-75": "75%",
"opacity-100": "100%"
}
}
}
Overflow Tokens
Overflow tokens define scrolling and clipping behaviors to control how content is displayed when it exceeds its container boundaries.
Example
{
overflow: {
style: {
"scroll": "scroll",
"auto": "auto",
"hidden": "hidden",
"visible": "visible",
"clip": "clip"
}
}
}
registerDesignTokens Optional Configuration Objects
The following parameters of the registerDesignTokens method are optional configuration objects that define how tokens are processed and which values are permitted.
allowDefaultDesignTokens
The allowDefaultDesignTokens option determines whether to merge custom design tokens with built-in defaults (true) or use only the custom tokens provided (false).
Use
Set true to inherit sensible defaults, or set false for a fully bespoke system.
Type: boolean
Example
// Include built-in tokens (recommended for most use cases)
const tokens = registerDesignTokens({
colorTokens: {
brand: "#ff6b6b",
accent: "#4ecdc4"
}
}, {
allowDefaultDesignTokens: true
});
// Use only custom tokens
const customTokens = registerDesignTokens({
colorTokens: {
brand: "#ff6b6b",
accent: "#4ecdc4"
}
}, {
allowDefaultDesignTokens: false
});
allowedValuesLevel
The allowedValuesLevel option governs the level of control authors have when selecting or inputting values in Studio.
Use
- dynamic: Most restrictive option. Only allows linking design properties through data sources. Ideal when design values are strictly managed in the CMS.
- tokens: Moderate flexibility. Allows both predefined design tokens and data binding, but does not allow arbitrary custom values.
- arbitrary: Most flexible option. Permits the use of tokens, data binding, and free-form custom values entered manually.
Type: tokens, dynamic, and arbitrary
Example
// Development environment - maximum flexibility
const devTokens = registerDesignTokens({
colorTokens: {
brand: "#ff6b6b",
accent: "#4ecdc4"
},
spacing: {
tokens: {
"space-custom": "2rem"
}
}
}, {
allowDefaultDesignTokens: true,
allowedValuesLevel: "arbitrary"
});
// Production environment - strict consistency
const prodTokens = registerDesignTokens({
colorTokens: {
brand: "#ff6b6b",
accent: "#4ecdc4"
}
}, {
allowDefaultDesignTokens: false,
allowedValuesLevel: "dynamic"
});
// Hybrid approach - built-ins with moderate flexibility
const hybridTokens = registerDesignTokens({
colorTokens: {
brand: "#ff6b6b"
}
}, {
allowDefaultDesignTokens: true,
allowedValuesLevel: "arbitrary"
});
Default Design Tokens
The DEFAULT_DESIGN_TOKENS object provides a predefined set of design tokens that establish consistent styling across components. It includes tokens for colors, typography, spacing, layout, borders, shadows, and other visual properties. These tokens act as reusable values that help maintain design consistency and simplify UI customization in Studio.
const DEFAULT_DESIGN_TOKENS = {
colorTokens: {
"color-dark": "#000000",
"color-primary": "#6c5ce7",
"color-white": "#ffffff",
"color-secondary": "#475161",
},
size: {
width: SIZE_TOKENS,
height: SIZE_TOKENS,
minWidth: SIZE_TOKENS,
minHeight: SIZE_TOKENS,
maxWidth: SIZE_TOKENS,
},
layout: {
gap: {
"gap-2": "0.125rem",
"gap-4": "0.25rem",
"gap-5": "0.3125rem",
"gap-6": "0.375rem",
"gap-8": "0.5rem",
"gap-10": "0.625rem",
"gap-12": "0.75rem",
"gap-14": "0.875rem",
"gap-15": "0.9375rem",
"gap-16": "1rem",
},
},
typography: {
fontSize: {
"font-size-sm": "0.75rem",
"font-size-md": "0.875rem",
"font-size-base": "1rem",
"font-size-lg": "1.25rem",
"font-size-xl": "1.75rem",
"font-size-2xl": "2.125rem",
},
fontWeight: {
"font-regular": 400,
"font-medium": 500,
"font-semi-bold": 600,
"font-bold": 700,
"font-extra-bold": 800,
},
lineHeight: {
"line-height-default": 1.5,
"line-height-sm": 1.25,
"line-height-xs": 1,
},
color: {
"color-dark": "#000000",
"color-primary": "#6c5ce7",
"color-white": "#ffffff",
"color-secondary": "#475161",
},
},
border: {
radius: {
"border-radius-xs": "2px",
"border-radius-sm": "4px",
"border-radius-md": "6px",
"border-radius-lg": "8px",
"border-radius-xl": "10px",
"border-radius-50": "50%",
"border-radius-full": "9999px",
},
color: {
"color-lighter": "#f5f5f5",
"color-light": "#a9b6cb",
"color-base": "#647696",
"color-dark": "#475161",
"color-link": "#6c5ce7",
"color-focus": "#edf1f7",
"color-warning": "#d62400",
"color-success": "#007a52",
},
},
spacing: {
tokens: {
"space-2": "0.125rem",
"space-4": "0.25rem",
"space-5": "0.3125rem",
"space-6": "0.375rem",
"space-8": "0.5rem",
"space-10": "0.625rem",
"space-12": "0.75rem",
"space-14": "0.875rem",
"space-15": "0.9375rem",
"space-16": "1rem",
"space-18": "1.125rem",
"space-20": "1.25rem",
"space-22": "1.375rem",
"space-24": "1.5rem",
"space-25": "1.5625rem",
"space-26": "1.625rem",
"space-28": "1.75rem",
"space-30": "1.875rem",
"space-32": "2rem",
"space-34": "2.125rem",
"space-35": "2.1875rem",
"space-36": "2.25rem",
"space-38": "2.375rem",
"space-40": "2.5rem",
"space-50": "3.125rem",
"space-60": "3.75rem",
"space-80": "5rem",
"space-100": "6.25rem",
"space-160": "10rem",
},
},
background: {
color: {
"color-dark": "#000000",
"color-primary": "#6c5ce7",
"color-white": "#ffffff",
"color-secondary": "#475161",
},
},
position: {
zIndex: {
"z-index-default": "0",
"z-index-positive": "1",
"z-index-negative": "-1",
"z-index-deepdive": "-99999",
},
},
visibility: {
opacity: {
"opacity-0": "0%",
"opacity-10": "10%",
"opacity-20": "20%",
"opacity-40": "40%",
"opacity-50": "50%",
"opacity-80": "80%",
"opacity-100": "100%",
},
},
shadow: {
style: {
default: { x: "0px", y: "1px", blur: "3px", spread: "0px", color: "rgba(0, 0, 0, 0.08)" },
heavy: { x: "0px", y: "2px", blur: "3px", spread: "0px", color: "rgba(0, 0, 0, 0.35)" },
"light-purple-1px": { x: "0px", y: "0px", blur: "2px", spread: "0px", color: "rgba(0, 0, 0, 0.14)" },
"light-purple-2px": { x: "0px", y: "2px", blur: "2px", spread: "0px", color: "rgba(0, 0, 0, 0.12)" },
"light-purple-3px": { x: "0px", y: "1px", blur: "3px", spread: "0px", color: "rgba(108, 92, 231, 0.2)" },
"light-purple-4px": { x: "0px", y: "2px", blur: "4px", spread: "0px", color: "rgba(0, 0, 0, 0.14)" },
"light-purple-5px": { x: "0px", y: "5px", blur: "5px", spread: "0px", color: "rgba(0, 0, 0, 0.12)" },
"light-purple-10px": { x: "0px", y: "1px", blur: "10px", spread: "0px", color: "rgba(108, 92, 231, 0.2)" },
"light-purple-10px-1": { x: "0px", y: "8px", blur: "10px", spread: "1px", color: "rgba(0, 0, 0, 0.14)" },
"light-purple-14px": { x: "0px", y: "3px", blur: "14px", spread: "3px", color: "rgba(0, 0, 0, 0.12)" },
"light-purple-15px": { x: "0px", y: "4px", blur: "15px", spread: "0px", color: "rgba(108, 92, 231, 0.2)" },
"light-purple-24px": { x: "0px", y: "16px", blur: "24px", spread: "2px", color: "rgba(0, 0, 0, 0.14)" },
"light-purple-30px": { x: "0px", y: "6px", blur: "30px", spread: "5px", color: "rgba(0, 0, 0, 0.12)" },
"light-purple-10px-3": { x: "0px", y: "8px", blur: "10px", spread: "3px", color: "rgba(108, 92, 231, 0.2)" },
"light-purple-38px": { x: "0px", y: "24px", blur: "38px", spread: "3px", color: "rgba(0, 0, 0, 0.14)" },
"light-purple-46px": { x: "0px", y: "9px", blur: "46px", spread: "8px", color: "rgba(0, 0, 0, 0.12)" },
"light-purple-15px-8": { x: "0px", y: "11px", blur: "15px", spread: "8px", color: "rgba(108, 92, 231, 0.2)" },
"black-1px": { x: "0px", y: "0px", blur: "2px", spread: "0px", color: "rgba(0, 0, 0, 0.14)" },
"black-2px": { x: "0px", y: "2px", blur: "2px", spread: "0px", color: "rgba(0, 0, 0, 0.12)" },
"black-3px": { x: "0px", y: "1px", blur: "3px", spread: "0px", color: "rgba(0, 0, 0, 0.2)" },
"black-4px": { x: "0px", y: "2px", blur: "4px", spread: "0px", color: "rgba(0, 0, 0, 0.14)" },
"black-5px": { x: "0px", y: "5px", blur: "5px", spread: "0px", color: "rgba(0, 0, 0, 0.12)" },
"black-10px": { x: "0px", y: "1px", blur: "10px", spread: "0px", color: "rgba(0, 0, 0, 0.2)" },
"black-10px-1": { x: "0px", y: "8px", blur: "10px", spread: "1px", color: "rgba(0, 0, 0, 0.14)" },
"black-14px": { x: "0px", y: "3px", blur: "14px", spread: "3px", color: "rgba(0, 0, 0, 0.12)" },
"black-15px": { x: "0px", y: "4px", blur: "15px", spread: "0px", color: "rgba(0, 0, 0, 0.2)" },
"black-16px": { x: "0px", y: "16px", blur: "24px", spread: "2px", color: "rgba(0, 0, 0, 0.14)" },
"black-30px": { x: "0px", y: "6px", blur: "30px", spread: "5px", color: "rgba(0, 0, 0, 0.12)" },
"black-10px-8": { x: "0px", y: "8px", blur: "10px", spread: "0px", color: "rgba(0, 0, 0, 0.2)" },
"black-38px": { x: "0px", y: "24px", blur: "38px", spread: "3px", color: "rgba(0, 0, 0, 0.14)" },
"black-46px": { x: "0px", y: "9px", blur: "46px", spread: "8px", color: "rgba(0, 0, 0, 0.12)" },
"black-15px-2": { x: "0px", y: "11px", blur: "15px", spread: "0px", color: "rgba(0, 0, 0, 0.2)" },
},
},
};registerDesignClasses
The registerDesignClasses method registers design classes with the design registry, making predefined styling combinations available in the Class tab of the Design section in Studio. These classes can be applied to components, and the method returns an object containing them for use in component props with type safety.
Parameters
| Name | Type | Description |
| designClasses | DesignClassesInput[] | An array of class definitions. Accepts either strings or objects with { name, displayName }. |
Returns
Type: DesignClassesNames<C>
Value: An object that maps each registered class name to its corresponding value.
Example:
{
primary: "primary",
card: "card"
}
Input Variants
String Input (Simple):
Provide class names only. The registry automatically returns a key–value map of those names.
Example
const classes = registerDesignClasses([
'primary',
'secondary',
'success',
'danger'
]);
// Returns:
// {
// primary: 'primary',
// secondary: 'secondary',
// success: 'success',
// danger: 'danger'
// }
Object Input (With Display Names):
Provide { name, displayName } to show a user-friendly label in Studio while keeping the internal name stable.
Example
const classes = registerDesignClasses([
{ name: 'primary', displayName: 'Primary Button' },
{ name: 'secondary', displayName: 'Secondary Button' },
{ name: 'outline', displayName: 'Outline Style' }
]);
// Returns:
// {
// primary: 'primary',
// secondary: 'secondary',
// outline: 'outline'
// }
Complete Example
import { registerDesignClasses } from '@contentstack/studio-react';
// Register design classes for a button component system
const buttonClasses = registerDesignClasses([
// Simple variants
'primary',
'secondary',
'success',
'danger',
'warning',
'info',
// Named variants with display names
{ name: 'outline-primary', displayName: 'Primary Outline' },
{ name: 'outline-secondary', displayName: 'Secondary Outline' },
{ name: 'ghost-primary', displayName: 'Primary Ghost' },
{ name: 'ghost-secondary', displayName: 'Secondary Ghost' },
// Size variants
{ name: 'sm', displayName: 'Small' },
{ name: 'md', displayName: 'Medium' },
{ name: 'lg', displayName: 'Large' },
// Special styles
{ name: 'rounded', displayName: 'Rounded' },
{ name: 'pill', displayName: 'Pill Shape' },
{ name: 'block', displayName: 'Full Width' }
]);
console.log(buttonClasses);
// Returns:
// {
// primary: 'primary',
// secondary: 'secondary',
// success: 'success',
// danger: 'danger',
// warning: 'warning',
// info: 'info',
// 'outline-primary': 'outline-primary',
// 'outline-secondary': 'outline-secondary',
// 'ghost-primary': 'ghost-primary',
// 'ghost-secondary': 'ghost-secondary',
// sm: 'sm',
// md: 'md',
// lg: 'lg',
// rounded: 'rounded',
// pill: 'pill',
// block: 'block'
// }
Usage in Components
Wire registered classes directly into component props for type safety and consistency:
// In your component registration
props: {
variant: {
type: "choice",
displayName: "Button Variant",
choices: Object.values(buttonClasses), // Use registered class names
defaultValue: "primary"
},
size: {
type: "choice",
displayName: "Button Size",
choices: ["sm", "md", "lg"],
defaultValue: "md"
}
}
getDesignTokens
The getDesignTokens method retrieves the currently registered design tokens from the design registry.
Example
import { getDesignTokens } from '@contentstack/studio-react';
const tokens = getDesignTokens();Returns
DesignTokens: All active tokens, normalized and mapped to CSS variables.
Usage Example
import { getDesignTokens } from '@contentstack/studio-react';
// Get current design tokens
const currentTokens = getDesignTokens();
console.log(currentTokens.typography.color);
// Returns: { "text-primary": "var(--token-text-primary)", ... }
console.log(currentTokens.spacing.margin);
// Returns: { "m-0": "var(--token-m-0)", "m-1": "var(--token-m-1)", ... }
extractStyles
The extractStyles method extracts CSS from composition specifications for Server-Side Rendering (SSR). It generates static CSS that can be injected into the <head> of an HTML document during SSR.
Parameters
| Name | Type | Description |
| specs | (StudioSpec | null)[] | Array of composition specifications to extract styles from. null entries are ignored. |
Returns
string: Combined CSS including root base styles, composition-specific styles, and utility styles.
Example
import { extractStyles } from '@contentstack/studio-react';
const cssStyles = extractStyles([compositionSpec]);
Usage Example: SSR Applications
// In your Next.js or similar SSR framework
import { extractStyles } from '@contentstack/studio-react';
export async function generateStaticParams() {
const { specOptions } = await useCompositionSpec({ ... });
const cssStyles = extractStyles([specOptions]);
return {
props: {
cssStyles,
specOptions
}
};
}
export default function Page({ cssStyles, specOptions }) {
return (
<>
<style dangerouslySetInnerHTML={{ __html: cssStyles }} />
<StudioComponent specOptions={specOptions} />
</>
);
}
Breakpoint Registry
The Breakpoint Registry defines responsive breakpoints in Studio and controls how content adapts to different screen sizes. By default, mobile, tablet, and desktop breakpoints are available. You can also register additional custom breakpoints if needed.
registerBreakpoints
The registerBreakpoints method registers breakpoints for responsive design. Breakpoints can be selected from the Viewport icon in the Studio navigation bar.
Example:
import { registerBreakpoints } from '@contentstack/studio-react';
registerBreakpoints([
{
id: "default",
displayName: "Desktop",
previewSize: {
width: 1200,
height: 800,
},
},
{
id: "Iphone 15",
displayName: "Iphone 15",
query: "(max-width: 428px)",
previewSize: {
width: 375,
height: 812,
},
},
]);
Breakpoint Options
Each breakpoint includes configuration details that specify its identifier, display name, activation conditions, and preview dimensions within Studio’s responsive view.
id
The id property defines the unique identifier for the breakpoint.
Type: string
Requirements:
- The first breakpoint must have id: "default" (this is the base breakpoint).
- No duplicate IDs allowed.
- IDs are case-sensitive (e.g., "Mobile" and "mobile" are different).
displayName
The displayName property defines the human-readable name shown in the UI.
Type: string | undefined
Behavior:
- Optional; if omitted, the id is used as the display name.
- Recommended to use descriptive names such as "iPhone 15" or "Tablet Portrait".
query
The query property defines the CSS media query that activates the breakpoint.
Type: string | undefined
Requirements:
- Default breakpoint: Must not include a query; it is automatically set to *.
- Non-default breakpoints: Must include a valid media query.
- Examples: (max-width: 768px), (min-width: 1024px), and (max-width: 1200px).
previewSize
The previewSize property defines the preview canvas dimensions used when the breakpoint is selected.
Type: object
Requirements:
- width (number): preview width in pixels.
- height (number): preview height in pixels.
Note: The preview size must fall within the range defined by the query.
Example: Preview size for mobile and tablet
// Mobile portrait
previewSize: {
width: 375,
height: 812
}
// Tablet landscape
previewSize: {
width: 1024,
height: 768
}
Basic responsive breakpoints
The following code registers default responsive breakpoints for desktop, tablet, and mobile views with defined preview sizes.
registerBreakpoints([
{
id: "default",
displayName: "Desktop",
previewSize: {
width: 1200,
height: 800,
},
},
{
id: "tablet",
displayName: "Tablet",
query: "(max-width: 1024px)",
previewSize: {
width: 768,
height: 1024,
},
},
{
id: "mobile",
displayName: "Mobile",
query: "(max-width: 768px)",
previewSize: {
width: 375,
height: 812,
},
},
]);
Device-specific breakpoints
The following code registers custom responsive breakpoints for specific devices.
registerBreakpoints([
{
id: "default",
displayName: "Desktop Large",
previewSize: {
width: 1440,
height: 900,
},
},
{
id: "iphone-15",
displayName: "iPhone 15",
query: "(max-width: 428px)",
previewSize: {
width: 375,
height: 812,
},
},
{
id: "ipad-pro",
displayName: "iPad Pro",
query: "(max-width: 1024px) and (min-width: 769px)",
previewSize: {
width: 1024,
height: 768,
},
},
]);
Minimal configuration (auto-filled displayName)
The following code registers responsive breakpoints with minimal configuration, automatically generating display names for each device.
registerBreakpoints([
{
id: "default",
previewSize: {
width: 1200,
height: 800,
},
},
{
id: "mobile",
query: "(max-width: 768px)",
previewSize: {
width: 375,
height: 812,
},
},
]);
Default Breakpoints
By default, the following breakpoints are included:
- Desktop (id: "default")
- Tablet (id: "tablet")
- Mobile (id: "mobile")
Example
const DEFAULT_BREAKPOINTS = [
{
id: "default",
displayName: "Desktop",
previewSize: {
width: 1200,
height: 800,
},
},
{
id: "tablet",
displayName: "Tablet",
query: "(max-width: 1023px)",
previewSize: {
width: 800,
height: 600,
},
},
{
id: "mobile",
displayName: "Mobile",
query: "(max-width: 767px)",
previewSize: {
width: 400,
height: 500,
},
},
];
Types & Interfaces
The Types & Interfaces defines the core type structures in the Studio SDK. These structures ensure type safety, consistency, and interoperability between components, design tokens, and composition specifications.
Spec-Related Types
The Spec-Related Types section defines type structures for compositions, design tokens, and component attributes in the Studio SDK. These definitions maintain data accuracy, UI consistency, and reliable interaction across Studio components.
StudioSpec
The StudioSpec type represents the complete composition specification, including UI structure, data, and metadata required for rendering compositions.
DesignTokensInput
The DesignTokensInput type defines the input object used when registering design tokens, specifying the structure and categories that form the basis of the design system.
StudioAttributes
The StudioAttributes type is required when registering a component with wrap: false. This attribute is required for the StudioComponent to work. Extend your component props with this attribute to ensure full compatibility.
Example
import { StudioAttributes } from "@contentstack/studio-react";
interface MyComponentProps extends StudioAttributes {
title: string;
}
const MyComponent = ({ studioAttributes, title }: MyComponentProps) => {
return (
<div {...studioAttributes}>
<h2>{title}</h2>
</div>
);
};
StudioComponentSpecOptions
The StudioComponentSpecOptions type is used to render a composition specification. It is passed to the StudioComponent and can be obtained either from the useCompositionSpec hook or from the fetchCompositionSpec method of the StudioSdk instance.
CslpTag
The CslpTag type is used to annotate CSLP attributes, which identify data in Live Preview and Visual Builder.
Usage Examples
Basic Usage
The following example demonstrates initializing the SDK, fetching a composition, and rendering it with the StudioComponent.
import {
registerDesignTokens,
registerDesignClasses,
registerBreakpoints
} from '@contentstack/studio-react';
// Register design tokens
const tokens = registerDesignTokens({
colors: {
primary: '#007bff',
secondary: '#6c757d'
},
spacing: {
sm: '0.5rem',
md: '1rem',
lg: '1.5rem'
}
});
// Register design classes
const classes = registerDesignClasses([
{ name: 'primary', displayName: 'Primary' },
{ name: 'secondary', displayName: 'Secondary' }
]);
// Register breakpoints
registerBreakpoints([
{ name: 'mobile', maxWidth: 768 },
{ name: 'tablet', minWidth: 769, maxWidth: 1024 },
{ name: 'desktop', minWidth: 1025 }
]);