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

NameTypeDescription
specOptionsStudioComponentSpecOptionsThe fetched composition specification.
seoSeoMetadataSEO metadata for the composition.
hasSpecbooleanIndicates whether the composition returned a UI spec. Used to determine 404 handling.
hasTemplatebooleanIndicates whether the composition has an associated template. Used to determine 404 handling.
specStudioSpecThe fetched composition specification, representing the UI of the composition.
refetchSpec() => voidFunction to refetch the specification.
isLoadingbooleanIndicates whether the request is in progress.

Parameters:

NameTypeDescription
compositionQueryCompositionQueryQuery object for fetching composition data.
optionsCompositionQueryOptionsOptional configuration options to fetch linked data.
Example
import { useCompositionData } from '@contentstack/studio-react';
const { spec, refetchSpec, isLoading, error } = useCompositionData(compositionQuery,options);

Options

The following options let you customize how the SDK fetches composition data.

NameTypeDescription
variantAliasstringIdentifies a CMS variant created by Contentstack Personalize. You can pass the alias to the Delivery API or Delivery SDK to fetch personalized content. Use this instead of the variant UID in API and SDK requests. 
extendQueryobjectControls which reference fields and entry fields are fetched per content type. Use this when you need additional references (for example, inside arrays or modular blocks) or extra entry fields that are not bound in the composition.

variantAlias

When you pass variantAlias, the SDK includes it in the request so the Delivery API returns data for the specified variant.

A variant alias typically looks like cs_personalize_a_0, where:

  • cs_personalize is the prefix for Personalize-created variants.
  • The remaining segments identify the experience and the activated variant.

Example:

useCompositionData(
  { compositionUid: "home-page", url: "/home" },
  { variantAlias: "cs_personalize_a_0" }
);

extendQuery

extendQuery is an optional configuration under CompositionQueryOptions. It lets you control:

  • Which reference fields are included
  • Which entry fields are fetched for each content type

By default, the SDK fetches only the entry fields that are bound in the composition. Use extendQuery when you need:

  • Additional references (for example, references inside arrays or modular blocks)
  • Additional entry fields that are not bound in the composition

extendQuery supports two optional properties:

  • includeReferences
  • only

includeReferences

The includeReferences works the same way as the Delivery SDK’s includeReferences option. It retrieves the content of referenced entries in the response.

Use this option to include reference fields that are not already implied by the composition’s bindings.

only

The only works the same way as the Delivery SDK’s only query parameter. It lets you select specific fields of an entry.

By default, only bound entry fields are fetched. Use only to include additional entry fields (field UIDs) that are not bound in the composition.

Example 1:

useCompositionData(
  { compositionUid: "home-page", url: "/home" },
  {
    extendQuery: {
      page: {
        includeReferences: ["hero_image", "sections.author"],
        only: ["title", "url", ["hero_image", "url"], ["sections.author", "name"]],
      },
    },
  }
);

Example 2:

const options = {
  variantAlias: "cs_personalize_a_0",
  extendQuery: {
    page: {
      includeReferences: ["hero_image"],
      only: ["title", "url", ["hero_image", "url"]],
    },
  },
};

// fetchCompositionData(for SSR)
const specOptions = await studioSdk.fetchCompositionData(
  { compositionUid: "home-page", searchQuery },
  options
);

// useCompositionData
const { specOptions, isLoading, error } = useCompositionData(
  { compositionUid: "home-page", url: "/home" },
  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.

NameTypeDescription
openHiddenElementNotification() => voidFunction to signal that the hidden element notification should be opened in Studio.
closeHiddenElementNotification() => voidFunction 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:

NameTypeDescription
compositionUidcompositionUidstringThe 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.

editThe composition is currently being edited.
show_edit_buttonThe environment should display an Edit button (inside Visual Builder or when the composition is inactive in Studio).
previewThe composition is in default preview mode.

Parameters:

NameTypeDescription
compositionUidstringThe 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

NameTypeDescription
componentsRegisterComponentOptionsInput<C>[]Array of component configuration objects to register.
optionsRegisterComponentOptions (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

ParameterTypeDescription
type (required)stringUnique identifier for the component. Used internally.
component (required)React.ComponentThe React component to register. Can be a function or class component.
wrapboolean | string

Controls whether the component is wrapped in a container element.

  • true (default): Wrapped in a <div>.
  • false: Not wrapped (useful when handling your own container).
  • string: Wrapped in the specified HTML tag (e.g., section, span).
displayNamestringName shown in the component library. Defaults to type if not specified.
descriptionstringDescription shown in the component library for authors.
sectionsstring[] | string

Determines where the component appears in the left panel. Built-in sections include:

  • Basic: Text, buttons, links
  • Media: Images, video, embed
  • Container: Section, box, columns
  • Smart Containers: Repeaters, condition blocks
  • Advanced: Tabs, modals, accordions
  • Custom Components: User-defined
thumbnailUrlstringThumbnail image displayed in the component library.
propsobjectDefines configurable props (see below).
stylesobjectDefines 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:

NameTypeDescription
configIJsonToHtmlOptionsConfiguration 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

NameTypeDescription
attrsstringHTML attributes represented as a string.
childstringThe rendered inner HTML content of the element.
jsonBlockobjectThe full JSON block object. Use jsonBlock.attrs to access attributes.
extraPropsobject (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

NameTypeDescription
childstringThe text content to wrap.
valueanyThe 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

SectionDescription
classCustom CSS classes.
sizeWidth, height, min/max dimensions.
spacingMargin and padding.
positionPosition and z-index.
visibilityDisplay and opacity.
layoutFlexbox and grid properties.
typographyFont properties and text alignment.
transformTransform properties.
mediaMedia query styles.
backgroundBackground properties.
shadowBox and text shadow.
effectFilter and backdrop-filter.
overflowOverflow properties.
borderBorder properties.
responsiveResponsive design utilities.

Style Section Categories

CategoryDescription
blockAll block-level styling options.
textTypography-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

NameTypeDescription
componentConfig (required)Component configuration objectOne or more component configurations to register.
optionsOptional registry configurationControls 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

NameTypeDescription
designTokens (required)DesignTokensInputObject containing your token groups and values.
optionsPartial<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

NameTypeDescription
designClassesDesignClassesInput[]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

NameTypeDescription
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.

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