Component Patterns
Tosui components fall into two categories: simple components that accept Box props for flexible styling, and complex components with their own focused APIs.
Simple Components
These components accept most Box props, letting you customize spacing, colors, layout, and more:
Layout
| Component | Description |
|---|---|
| Stack | Flex container with direction control |
| HStack | Horizontal stack (row) |
| VStack | Vertical stack (column) |
| Flex | Flexbox container |
| Grid | CSS Grid container |
| Container | Centered max-width container |
| Spacer | Flexible space filler |
| Divider | Visual separator |
Typography
| Component | Description |
|---|---|
| Text | Body text with size/weight props |
| Heading | Semantic headings (h1-h6) |
Forms
| Component | Description |
|---|---|
| Input | Text input field |
| Textarea | Multi-line text input |
| Checkbox | Checkbox with label |
| Radio | Radio button group |
| Switch | Toggle switch |
Form components accept layout props (margin, sizing) but have their own styling for the input itself.
Actions
| Component | Description |
|---|---|
| Button | Action button |
| IconButton | Icon-only button |
| Link | Styled anchor |
Using Box Props
Simple components accept layout, spacing, and some visual props:
// Text with margin and custom color
<Text mb={4} color="foreground-muted">
Description text
</Text>
// Button with custom margin
<Button mt={6}>Submit</Button>
// Input with full width
<Input w="100%" />
// Stack with responsive gap
<VStack gap={{ base: 4, md: 8 }}>
<Text>Item 1</Text>
<Text>Item 2</Text>
</VStack>
Button and IconButton Props
Button and IconButton accept these specific props:
| Prop | Values | Default |
|---|---|---|
variant | "solid", "outline", "ghost" | "solid" |
size | "sm", "md", "lg" | "md" |
colorScheme | "primary", "accent", "success", "warning", "error", "info" | "primary" |
disabled | boolean | false |
loading | boolean | false |
Button also accepts:
fullWidth- Makes button 100% widthleftIcon/rightIcon- Icon elements to display
IconButton requires:
icon- The icon element to displayaria-label- Required accessibility label
// Button variants
<Button variant="solid">Solid</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
// Button sizes
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>
// Color schemes
<Button colorScheme="primary">Primary</Button>
<Button colorScheme="error">Delete</Button>
// With icons
<Button leftIcon={<PlusIcon />}>Add Item</Button>
// IconButton
<IconButton icon={<SearchIcon />} aria-label="Search" />
Button and IconButton have built-in state styling (hover, active, focus, disabled). See State Styling for details.
Complex Components
These components have focused APIs designed for their specific use case. They don't expose Box props directly but may have subcomponents that do.
Overlays
| Component | Description |
|---|---|
| Modal | Dialog overlay |
| Popover | Click-triggered floating panel |
| Tooltip | Hover information overlay |
Containers
| Component | Description |
|---|---|
| Accordion | Collapsible sections |
| Menu | Dropdown menu |
| Tabs | Tabbed content |
| Card | Content container |
Data Display
| Component | Description |
|---|---|
| Alert | Status messages |
| Avatar | User/entity image |
| Badge | Status indicator |
| Skeleton | Loading placeholder |
| Progress | Progress indicator |
Working with Complex Components
Complex components accept specific props for their behavior:
// Modal - controlled open state
<Modal isOpen={isOpen} onClose={onClose} size="lg">
<ModalHeader>Title</ModalHeader>
<ModalBody>Content</ModalBody>
</Modal>
// Accordion - expansion control
<Accordion allowMultiple defaultIndex={[0]}>
<AccordionItem index={0} title="Section 1">
Content
</AccordionItem>
</Accordion>
// Tabs - index-based selection
<Tabs defaultIndex={0} onChange={(index) => console.log(index)}>
<TabList>
<Tab index={0}>Tab 1</Tab>
<Tab index={1}>Tab 2</Tab>
</TabList>
<TabPanels>
<TabPanel index={0}>Content 1</TabPanel>
<TabPanel index={1}>Content 2</TabPanel>
</TabPanels>
</Tabs>
Subcomponents
Many complex components have subcomponents with built-in styling:
Modal:
ModalHeader- Title with bottom borderModalBody- Content area with paddingModalFooter- Actions area with top border
Card:
CardHeader- Title areaCardBody- Content with paddingCardFooter- Actions area
Accordion:
AccordionItem- Individual collapsible section
Tabs:
TabList- Container for tab buttonsTab- Individual tab buttonTabPanels- Container for tab contentTabPanel- Individual tab content
// Subcomponents have built-in padding and styling
<Card>
<CardHeader>
<Heading fontSize="lg">Card Title</Heading>
</CardHeader>
<CardBody>
Content with padding
</CardBody>
<CardFooter>
<Button>Action</Button>
</CardFooter>
</Card>
To add custom styles to subcomponents, use className:
<ModalBody className="custom-body-class">
Custom styled body
</ModalBody>
Polymorphic Components
Some simple components support the as prop to change the rendered HTML element:
// Box as different elements
<Box as="section">Section content</Box>
<Box as="article">Article content</Box>
<Box as="nav">Navigation</Box>
// Text as different elements
<Text as="label">Form label</Text>
<Text as="span">Inline text</Text>
// Heading levels
<Heading as="h1">Page Title</Heading>
<Heading as="h2">Section Title</Heading>
TypeScript will correctly infer the allowed attributes based on the element type.
When to Use Each Pattern
Use simple components when:
- You need custom spacing or layout
- You're building custom UI patterns
- You need responsive styling
Use complex components when:
- You need specific behavior (modals, accordions, menus)
- The component handles interactions for you
- You want consistent patterns across your app
Combine both:
<Card>
<CardBody>
{/* Simple components inside complex component */}
<VStack gap={4}>
<Heading fontSize="lg">Card Title</Heading>
<Text color="foreground-muted">Description</Text>
<Button>Action</Button>
</VStack>
</CardBody>
</Card>