windo/API/Render context
API ReferenceObject

Render context

ctx is the live wire into the canvas. Every render-time function a windo declares — component, defaultProps, an action's run and disabled — receives it as its trailing argument, and through it reads the ambient environment (color scheme, viewport, motion, direction, locale), drives component-local state, and reaches the console and opted-in contexts.

Where it comes from

You never construct a WindoRenderContext. The iframe runtime builds one per render from the ambient state the chrome pushes down, then hands the same object to your component(props, ctx), to a function-form defaultProps(ctx), and to each action's run(ctx, active) / disabled(ctx). Read it at render time — never close over it in the factory body, which runs once at definition time before any ctx exists.

TSXbanner.windo.tsx
component: (props, ctx) => (
<Banner
  {...props}
  theme={ctx.colorScheme}
  compact={ctx.viewport.name === 'mobile'}
/>
)

Fields

The state generic threads through: ctx.state and ctx.setState are typed by the windo's State. The rest of the object is fixed.

FieldTypeDescription
colorScheme'light' | 'dark'The active scheme, toggled from the chrome. Branch your component on it instead of reading the host page — the preview is isolated.
viewportWindoViewportThe current frame size: { width, height, name } where name is 'mobile' | 'tablet' | 'desktop'. Use it to drive responsive rendering without a media query.
reducedMotionbooleanTrue when the chrome's reduced-motion toggle is on. Gate animations on it so motion-sensitive previews stay still.
direction'ltr' | 'rtl'Writing direction. Mirror layout and icon orientation off this to preview right-to-left rendering.
localestringThe active locale string (e.g. en-US). Feed it to Intl formatters or your own i18n lookup.
loggerWindoLoggerConsole channel for the preview. logger.log(...args) posts an entry to the chrome's Console tab — the windo runs in an iframe, so this is how output surfaces.
stateStateCurrent component-local state, seeded from the windo's state field and typed by the State generic. Read-only here — mutate through setState.
setState(patch: Partial<State>) => voidMerges a partial patch into the component-local state and re-renders. This is how actions and the component itself advance state.
contextsRecord<string, unknown>Resolved values of the contexts this windo opted into via uses, keyed by context name. Each value is whatever that context resolved to (its resolve return, or its control values by default).

colorScheme, viewport, reducedMotion, direction, locale

These five are the ambient environment — the values the chrome's toolbar controls. They arrive read-only on every render, so the way to respond is to read them in component and branch. Because the preview lives in an isolated iframe, these are the source of truth for the environment; don't reach for the host document's theme or matchMedia.

TSXprice.windo.tsx
component: (props, ctx) => {
const price = new Intl.NumberFormat(ctx.locale, {
  style: 'currency',
  currency: 'USD',
}).format(props.amount)

return (
  <Tag
    dir={ctx.direction}
    animate={!ctx.reducedMotion}
    compact={ctx.viewport.name !== 'desktop'}
  >
    {price}
  </Tag>
)
}

logger

The preview renders inside an iframe, so a bare console.log lands in the iframe's own console, not the chrome. ctx.logger.log(...args) bridges that gap — each call posts a WindoLogEntry to the chrome's Console tab, where you read it alongside the canvas. Reach for it to trace renders, action fires, and prop changes.

TSXselect.windo.tsx
component: (props, ctx) => (
<Select
  {...props}
  onChange={value => {
    ctx.logger.log('selected', value)
    ctx.setState({ value })
  }}
/>
)

state and setState

ctx.state is the windo's component-local state — the same shape you declared in the state field, typed by the State generic. It is read-only on the ctx; to advance it, call ctx.setState(patch), which merges the partial patch and re-renders. This pair is what makes a windo interactive: the component reads ctx.state to render, and both the component and the windo's actions call ctx.setState to move it forward.

TSXtoast.windo.tsx
state: { open: false },
actions: [
{ label: 'Show', run: ctx => ctx.setState({ open: true }) },
{ label: 'Hide', run: ctx => ctx.setState({ open: false }), disabled: ctx => !ctx.state.open },
],
component: (props, ctx) => <Toast {...props} open={ctx.state.open} />,

contexts

When a windo opts into a provider context via uses, the resolved value of each one shows up on ctx.contexts, keyed by context name. The value is whatever that context's resolve returned — or, when it has none, its raw control values. It is typed unknown, so narrow it before use.

TSXcard.windo.tsx
uses: ['theme'],
component: (props, ctx) => {
const theme = ctx.contexts.theme as { accent: string }
return <Card {...props} accent={theme.accent} />
}