Component Architecture
Matrix UI components are built with consistency, accessibility, and developer experience in mind.
Core Principles
1. Radix UI Foundation
Components are built on top of Radix UI primitives, providing:
- Accessibility out of the box (ARIA patterns, keyboard navigation)
- Unstyled, composable primitives
- Focus management and screen reader support
- Consistent behavior across browsers
2. Forward Ref Pattern
All components use React.forwardRef
for proper ref forwarding:
const Component = React.forwardRef<HTMLElementType, ComponentProps>( ({ className, ...props }, ref) => { return ( <element ref={ref} className={cn(baseStyles, className)} {...props} /> ) } ) Component.displayName = 'Component'
3. Variant System
Components use class-variance-authority
for variant management:
import { cva } from 'class-variance-authority' export const buttonVariants = cva( 'base-styles', { variants: { variant: { default: 'variant-default-styles', secondary: 'variant-secondary-styles', }, size: { default: 'size-default-styles', sm: 'size-sm-styles', }, }, defaultVariants: { variant: 'default', size: 'default', }, } )
Component Structure
File Organization
Each component follows a consistent file structure:
components/ └── src/ └── ui/ └── button/ ├── button.tsx # Main component ├── button.types.ts # TypeScript types ├── button.variants.ts # CVA variants └── button.test.tsx # Component tests
Type Definitions
Components maintain strict typing with exported interfaces:
// button.types.ts import { type VariantProps } from 'class-variance-authority' import { buttonVariants } from './button.variants' export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> { asChild?: boolean loading?: boolean leftIcon?: React.ReactNode rightIcon?: React.ReactNode fullWidth?: boolean }
Compound Components
Complex components use the compound component pattern for flexibility:
// Card compound component const Card = React.forwardRef<HTMLDivElement, CardProps>( ({ className, ...props }, ref) => ( <div ref={ref} className={cn('card-styles', className)} {...props} /> ) ) const CardHeader = React.forwardRef<HTMLDivElement, CardHeaderProps>( ({ className, ...props }, ref) => ( <div ref={ref} className={cn('header-styles', className)} {...props} /> ) ) // Export as namespace Card.Header = CardHeader Card.Title = CardTitle Card.Description = CardDescription Card.Content = CardContent Card.Footer = CardFooter export { Card }
Usage Example
<Card> <Card.Header> <Card.Title>Card Title</Card.Title> <Card.Description>Card description</Card.Description> </Card.Header> <Card.Content> Content goes here </Card.Content> <Card.Footer> <Button>Action</Button> </Card.Footer> </Card>
Polymorphic Components
The asChild
prop enables polymorphic behavior:
import { Slot } from '@radix-ui/react-slot' const Button = React.forwardRef<HTMLButtonElement, ButtonProps>( ({ asChild = false, ...props }, ref) => { const Comp = asChild ? Slot : 'button' return <Comp ref={ref} {...props} /> } ) // Usage as different elements <Button asChild> <Link href="/home">Home</Link> </Button> <Button asChild> <span>Styled span</span> </Button>
Styling Architecture
Tailwind CSS Integration
Components use Tailwind CSS with a utility-first approach:
- Base styles defined with Tailwind utilities
- Theme-aware color tokens (foreground, background, etc.)
- Responsive modifiers supported
- Dark mode support via CSS variables
Class Name Merging
The cn
utility ensures proper class merging:
import { clsx, type ClassValue } from 'clsx' import { twMerge } from 'tailwind-merge' export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) } // Usage in components <div className={cn( 'base-styles', variant === 'primary' && 'primary-styles', size === 'lg' && 'large-styles', className // User-provided classes override defaults )} />
Props Composition
Props Spreading Pattern
Components spread native HTML props while extracting custom props:
const Component = React.forwardRef<HTMLDivElement, ComponentProps>( ({ // Custom props variant, size, loading, // Native props className, children, ...props // Remaining HTML attributes }, ref) => { return ( <div ref={ref} className={cn(/* styles */)} aria-busy={loading} {...props} // Spread remaining props > {children} </div> ) } )
Event Handler Composition
Components compose event handlers with user-provided ones:
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => { // Internal logic if (loading) { event.preventDefault() return } // Call user's onClick if provided props.onClick?.(event) } return <button onClick={handleClick} {...otherProps} />
Testing Strategy
- Unit tests for all components using React Testing Library
- Accessibility testing with jest-axe
- Visual regression testing considerations
- Interaction testing for complex components
Performance Optimizations
- Tree-shakeable exports for optimal bundle size
- Lazy loading for heavy components
- Memoization where appropriate
- CSS-in-JS avoided for runtime performance
Best Practices
- Always forward refs for DOM access
- Export component types alongside components
- Maintain consistent prop naming across components
- Document complex props with JSDoc comments
- Use semantic HTML elements
- Provide sensible defaults for optional props
- Keep components focused and single-purpose