# Integration of Radix UI Primitives with OpenCut's Custom UI Components: Implementation Guide

> Learn how to integrate Radix UI primitives with OpenCut's custom UI components. Discover how OpenCut enforces consistent styling and accessibility in this implementation guide.

- Repository: [OpenCut.app/OpenCut](https://github.com/OpenCut-app/OpenCut)
- Tags: how-to-guide
- Published: 2026-06-23

---

**OpenCut integrates Radix UI primitives by wrapping them in custom React components located in `apps/web/src/components/ui/` to enforce consistent styling via the `cn` utility while preserving Radix's accessibility features.**

OpenCut's web application leverages a sophisticated component architecture that combines the accessibility-first primitives of Radix UI with opinionated styling. The integration of Radix UI primitives with OpenCut's custom UI components follows a wrapper-pattern strategy, enabling developers to maintain standards-compliant form behaviors while applying project-specific design tokens throughout the `apps/web` codebase.

## Architecture of the Radix UI Integration

The integration relies on a **wrapper-primitive pattern** where low-level Radix UI components serve as the semantic foundation, and OpenCut's custom wrappers handle visual presentation and context management.

This approach provides three distinct advantages:

- **Accessibility preservation**: Radix handles focus management, ARIA attributes, and keyboard interactions without additional implementation.
- **Design system enforcement**: All markup routes through OpenCut's wrappers, ensuring the `cn` utility applies consistent Tailwind class compositions.
- **Implementation flexibility**: Underlying primitives can be swapped by updating wrappers rather than refactoring individual components across the application.

## Core Integration Files

The primary integration occurs across three key files that bridge Radix primitives with OpenCut's component API.

### form.tsx: The Central Integration Point

Located at [`apps/web/src/components/ui/form.tsx`](https://github.com/OpenCut-app/OpenCut/blob/main/apps/web/src/components/ui/form.tsx), this file imports Radix primitives and re-exports them as higher-level OpenCut components. The file handles the marriage between **Radix UI** primitives and **react-hook-form** context.

Key imports include:

```typescript
import type { Label as LabelPrimitive } from "radix-ui"
import { Slot } from "radix-ui"

```

These primitives are then wrapped to create the `FormLabel`, `FormControl`, `FormDescription`, and `FormMessage` components that comprise OpenCut's form API.

### label.tsx: The Styled Abstraction

The `Label` component in [`apps/web/src/components/ui/label.tsx`](https://github.com/OpenCut-app/OpenCut/blob/main/apps/web/src/components/ui/label.tsx) provides a thin wrapper around the native HTML `<label>` element. Unlike the Radix primitive, this component focuses exclusively on applying project-wide styling through the `cn` helper:

```typescript
// Simplified structure showing the styling wrapper pattern
function Label({ className, ...props }: React.LabelHTMLAttributes<HTMLLabelElement>) {
  return (
    <label 
      className={cn("text-sm font-medium leading-none", className)} 
      {...props} 
    />
  )
}

```

This design separates concerns: Radix provides the semantic behavior, while OpenCut's `Label` handles the visual language.

### utils.ts: Class Name Composition

The `cn` utility imported from [`apps/web/src/lib/utils.ts`](https://github.com/OpenCut-app/OpenCut/blob/main/apps/web/src/lib/utils.ts) serves as the styling backbone for all UI wrappers. It merges Tailwind class strings using `clsx` and `tailwind-merge`, ensuring that style overrides propagate correctly through the component tree without conflicting with Radix's default behaviors.

## How Form Components Wrap Radix Primitives

The [`form.tsx`](https://github.com/OpenCut-app/OpenCut/blob/main/form.tsx) file implements specific wrapping strategies for different Radix primitives, each serving distinct accessibility and functional roles.

### FormLabel: Wrapping LabelPrimitive.Root

Lines 88-101 of [`form.tsx`](https://github.com/OpenCut-app/OpenCut/blob/main/form.tsx) implement `FormLabel` as a wrapper around `LabelPrimitive.Root`:

```typescript
function FormLabel({ 
  className, 
  ...props 
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
  const { error, formItemId } = useFormField()
  
  return (
    <Label
      className={cn(error && "text-destructive", className)}
      htmlFor={formItemId}
      {...props}
    />
  )
}

```

This implementation preserves Radix's label semantics—including proper association with form controls via `htmlFor`—while injecting OpenCut's error-state styling and the `formItemId` from react-hook-form context.

### FormControl: Leveraging Slot.Root

Lines 105-121 utilize **Radix's `Slot.Root`** component to create a polymorphic control wrapper:

```typescript
function FormControl({ 
  ...props 
}: React.ComponentProps<typeof Slot.Root>) {
  const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
  
  return (
    <Slot.Root
      id={formItemId}
      aria-describedby={!error ? formDescriptionId : `${formDescriptionId} ${formMessageId}`}
      aria-invalid={!!error}
      {...props}
    />
  )
}

```

The `Slot` primitive enables `FormControl` to accept any child component (inputs, selects, custom components) while automatically injecting the appropriate ARIA attributes for validation feedback and description linkage.

### FormDescription and FormMessage

Lines 124-136 and 137-154 implement description and error message components that follow Radix's slot conventions. These render as standard `<p>` elements but receive ID linkage from the form field context:

- **FormDescription**: Receives `formDescriptionId` for linking to controls via `aria-describedby`
- **FormMessage**: Conditionally renders only when validation errors exist, using `formMessageId` for error announcement

## Practical Implementation Examples

### Basic Form Using OpenCut Wrappers

The following example demonstrates the complete integration pattern in practice:

```tsx
import { 
  Form, 
  FormItem, 
  FormLabel, 
  FormControl, 
  FormDescription, 
  FormMessage, 
  FormField 
} from "#/components/ui/form"
import { Input } from "#/components/ui/input"
import { useForm } from "react-hook-form"

function MyForm() {
  const methods = useForm()
  
  return (
    <Form {...methods}>
      <FormField
        name="email"
        render={({ field }) => (
          <FormItem>
            <FormLabel htmlFor="email">Email</FormLabel>
            <FormControl asChild>
              <Input id="email" placeholder="you@example.com" {...field} />
            </FormControl>
            <FormDescription>
              We’ll never share your email.
            </FormDescription>
            <FormMessage />
          </FormItem>
        )}
      />
    </Form>
  )
}

```

Under the hood, this markup triggers the following Radix integration:

1. `FormLabel` renders `LabelPrimitive.Root` with `htmlFor` forwarded to the native label
2. `FormControl` wraps the `Input` inside `Slot.Root`, automatically adding `aria-describedby` and `aria-invalid` based on form state
3. Validation messages appear in `FormMessage`, linked via the generated `formMessageId`

### Extending Custom Components with Radix Slots

Custom components like a `DatePicker` can participate in the form system without additional wiring:

```tsx
<FormItem>
  <FormLabel htmlFor="date">Pick a date</FormLabel>
  <FormControl asChild>
    <DatePicker id="date" />
  </FormControl>
  <FormMessage />
</FormItem>

```

Because `FormControl` uses `Slot.Root`, the `DatePicker` receives all necessary ARIA attributes—including `aria-invalid` on validation errors—without explicit prop drilling.

## Summary

- **OpenCut's component architecture** wraps Radix UI primitives in [`apps/web/src/components/ui/form.tsx`](https://github.com/OpenCut-app/OpenCut/blob/main/apps/web/src/components/ui/form.tsx) to combine accessibility compliance with custom styling.
- **The `FormLabel` component** (lines 88-101) wraps `LabelPrimitive.Root` to preserve semantic label associations while injecting `cn`-based styling.
- **The `FormControl` component** (lines 105-121) uses `Slot.Root` to enable polymorphic form controls with automatic ARIA attribute injection.
- **The `Label` component** in [`label.tsx`](https://github.com/OpenCut-app/OpenCut/blob/main/label.tsx) handles visual presentation separately from Radix's semantic primitives, enabling design system consistency.
- **This wrapper-primitive pattern** allows developers to swap underlying implementations or update styling globally without refactoring individual form instances.

## Frequently Asked Questions

### How does OpenCut maintain Radix UI's accessibility while applying custom styles?

OpenCut preserves Radix's accessibility by importing the raw primitives (such as `LabelPrimitive.Root` and `Slot.Root`) and forwarding all semantic props and ARIA attributes to the underlying Radix components. The custom wrappers only intercept the `className` prop to inject Tailwind styles via the `cn` utility, ensuring that focus management, keyboard navigation, and screen-reader announcements remain intact according to Radix's standards-compliant implementation.

### Can I use non-standard components inside OpenCut's FormControl?

Yes. Because `FormControl` in [`form.tsx`](https://github.com/OpenCut-app/OpenCut/blob/main/form.tsx) utilizes Radix's `Slot.Root` component (lines 105-121), it can wrap any valid React element. The `Slot` primitive clones the child element and merges the ARIA attributes (`aria-describedby`, `aria-invalid`, `id`) directly onto it. This means custom components—including third-party date pickers or rich text editors—receive proper accessibility linkage without requiring explicit prop support or wrapper divs.

### Where does the form validation state connect to the Radix primitives?

The validation state integration occurs within [`form.tsx`](https://github.com/OpenCut-app/OpenCut/blob/main/form.tsx) through the `useFormField` hook, which accesses react-hook-form's context. When rendering `FormLabel`, the component checks the `error` state to conditionally apply error styling (line 94: `cn(error && "text-destructive", className)`). Similarly, `FormControl` passes `aria-invalid={!!error}` to the `Slot.Root` component, ensuring that Radix's accessibility tree correctly reflects validation errors for assistive technologies.

### What is the purpose of the `cn` utility in the Radix integration?

The `cn` utility, imported from [`apps/web/src/lib/utils.ts`](https://github.com/OpenCut-app/OpenCut/blob/main/apps/web/src/lib/utils.ts), merges Tailwind CSS class strings while resolving conflicts. In the context of Radix integration, it allows OpenCut's wrappers to apply project-specific design tokens (such as spacing, colors, and typography) to Radix primitives without overriding the functional classes Radix might inject. This utility enables the composition pattern where base styles, variant styles, and consumer-provided `className` props merge predictably.