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 testsType 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