Studio Docs

When to Use

Add per-prop defaultValue to a registered Studio component so it renders visibly the moment an author drops it on the canvas, before any data binding.

Use when registering (or revisiting) a component that needs placeholder content the instant an author drags it from the palette, before any prop is bound. Also for triaging “component renders blank after drop”, empty palette previews, blank array items. Phrases: “set default value”, “blank on drop”, “palette preview empty”. Defaults are UX requirement, not polish.

Mandatory auth preflight: settle the credential before the first API call. Resolve it OAuth-first per authenticate-cma: CS_OAUTH_ACCESS_TOKEN, else the Contentstack MCP’s stored session. Never ask the user for a session authtoken. If nothing resolves, or a refresh fails with 400 invalid_refresh_token, hand them ! CONTENTSTACK_REGION=<code> npx @contentstack/mcp --auth (it needs a TTY and a browser, so it cannot be run for them) and wait. 403 error_code 316 is a valid credential aimed at another org: fix the org or the api_key, do not re-authenticate.

Wire Component Default Data

Context

Studio renders a component the moment it’s dropped on the canvas, long before the author wires any prop to entry data. If the props schema passed to registerComponent doesn’t set defaultValue on each visible prop, the canvas shows empty rectangles, missing images, and unlabeled buttons. Authors read that as “broken”, not “unbound”.

Reference: docs/bring-your-own-components/set-component-default-data.md.

Mental model:

  • defaultValue lives on each prop, never on the component as a whole.
  • Defaults apply only when a prop is unbound and not explicitly cleared. The moment the author binds the prop to template.*, contentstack.*, repeater context (e.g. repeater.title), or a static value, the binding wins.
  • If the author clears a field (empty string / null), Studio respects the explicit empty: it does not fall back to the default.
  • defaultValue is different from defaultValueHint:
    • defaultValue: real value passed to the component until overridden.
    • defaultValueHint: placeholder text inside the form input only, never reaches the component.

Use defaults for palette preview + pre-binding state. Use bindings for real authored content.

Task

  1. Open the registration file at registrationFile and locate the registerComponent({ type: "<componentType>", ... }) call.

  2. List the props that contribute visible structure: text, images, choice variants, link labels, repeated items. Behavioral props (callbacks, ids, analytics tags) generally do not need defaults.

  3. Set per-prop defaultValue matching each prop’s type. Use this template:

    registerComponent({
      type: "Hero",
      component: Hero,
      props: {
        headline: { type: "string",   defaultValue: "Your headline here" },
        subtitle: { type: "string",   defaultValue: "Short description of this section" },
        image:    { type: "imageurl", defaultValue: "/placeholder-hero.png" },
        size:     { type: "choice",   options: ["small", "medium", "large"], defaultValue: ["medium"] },
        cta: {
          type: "object",
          properties: {
            label: { type: "string", defaultValue: "Get started" },
            href:  { type: "href",   defaultValue: "#" },
          },
        },
        features: {
          type: "array",
          items: { type: "string", defaultValue: "Feature item" },
          defaultValue: ["First feature", "Second feature", "Third feature"],
        },
      },
    });

    Rules by type:

    • choice default shape follows multiSelect: single-select takes the bare value (defaultValue: "medium"), multi-select takes an array (defaultValue: ["medium"]). A one-element array on single-select still works, but only for backward compatibility.
    • object parents do not take their own defaultValue: each child prop owns its default.
    • array props need two defaults: outer defaultValue (initial items the canvas shows) and items.defaultValue (each new row added later).
    • imageurl defaults must resolve: ship the placeholder asset alongside the component bundle at placeholderAssetPath.
  4. Use placeholder phrasing, not marketing copy. Defaults ship as-is if the author forgets to bind.

    • Good: "Your headline here", "Short description of this section", "Feature item".
    • Bad: "Buy our amazing product today!".
  5. Default choice props to the safest / most common variant (size: ["medium"], tone: ["neutral"]), not the rarest.

  6. Decide between defaultValue and defaultValueHint per prop. If you want guidance text in the form input only (and a blank canvas is fine for that prop) use the hint instead:

    title: {
      type: "string",
      defaultValueHint: "e.g. Welcome to our site",
    }

    Reach for defaultValue when the canvas must not render blank. Reach for defaultValueHint when blank is fine but the form needs a nudge.

  7. For complex components (heroes, cards with many regions) pre-populate a coherent stub set across every visible region so the dropped component reads as a recognizable shape, not a half-rendered skeleton.

  8. Ship any referenced placeholder assets. If a defaultValue points at /placeholder-hero.png, add that file to the project’s public/ (or framework equivalent) and confirm it loads. A broken image is worse than no image.

Defaults vs Binding: When Each Applies

SituationWhat renders
Prop unbound, no author inputdefaultValue
Prop bound to template.title / contentstack.* / repeater.<field>Bound value (default ignored)
Author cleared the field (empty string / null)The empty value: Studio does NOT fall back to default
validate rejects every valueDefault still renders, but a validation error shows in the form

Inputs Needed From the User

In this order. If any is missing, ask before editing code.

  1. componentType: the type string used at registration.
  2. registrationFile: path to the file calling registerComponent.
  3. placeholderAssetPath: optional, only required if the component has image props.

Acceptance

This skill succeeds only when ALL of the following are true. If any fails, surface the failure and stop.

  • Every visible prop on the component has either a defaultValue or a deliberate defaultValueHint.
  • choice defaults are arrays (["medium"], not "medium").
  • No object parent has its own defaultValue. Each child prop owns its default.
  • Every array prop has both outer defaultValue and items.defaultValue.
  • No default contains real marketing copy.
  • Every imageurl default resolves (the placeholder file exists in public/).
  • Rebuilding the canvas app, refreshing Studio, and dragging the component onto an empty canvas renders every region with placeholder content, verified by inspecting a PNG screenshot of the canvas iframe (a11y snapshots are opaque to iframe contents).
  • Binding one prop to template.* replaces the default. Unbinding restores the default.

Common Pitfalls

PitfallWhy it bitesFix
choice defaults written as stringsSilently fall back to first option or blankWrap in an array: ["medium"]
defaultValue on an object parentHas no effect. Only child props take defaultsSet defaults on each child prop
Forgetting items.defaultValue on arraysInitial items render. “Add item” produces a blank rowSet both outer and items.defaultValue
Confusing defaultValue with defaultValueHintHints never render on the canvas: component appears blank on dropUse defaultValue when canvas must not render blank
Real marketing copy as defaultsAuthors leave it in place and it ships to productionUse placeholder phrasing like “Your headline here”
Expecting defaults to back-fill after a clearStudio respects explicit emptiesIf a default is needed, don’t let the author clear
Image defaults that 404Broken placeholder is worse than no imageShip the asset alongside the bundle

See Also

  • docs/bring-your-own-components/set-component-default-data.md: full reference.
  • Pair with register-studio-component when you’re introducing the component for the first time.
  • Pair with bind-prop-to-template once the component renders and the author is ready to wire real data.
  • wire-slot-data: when the values a child needs come from the component that owns its slot, not from a static default.