Placement
A windo renders inside a resizable canvas frame, not a tight box around your component. placement decides where the component sits in that frame — pinned to a corner, stretched edge to edge, or floating in the middle — so you can see how it behaves at the size and position it will actually occupy.
Why placement exists
The canvas frame is bigger than most components and you can drag it to any size. That leaves a question every preview has to answer: where does the component go inside all that space? A toast belongs in a corner. A sticky header belongs flush against the top. A modal floats dead center. A layout shell wants the whole frame.
placement is how the windo states that intent once, in the definition, instead of you eyeballing it. The component renders where it would live in a real app, and as you resize the frame it stays anchored — a bottom-right toast tracks the corner, a fill panel grows with the frame.
export default windo<ToastProps>(() => ({ title: 'Toast', group: 'feedback', placement: 'bottom-right', defaultProps: { message: 'Saved your changes' }, component: (props) => <Toast {...props} />, }))
placement is optional. Omit it and the component renders center — the safe default for self-contained components that don't care about the frame edges.
The two halves of a placement value
A placement is an anchor and an optional -padding suffix.
The anchor decides position: a side, a corner, the center, or fill to stretch across the whole frame. By default the component sits flush against that anchor — top hugs the top edge with no gap, exactly like a real sticky header would. Append -padding to inset the component from the frame edges so it breathes: top-padding is the same top anchor with space around it.
Pick flush when the edge contact is the point (headers, edge-docked panels, full-bleed layouts). Pick -padding when you're previewing a component that normally lives with margin around it.
Every value
Ten base anchors, each available flush or with the -padding suffix — twenty placements in all, exposed as WINDO_PLACEMENTS.
| Anchor | Flush / padded | Where it sits |
|---|---|---|
| center | center · center-padding | Floating in the middle of the frame (default). |
| fill | fill · fill-padding | Stretches to occupy the entire frame. |
| top | top · top-padding | Pinned to the top edge, horizontally centered. |
| bottom | bottom · bottom-padding | Pinned to the bottom edge, horizontally centered. |
| left | left · left-padding | Pinned to the left edge, vertically centered. |
| right | right · right-padding | Pinned to the right edge, vertically centered. |
| top-left | top-left · top-left-padding | Anchored to the top-left corner. |
| top-right | top-right · top-right-padding | Anchored to the top-right corner. |
| bottom-left | bottom-left · bottom-left-padding | Anchored to the bottom-left corner. |
| bottom-right | bottom-right · bottom-right-padding | Anchored to the bottom-right corner. |
Placement that reacts to the canvas
A fixed anchor assumes the component sits the same way everywhere. Real components don't: a panel that floats center on desktop usually wants to fill the screen on mobile. So placement also accepts a function of the live ctx — read whatever you need off the canvas environment and return a WindoPlacement.
export default windo<PanelProps>(() => ({ title: 'Panel', placement: (ctx) => (ctx.viewport.name === 'mobile' ? 'fill' : 'center'), component: (props) => <Panel {...props} />, }))
The function receives WindoRenderContext — the same live environment your component gets — so the placement can hang off ctx.viewport, ctx.colorScheme, or anything in the shared ctx.ctxState. Keep the resolver a pure read of ctx: it runs during render, so calling setState, setCtxState, or setColorScheme from inside it does nothing.
The reason to reach for the function form is timing. Of the context-aware definition fields, placement re-resolves on every render — it tracks ctx live. Resize the frame down to the mobile viewport and the panel above switches to fill the instant the viewport crosses over; widen it again and it snaps back to center. No reselect, no remount. The same goes for flipping the color scheme or writing to ctxState — any change to ctx re-runs the resolver and re-anchors the component.
That live behavior is specific to placement. The other context-aware fields (props, variants, code) snapshot ctx once when the component is selected and don't re-resolve afterward — only placement follows the canvas as it changes.
Where next
- How it works — the iframe runtime that mounts your component into the canvas frame.
- Writing a windo — assemble placement, props, and the rest of the definition into a working file.