> ## Documentation Index
> Fetch the complete documentation index at: https://docs.metabind.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Layout — Stacks

> Components for arranging children vertically, horizontally, or layered front-to-back

export const PlatformStatuses = ({statuses}) => {
  const StatusBadge = ({status, label}) => {
    const styles = {
      green: {
        backgroundColor: '#dcfce7',
        color: '#166534'
      },
      orange: {
        backgroundColor: '#fed7aa',
        color: '#9a3412'
      },
      red: {
        backgroundColor: '#fecaca',
        color: '#991b1b'
      },
      gray: {
        backgroundColor: '#f3f4f6',
        color: '#4b5563'
      }
    };
    const baseStyle = {
      display: 'inline-flex',
      alignItems: 'center',
      padding: '0.125rem 0.625rem',
      borderRadius: '9999px',
      fontSize: '0.875rem',
      fontWeight: '500'
    };
    const colorStyle = styles[status] || styles.green;
    return <span style={{
      ...baseStyle,
      ...colorStyle
    }}>
        {label || status}
      </span>;
  };
  const STATUS_CONFIG = {
    supported: {
      label: "Supported",
      color: "green"
    },
    partial: {
      label: "Partial",
      color: "orange"
    },
    "not-implemented": {
      label: "Not Implemented",
      color: "gray"
    }
  };
  const renderCard = (platform, value) => {
    if (!value) return null;
    const {status, note} = typeof value === "string" ? {
      status: value
    } : value;
    const config = STATUS_CONFIG[status];
    if (!config) return null;
    const titleMap = {
      ios: "SwiftUI",
      android: "Jetpack Compose",
      web: "Web"
    };
    return <Card key={platform} title={titleMap[platform] || platform}>
          <StatusBadge status={config.color} label={config.label} />
          {note && <div style={{
      marginTop: '0.5rem',
      fontSize: '0.875rem',
      color: '#6b7280'
    }}>
              {note}
            </div>}
      </Card>;
  };
  if (statuses == null) {
    return null;
  }
  return <Columns cols="3">
      {Object.entries(statuses).map(([platform, value]) => renderCard(platform, value))}
    </Columns>;
};

export const ComposeJS = ({code, name, height}) => {
  const encodedCode = useMemo(() => {
    if (!code) return "";
    try {
      return btoa(code);
    } catch (e) {
      console.error("Failed to encode code", e);
      return "";
    }
  }, [code]);
  if (!encodedCode) {
    return null;
  }
  return <iframe src={`https://www.metabind.ai/embed?code=${encodedCode}&name=${name ?? 'Example'}`} loading="lazy" style={{
    width: "100%",
    height: height || '350px',
    border: "1px solid #e5e7eb",
    borderRadius: "var(--rounded-2xl,1rem)",
    overflow: "hidden"
  }} title="ComposeJS Preview" />;
};

Stacks are the primary layout primitives in BindJS. `VStack`, `HStack`, and `ZStack` arrange children vertically, horizontally, or as overlaid layers. `LazyVStack` and `LazyHStack` are virtualized variants for long scrollable lists. `Group` and `Section` add no layout of their own — they let you apply modifiers across multiple components or attach a header and footer for use inside [List](/bindjs/components/List).

Every stack accepts either `(children)` or `(props, children)`. Alignment types differ by axis: `VStack` and `LazyVStack` use `HorizontalAlignment`, `HStack` and `LazyHStack` use `VerticalAlignment`, and `ZStack` uses [Alignment](/bindjs/types/Alignment).

## VStack

Arranges children in a vertical line from top to bottom.

```typescript theme={null}
VStack(children: Component): Component;
VStack(children: Component[]): Component;
VStack(props: { spacing?: number; alignment?: HorizontalAlignment }, children: Component[]): Component;
```

<ParamField path="children" type="Component | Component[]" required>
  A single component or array of components to arrange vertically.
</ParamField>

<ParamField path="props" type="object" optional>
  Configuration options for the stack layout.

  <Expandable title="properties">
    <ParamField path="spacing" type="number" optional>
      The distance in points between adjacent children. When omitted, the system uses a default spacing.
    </ParamField>

    <ParamField path="alignment" type="HorizontalAlignment" optional>
      The horizontal alignment of children within the stack. One of `"leading"`, `"center"`, or `"trailing"`. Defaults to `"center"`.
    </ParamField>
  </Expandable>
</ParamField>

<PlatformStatuses
  statuses={{
ios: { status: "supported" },
android: { status: "supported" },
web: { status: "supported" },
}}
/>

**Basic vertical stack**

```typescript theme={null}
VStack([
    Text("First"),
    Text("Second"),
    Text("Third")
])
```

**With spacing and alignment**

```typescript theme={null}
VStack({ spacing: 16, alignment: "leading" }, [
    Text("Title").font("headline"),
    Text("Subtitle").foregroundStyle(Color("secondary"))
])
```

**Nested layout**

```typescript theme={null}
VStack({ spacing: 24 }, [
    HStack({ spacing: 12 }, [
        Image({ systemName: "person.fill" }),
        Text("Profile")
    ]),
    Divider(),
    Text("Welcome back!").font("body")
])
```

**Pushing content with spacers**

```typescript theme={null}
VStack([
    Text("Top"),
    Spacer(),
    Text("Bottom")
])
    .frame({ maxHeight: ".infinity" })
```

## HStack

Arranges children in a horizontal line from leading to trailing.

```typescript theme={null}
HStack(children: Component): Component;
HStack(children: Component[]): Component;
HStack(props: { spacing?: number; alignment?: VerticalAlignment }, children: Component[]): Component;
```

<ParamField path="children" type="Component | Component[]" required>
  A single component or array of components to arrange horizontally.
</ParamField>

<ParamField path="props" type="object" optional>
  Configuration options for the stack layout.

  <Expandable title="properties">
    <ParamField path="spacing" type="number" optional>
      The distance in points between adjacent children. When omitted, the system uses a default spacing.
    </ParamField>

    <ParamField path="alignment" type="VerticalAlignment" optional>
      The vertical alignment of children within the stack. One of `"top"`, `"center"`, `"bottom"`, `"firstTextBaseline"`, or `"lastTextBaseline"`. Defaults to `"center"`.
    </ParamField>
  </Expandable>
</ParamField>

<PlatformStatuses
  statuses={{
ios: { status: "supported" },
android: { status: "supported" },
web: { status: "supported" },
}}
/>

**Basic horizontal stack**

```typescript theme={null}
HStack([
    Image({ systemName: "star.fill" }),
    Text("Favorites")
])
```

**Avatar row with vertical alignment**

```typescript theme={null}
HStack({ spacing: 12, alignment: "top" }, [
    Image({ url: "avatar.jpg" })
        .frame({ width: 48, height: 48 })
        .clipShape(Circle()),
    VStack({ alignment: "leading" }, [
        Text("Jane Doe").font("headline"),
        Text("Online").foregroundStyle(Color("green"))
    ])
])
```

**Pushing edges apart**

```typescript theme={null}
HStack([
    Text("Leading"),
    Spacer(),
    Text("Trailing")
])
    .padding(16)
```

**Baseline alignment**

```typescript theme={null}
HStack({ alignment: "firstTextBaseline" }, [
    Text("Title").font("title"),
    Text("subtitle").font("caption")
])
```

## ZStack

Layers children on top of each other in a back-to-front stack. The last child in the array renders on top.

```typescript theme={null}
ZStack(children: Component): Component;
ZStack(children: Component[]): Component;
ZStack(props: { alignment?: Alignment }, children: Component[]): Component;
```

<ParamField path="children" type="Component | Component[]" required>
  A single component or array of components to layer. The last child renders on top.
</ParamField>

<ParamField path="props" type="object" optional>
  Configuration options for the stack.

  <Expandable title="properties">
    <ParamField path="alignment" type="Alignment" optional>
      The alignment of children within the stack. See [Alignment](/bindjs/types/Alignment) for all values. Defaults to `"center"`.
    </ParamField>
  </Expandable>
</ParamField>

<PlatformStatuses
  statuses={{
ios: { status: "supported" },
android: { status: "supported" },
web: { status: "supported" },
}}
/>

**Basic overlay**

```typescript theme={null}
ZStack([
    Image({ url: "background.jpg" }),
    Text("Overlay text")
        .foregroundStyle(Color("white"))
        .font("title")
])
```

**Caption with corner alignment**

```typescript theme={null}
ZStack({ alignment: "bottomTrailing" }, [
    Image({ url: "photo.jpg" })
        .frame({ width: 200, height: 200 }),
    Text("Caption")
        .padding(8)
        .background(Color("black").opacity(0.6))
        .foregroundStyle(Color("white"))
        .cornerRadius(4)
])
```

**Notification badge**

```typescript theme={null}
ZStack({ alignment: "topTrailing" }, [
    Image({ systemName: "bell" })
        .font("title"),
    Circle()
        .fill(Color("red"))
        .frame({ width: 12, height: 12 })
])
```

**Layered backgrounds**

```typescript theme={null}
ZStack([
    Rectangle().fill(Color("blue")),
    Circle()
        .fill(Color("white").opacity(0.3))
        .frame({ width: 150, height: 150 }),
    Text("Center")
        .foregroundStyle(Color("white"))
])
    .frame({ width: 200, height: 200 })
```

## LazyVStack

A vertical stack that renders only the children currently visible on screen. Use inside a [ScrollView](/bindjs/components/ScrollView) for efficient rendering of long lists.

```typescript theme={null}
LazyVStack(children: Component): Component;
LazyVStack(children: Component[]): Component;
LazyVStack(props: { spacing?: number; alignment?: HorizontalAlignment; pinnedViews?: PinnedScrollableViews }, children: Component[]): Component;
```

<ParamField path="children" type="Component | Component[]" required>
  A single component or array of components to arrange vertically with lazy rendering.
</ParamField>

<ParamField path="props" type="object" optional>
  Configuration options for the lazy stack.

  <Expandable title="properties">
    <ParamField path="spacing" type="number" optional>
      The distance in points between adjacent children. When omitted, the system uses a default spacing.
    </ParamField>

    <ParamField path="alignment" type="HorizontalAlignment" optional>
      The horizontal alignment of children within the stack. One of `"leading"`, `"center"`, or `"trailing"`. Defaults to `"center"`.
    </ParamField>

    <ParamField path="pinnedViews" type="PinnedScrollableViews" optional>
      Which views to pin during scrolling. One of `"sectionHeaders"`, `"sectionFooters"`, or `"all"`. When set, [Section](#section) headers and footers stick to the edges of the scroll view while their content is visible.
    </ParamField>
  </Expandable>
</ParamField>

<PlatformStatuses
  statuses={{
ios: { status: "supported" },
android: "not-implemented",
web: { status: "supported" },
}}
/>

**Basic lazy list**

```typescript theme={null}
ScrollView([
    LazyVStack({ spacing: 8 }, [
        ForEach(items, (item) =>
            Text(item.name).padding(12)
        )
    ])
])
```

**Pinned section headers**

```typescript theme={null}
ScrollView([
    LazyVStack({ pinnedViews: "sectionHeaders" }, [
        Section({ header: Text("Group A").font("headline") }, [
            ForEach(groupA, (item) => Text(item.name))
        ]),
        Section({ header: Text("Group B").font("headline") }, [
            ForEach(groupB, (item) => Text(item.name))
        ])
    ])
])
```

**Leading-aligned message list**

```typescript theme={null}
ScrollView([
    LazyVStack({ spacing: 12, alignment: "leading" }, [
        ForEach(messages, (msg) =>
            Text(msg.text)
                .padding(12)
                .background(Color("secondarySystemBackground"))
                .cornerRadius(8)
        )
    ])
])
```

<Note>
  `LazyVStack` only creates views for children currently in the viewport. Always place it inside a [ScrollView](/bindjs/components/ScrollView) — without a scrollable parent there is no viewport boundary to drive the lazy rendering. For static lists, use [VStack](#vstack).
</Note>

## LazyHStack

A horizontal stack that renders only the children currently visible on screen. Use inside a horizontal [ScrollView](/bindjs/components/ScrollView).

```typescript theme={null}
LazyHStack(children: Component): Component;
LazyHStack(children: Component[]): Component;
LazyHStack(props: { spacing?: number; alignment?: VerticalAlignment; pinnedViews?: PinnedScrollableViews }, children: Component[]): Component;
```

<ParamField path="children" type="Component | Component[]" required>
  A single component or array of components to arrange horizontally with lazy rendering.
</ParamField>

<ParamField path="props" type="object" optional>
  Configuration options for the lazy stack.

  <Expandable title="properties">
    <ParamField path="spacing" type="number" optional>
      The distance in points between adjacent children. When omitted, the system uses a default spacing.
    </ParamField>

    <ParamField path="alignment" type="VerticalAlignment" optional>
      The vertical alignment of children within the stack. One of `"top"`, `"center"`, `"bottom"`, `"firstTextBaseline"`, or `"lastTextBaseline"`. Defaults to `"center"`.
    </ParamField>

    <ParamField path="pinnedViews" type="PinnedScrollableViews" optional>
      Which views to pin during scrolling. One of `"sectionHeaders"`, `"sectionFooters"`, or `"all"`. When set, [Section](#section) headers and footers stick to the edges of the scroll view while their content is visible.
    </ParamField>
  </Expandable>
</ParamField>

<PlatformStatuses
  statuses={{
ios: { status: "supported" },
android: "not-implemented",
web: { status: "supported" },
}}
/>

**Horizontal carousel**

```typescript theme={null}
ScrollView({ axis: "horizontal", showsIndicators: false }, [
    LazyHStack({ spacing: 16 }, [
        ForEach(cards, (card) =>
            VStack([
                Image({ url: card.imageUrl })
                    .frame({ width: 200, height: 150 })
                    .cornerRadius(12),
                Text(card.title).font("caption")
            ])
        )
    ])
])
```

**Top-aligned cards**

```typescript theme={null}
ScrollView({ axis: "horizontal" }, [
    LazyHStack({ spacing: 8, alignment: "top" }, [
        ForEach(items, (item) =>
            VStack({ alignment: "leading" }, [
                Text(item.title).font("headline"),
                Text(item.subtitle).font("subheadline")
            ])
                .frame({ width: 120 })
        )
    ])
])
```

<Note>
  `LazyHStack` only creates views for children currently in the viewport. Always place it inside a horizontal [ScrollView](/bindjs/components/ScrollView). For static rows, use [HStack](#hstack).
</Note>

## Group

Groups multiple components without adding any layout. Modifiers applied to a `Group` propagate to each child. A subviews overload lets you decompose an existing component tree and rearrange its children.

```typescript theme={null}
Group(children: Component): Group;
Group(children: Component[]): Group;
Group(subviews: Component, transform: (subviews: Component[]) => Component): Group;
```

<ParamField path="children" type="Component | Component[]" required>
  A single component or array of components to group together.
</ParamField>

<ParamField path="subviews" type="Component" required>
  Subviews overload: a component whose children are decomposed into an array of subviews.
</ParamField>

<ParamField path="transform" type="(subviews: Component[]) => Component" required>
  Subviews overload: a function that receives the subviews array and returns a new component layout.
</ParamField>

<PlatformStatuses
  statuses={{
ios: { status: "supported" },
android: { status: "supported" },
web: { status: "supported" },
}}
/>

**Basic grouping**

```typescript theme={null}
Group([
    Text("First"),
    Text("Second"),
    Text("Third")
])
```

**Apply a modifier to multiple children**

```typescript theme={null}
Group([
    Text("Red text"),
    Text("Also red")
])
    .foregroundStyle(Color("red"))
```

**Conditional rendering**

```typescript theme={null}
const body = () => {
    const [showDetails, setShowDetails] = useState(false)

    return Group(
        showDetails
            ? [Text("Name"), Text("Email")]
            : [Text("Sign in to view")]
    )
}
```

**Subview decomposition**

```typescript theme={null}
Group(
    VStack([Text("A"), Text("B"), Text("C")]),
    (subviews) =>
        HStack([subviews[0], Spacer(), subviews[1]])
)
```

<Note>
  `Group` adds no layout semantics. Children render according to the parent container's layout rules. The subviews overload enables view decomposition — inspecting and rearranging children of an existing component tree.
</Note>

## Section

Groups content with an optional header and footer. Used inside [List](/bindjs/components/List), `Form`, or [LazyVStack](#lazyvstack) with `pinnedViews`.

```typescript theme={null}
Section(children: Component): Component;
Section(children: Component[]): Component;
Section(props: { header?: Component; footer?: Component }, children: Component[]): Component;
```

<ParamField path="children" type="Component | Component[]" required>
  The content of the section.
</ParamField>

<ParamField path="props" type="object" optional>
  Configuration options for the section.

  <Expandable title="properties">
    <ParamField path="header" type="Component" optional>
      A component displayed above the section content. Typically a `Text` with a headline style.
    </ParamField>

    <ParamField path="footer" type="Component" optional>
      A component displayed below the section content. Typically a `Text` with a caption or explanatory note.
    </ParamField>
  </Expandable>
</ParamField>

<PlatformStatuses
  statuses={{
ios: { status: "supported" },
android: { status: "supported" },
web: { status: "supported" },
}}
/>

**Basic section**

```typescript theme={null}
Section([
    Text("Item 1"),
    Text("Item 2"),
    Text("Item 3")
])
```

**Section with header**

```typescript theme={null}
Section({ header: Text("Settings") }, [
    Toggle({ label: "Dark Mode", isOn, setIsOn }),
    Toggle({ label: "Notifications", isOn: notifs, setIsOn: setNotifs })
])
```

**Header and footer**

```typescript theme={null}
Section(
    {
        header: Text("Account"),
        footer: Text("Your email is used for sign-in.")
    },
    [
        Text("user@example.com"),
        Text("Change password")
    ]
)
```

**Section inside a List**

```typescript theme={null}
List([
    Section({ header: Text("Fruits") }, [
        ForEach(fruits, (fruit) => Text(fruit))
    ]),
    Section({ header: Text("Vegetables") }, [
        ForEach(veggies, (veg) => Text(veg))
    ])
])
```

**Sticky headers in a LazyVStack**

```typescript theme={null}
ScrollView([
    LazyVStack({ pinnedViews: "sectionHeaders" }, [
        Section({ header: Text("Section A").font("headline") }, [
            ForEach(sectionAItems, (item) => Text(item.name))
        ])
    ])
])
```

## See also

* [ScrollView](/bindjs/components/ScrollView) — scrollable container for lazy stacks
* [List](/bindjs/components/List) — scrollable list with native row chrome
* [ForEach](/bindjs/components/ForEach) — iterate over data to produce children
* [Spacer](/bindjs/components/Spacer) — flexible space inside stacks
* [Alignment](/bindjs/types/Alignment) — alignment values for `ZStack` and corner-aligned overlays
