Dynamic Blocks

Full React blocks with hooks and state (experimental)

Experimental. For most use cases, static blocks are recommended — they’re faster, automatically adapt to multiple sizes, and require no boilerplate.

Dynamic blocks let you write full React components with hooks, state, and effects. They render in a sandboxed iframe at roughly 400x600px and work on both mobile and web.

Basic UI

The default export defines your block’s interface. Import layout components from start/ui:

import { YStack, SizableText } from 'start/ui'
export default () => (
<YStack p="$4" gap="$2">
<SizableText size="$6">Dashboard</SizableText>
<SizableText color="$color11">Your content here</SizableText>
</YStack>
)

Layout components

start/ui exports layout and text components:

  • XStack — horizontal flex container
  • YStack — vertical flex container
  • ZStack — absolute positioned stack
  • SizableText — text with size scaling (recommended)
  • View, ScrollView, Separator, Spacer, Theme
  • H1H6, Paragraph, Circle

Shorthands

Shorthand props available on all layout components:

ShortProperty
p, px, py, pt, pb, pl, prpadding
m, mx, my, mt, mb, ml, mrmargin
bgbackgroundColor
roundedborderRadius
itemsalignItems
justifyjustifyContent
grow, shrinkflexGrow, flexShrink
selfalignSelf
zzIndex

useData hook

Query workspace data from your UI. Returns live results that update in real time:

import { YStack, SizableText, useData } from 'start/ui'
export default () => {
const [data] = useData({
kindFilter: 'customer',
sortColumn: 'createdAt',
sortDirection: 'desc',
limit: 10,
})
return (
<YStack p="$4" gap="$2">
{data.map((item) => (
<SizableText key={item.id}>{item.title}</SizableText>
))}
</YStack>
)
}

Filter params

ParamTypeDescription
kindFilterstringFilter by kind
statusFilterstringFilter by status
keyFilterstringSearch keys (case-insensitive)
appIdFilterstringFilter by app install
channelIdstringFilter by channel
sortColumnstringColumn to sort by
sortDirectionstring'asc' or 'desc'
limitnumberMax results (may be capped)

The hook returns a [data, actions] tuple where actions includes mutate and delete methods.

useBlockContext hook

Adapt your layout based on how the block is being rendered:

import { useBlockContext } from 'start/ui'
export default () => {
const { renderSize, renderMode } = useBlockContext()
// renderSize: 'mini' | 'medium' | 'large'
// renderMode: 'inline' | 'iframe'
}

Safety

Dynamic blocks have built-in safety: they don’t run when closed, error boundaries catch crashes, and excessive re-renders are throttled.