shadcn/ui
Connect FormAdapter to the shadcn/ui components already owned by your app.
The shadcn adapter renders your generated components, preserving their source changes and styling. FormAdapter supplies form state, accessibility, and validation props through a small contract containing only the button variants and other props it renders.
Install the components
Initialize shadcn/ui with the primitive library you use, then add the components FormAdapter needs.
bunx shadcn@latest init -d --base base
# Or: bunx shadcn@latest init -d --base radix
bunx shadcn@latest add alert button checkbox field input native-select progress radio-group spinner textarea
bun add @formadapter/react @formadapter/shadcn zodThere is no FormAdapter CSS import. Keep the global styles and theme generated by shadcn/ui.
Connect your components
Use the entry point that matches components.json:
"use client";
import { createShadcn } from "@formadapter/shadcn/baseui";
// Radix UI: import { createShadcn } from "@formadapter/shadcn/radix";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import {
Field, FieldContent, FieldDescription, FieldError, FieldGroup,
FieldLabel, FieldLegend, FieldSet, FieldTitle,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { NativeSelect, NativeSelectOption } from "@/components/ui/native-select";
import { Progress } from "@/components/ui/progress";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Spinner } from "@/components/ui/spinner";
import { Textarea } from "@/components/ui/textarea";
export const shadcn = createShadcn({
Alert, AlertDescription, AlertTitle, Button, Checkbox,
Field, FieldContent, FieldDescription, FieldError, FieldGroup,
FieldLabel, FieldLegend, FieldSet, FieldTitle,
Input, NativeSelect, NativeSelectOption, Progress,
RadioGroup, RadioGroupItem, Spinner, Textarea,
});
export const ShadcnProvider = shadcn.Provider;The component bundle is intentionally explicit. Missing or incompatible components fail type-checking where createShadcn is called instead of silently falling back to FormAdapter-owned lookalikes.
Provide it once
"use client";
import { ShadcnProvider } from "./shadcn";
export function Providers({ children }: { children: React.ReactNode }) {
return <ShadcnProvider>{children}</ShadcnProvider>;
}Forms created with createForm from @formadapter/react now use the nearest provider. A child provider still replaces its parent for that subtree.
Adapter-bound forms and extensions
createShadcn returns one stable setup:
const { adapter, createForm, Provider } = shadcn;Providersets this component bundle for a subtree.adaptercan be extended or passed directly to one form.createFormbinds forms to this adapter without a provider.
import { z } from "zod";
import { shadcn } from "@/app/shadcn";
const Feedback = shadcn.createForm(z.object({
email: z.email(),
message: z.string().min(20),
})).configure({
fields: { message: { control: "textarea" } },
});If a customized component adds a required prop, wrap it locally and provide the wrapper. That keeps application-specific defaults in your design system rather than in FormAdapter.