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