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
- Root Component - Provides context and state to all children
- Child Components - Access context via hooks and render specific parts
- 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>