Skip to main content

Documentation Index

Fetch the complete documentation index at: https://copylabs.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

Compound Components

This guide covers the compound components pattern used in Radix Themes Native for building flexible, composable UI components.

What are Compound Components?

Compound components are a pattern where components work together to form a complete UI element. They share state implicitly through React Context and provide a flexible API for composition.

Real Examples from Radix Themes Native

Dialog

The Dialog component uses compound components for its structure:
import { Dialog, Button } from 'radix-native-ui';

<Dialog.Root>
  <Dialog.Trigger asChild>
    <Button>Open Dialog</Button>
  </Dialog.Trigger>
  <Dialog.Portal>
    <Dialog.Overlay />
    <Dialog.Content>
      <Dialog.Title>Dialog Title</Dialog.Title>
      <Dialog.Description>
        This is the dialog description text.
      </Dialog.Description>
      <Box direction="row" gap={2}>
        <Dialog.Close asChild>
          <Button variant="soft">Cancel</Button>
        </Dialog.Close>
        <Dialog.Action>Confirm</Dialog.Action>
      </Box>
    </Dialog.Content>
  </Dialog.Portal>
</Dialog.Root>

Popover

The Popover component uses compound components with an arrow:
import { Popover, Button, Text } from 'radix-native-ui';

<Popover.Root>
  <Popover.Trigger asChild>
    <Button>Open Popover</Button>
  </Popover.Trigger>
  <Popover.Portal>
    <Popover.Overlay />
    <Popover.Content>
      <Popover.Arrow />
      <Popover.Title>Popover Title</Popover.Title>
      <Popover.Description>
        This is the popover content.
      </Popover.Description>
      <Popover.Close aria-label="Close">Close</Popover.Close>
    </Popover.Content>
  </Popover.Portal>
</Popover.Root>

Select

The Select component uses compound components for its structure:
import { Select, Button } from 'radix-native-ui';

const [value, setValue] = useState('');

<Select.Root value={value} onValueChange={setValue}>
  <Select.Trigger>
    <Select.Value placeholder="Select an option" />
  </Select.Trigger>
  <Select.Portal>
    <Select.Content>
      <Select.Viewport>
        <Select.Item value="option1">
          <Select.ItemText>Option 1</Select.ItemText>
        </Select.Item>
        <Select.Item value="option2">
          <Select.ItemText>Option 2</Select.ItemText>
        </Select.Item>
        <Select.Separator />
        <Select.Group>
          <Select.Label>Group Label</Select.Label>
          <Select.Item value="option3">
            <Select.ItemText>Option 3</Select.ItemText>
          </Select.Item>
        </Select.Group>
      </Select.Viewport>
    </Select.Content>
  </Select.Portal>
</Select.Root>

Accordion

The Accordion component uses compound components:
import { Accordion, Text } from 'radix-native-ui';

<Accordion.Root>
  <Accordion.Item value="item1">
    <Accordion.Header>
      <Accordion.Trigger>
        <Text>Section 1</Text>
      </Accordion.Trigger>
    </Accordion.Header>
    <Accordion.Content>
      <Text>Content for section 1</Text>
    </Accordion.Content>
  </Accordion.Item>
  <Accordion.Item value="item2">
    <Accordion.Header>
      <Accordion.Trigger>
        <Text>Section 2</Text>
      </Accordion.Trigger>
    </Accordion.Header>
    <Accordion.Content>
      <Text>Content for section 2</Text>
    </Accordion.Content>
  </Accordion.Item>
</Accordion.Root>

Card

The Card component has compound components for its structure:
import { Card, Button, Text } from 'radix-native-ui';

<Card>
  <Card.Content>
    <Text>Card content goes here</Text>
  </Card.Content>
  <Card.Footer>
    <Button>Action</Button>
  </Card.Footer>
</Card>

Callout

The Callout component uses compound components:
import { Callout, Text } from 'radix-native-ui';

<Callout>
  <Callout.Icon>
    <Icon name="info" />
  </Callout.Icon>
  <Callout.Text>
    This is an informational callout
  </Callout.Text>
</Callout>

Compound Component Pattern

How It Works

  1. Root Component - Provides context and state to all children
  2. Child Components - Access context via hooks and render specific parts
  3. Context Sharing - State is shared implicitly through React Context

asChild Prop

Many compound components support the asChild prop, which merges the component’s props onto its single child element instead of rendering a wrapper:
// Without asChild - renders a wrapper
<Dialog.Trigger>
  <Button>Open</Button>
</Dialog.Trigger>

// With asChild - merges props onto the child
<Dialog.Trigger asChild>
  <Button>Open</Button>
</Dialog.Trigger>
This is useful when you want to customize the trigger element while maintaining the component’s behavior.

Benefits

1. Flexibility

Users can compose components in any order:
<Dialog.Content>
  <Dialog.Description>...</Dialog.Description>
  <Dialog.Title>...</Dialog.Title>
</Dialog.Content>

2. Separation of Concerns

Each component handles its own rendering:
// Dialog.Title only handles title styling
<Dialog.Title>Title Text</Dialog.Title>

// Dialog.Description only handles description styling
<Dialog.Description>Description Text</Dialog.Description>

3. Implicit State Sharing

Child components share state through context:
// Trigger automatically knows about open state
<Dialog.Trigger>Open</Dialog.Trigger>

// Content automatically receives open state
<Dialog.Content>Content</Dialog.Content>

4. Custom Composition

Users can customize with the asChild prop:
<Dialog.Trigger asChild>
  <Pressable onPress={customHandler}>
    <Icon name="menu" />
  </Pressable>
</Dialog.Trigger>

Best Practices

1. Use Context for Shared State

Components share state through React Context:
// Good - Share state through context
<Dialog.Root>
  <Dialog.Trigger>...</Dialog.Trigger>
  <Dialog.Content>...</Dialog.Content>
</Dialog.Root>

2. Provide Sensible Defaults

Components come with default values:
// Default size is 2
<Card>...</Card>

// Override with custom size
<Card size={3}>...</Card>

3. Use asChild for Custom Elements

When you need custom elements, use asChild:
// Merge props onto custom element
<Dialog.Trigger asChild>
  <CustomButton>Open</CustomButton>
</Dialog.Trigger>

4. Compose Components Freely

Components can be composed in many ways:
<Popover.Content>
  <Box padding={3}>
    <Popover.Title>Title</Popover.Title>
    <Popover.Description>Description</Popover.Description>
  </Box>
  <Popover.Close>Close</Popover.Close>
</Popover.Content>