Component API patterns
Tosui uses three API patterns. The pattern tells you whether a component accepts Box style props, owns a focused set of props, or coordinates several subcomponents.
Polymorphic styled components
Polymorphic styled components build on Box. They accept Box style props, responsive values, state props, and an as prop unless their reference page lists an exception.
| Category | Components |
|---|---|
| Primitives | Box, Text, Heading, Code |
| Layout | Stack, HStack, VStack, Flex, Grid, Container, Divider, Spacer |
| Forms | Button, Input, Textarea, Select, Label |
| Other | Link, Spinner |
<Button mt={6}>Submit</Button>
<Input w="100%" />
<VStack gap={{ base: 4, md: 8 }}>
<Text>First item</Text>
<Text>Second item</Text>
</VStack>
Some components replace a Box prop with a narrower convenience prop. Text and Heading, for example, use size, weight, and align instead of fontSize, fontWeight, and textAlign.
<Text size="sm" weight="medium" align="center" />
<Heading level={2} size="2xl" />
The as prop changes the rendered element and its native TypeScript attributes:
<Box as="section">Section content</Box>
<Text as="p">Paragraph content</Text>
<Button as="a" href="/signup">Create an account</Button>
Focused components
Focused components expose only the props needed for their visual or interaction contract. They do not accept the full Box API.
| Category | Components |
|---|---|
| Form controls | IconButton, Checkbox, Radio, Switch |
| Feedback | Alert, Badge, Progress, Skeleton |
| Data display | Avatar, Image |
| Navigation | Pagination |
Wrap a focused component in Box when you need layout that its API does not expose:
<Box mt={6} display="flex" justifyContent="end">
<Pagination totalPages={12} />
</Box>
Use each component's className prop when you need CSS that cannot be expressed by its public props.
Compound components
Compound components coordinate state or structure across named children.
| Root | Subcomponents |
|---|---|
Accordion | AccordionItem |
Breadcrumb | BreadcrumbItem |
Card | CardHeader, CardBody, CardFooter |
List | ListItem, ListIcon |
Menu | MenuButton, MenuList, MenuItem |
Modal | ModalHeader, ModalBody, ModalFooter |
Popover | PopoverHeader, PopoverBody |
Tabs | TabList, Tab, TabPanel |
Pass the documented subcomponents directly to their root unless a component reference says otherwise.
<Tabs defaultIndex={0}>
<TabList>
<Tab index={0}>Profile</Tab>
<Tab index={1}>Security</Tab>
</TabList>
<TabPanel index={0}>Profile settings</TabPanel>
<TabPanel index={1}>Security settings</TabPanel>
</Tabs>
Tabs does not export a TabPanels wrapper. TabPanel elements are direct children of Tabs.
Composition through context
FormField is a separate composition pattern. Tosui form controls read the field ID, description, required state, disabled state, and invalid state from context. The controls can remain inside layout components.
<FormField label="Email" helperText="We use this for account notices">
<Box>
<Input type="email" />
</Box>
</FormField>
For a native or custom control, use the render-function adapter so that the accessible props reach the focusable element:
<FormField label="Email" isRequired>
{(controlProps) => <input type="email" {...controlProps} />}
</FormField>
See the FormField reference for the complete adapter contract.