Skip to main content

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

ComponentDescription
StackFlex container with direction control
HStackHorizontal stack (row)
VStackVertical stack (column)
FlexFlexbox container
GridCSS Grid container
ContainerCentered max-width container
SpacerFlexible space filler
DividerVisual separator

Typography

ComponentDescription
TextBody text with size/weight props
HeadingSemantic headings (h1-h6)

Forms

ComponentDescription
InputText input field
TextareaMulti-line text input
CheckboxCheckbox with label
RadioRadio button group
SwitchToggle switch

Form components accept layout props (margin, sizing) but have their own styling for the input itself.

Actions

ComponentDescription
ButtonAction button
IconButtonIcon-only button
LinkStyled 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:

PropValuesDefault
variant"solid", "outline", "ghost""solid"
size"sm", "md", "lg""md"
colorScheme"primary", "accent", "success", "warning", "error", "info""primary"
disabledbooleanfalse
loadingbooleanfalse

Button also accepts:

  • fullWidth - Makes button 100% width
  • leftIcon / rightIcon - Icon elements to display

IconButton requires:

  • icon - The icon element to display
  • aria-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

ComponentDescription
ModalDialog overlay
PopoverClick-triggered floating panel
TooltipHover information overlay

Containers

ComponentDescription
AccordionCollapsible sections
MenuDropdown menu
TabsTabbed content
CardContent container

Data Display

ComponentDescription
AlertStatus messages
AvatarUser/entity image
BadgeStatus indicator
SkeletonLoading placeholder
ProgressProgress 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 border
  • ModalBody - Content area with padding
  • ModalFooter - Actions area with top border

Card:

  • CardHeader - Title area
  • CardBody - Content with padding
  • CardFooter - Actions area

Accordion:

  • AccordionItem - Individual collapsible section

Tabs:

  • TabList - Container for tab buttons
  • Tab - Individual tab button
  • TabPanels - Container for tab content
  • TabPanel - 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>