This commit is contained in:
shadcn
2026-01-07 14:37:42 +04:00
parent 413dc4c01f
commit 2f829db41d
64 changed files with 244 additions and 681 deletions

View File

@@ -149,8 +149,12 @@ export default async function Page(props: {
)}
</div>
{params.slug?.[0] === "components" &&
(params.slug?.[1] === "radix" || params.slug?.[1] === "base") && (
<DocsBaseSwitcher />
(params.slug?.[1] === "radix" || params.slug?.[1] === "base") &&
params.slug?.[2] && (
<DocsBaseSwitcher
base={params.slug[1]}
component={params.slug[2]}
/>
)}
{links ? (
<div className="flex items-center gap-2 pt-4">

View File

@@ -1,38 +1,29 @@
"use client"
import Link from "next/link"
import { usePathname } from "next/navigation"
import { cn } from "@/lib/utils"
import { BASES } from "@/registry/bases"
const BASES = [
{ name: "radix", label: "Radix UI" },
{ name: "base", label: "Base UI" },
]
export function DocsBaseSwitcher() {
const pathname = usePathname()
// Extract base and component from /docs/components/{base}/{component}.
const match = pathname.match(/\/docs\/components\/(radix|base)\/(.+)/)
if (!match) return null
const [, currentBase, component] = match
export function DocsBaseSwitcher({
base,
component,
}: {
base: string
component: string
}) {
return (
<div className="flex gap-1 rounded-md border p-1">
{BASES.map((base) => (
{BASES.map((baseItem) => (
<Link
key={base.name}
href={`/docs/components/${base.name}/${component}`}
key={baseItem.name}
href={`/docs/components/${baseItem.name}/${component}`}
className={cn(
"rounded px-3 py-1 text-sm transition-colors",
currentBase === base.name
base === baseItem.name
? "bg-primary text-primary-foreground"
: "hover:bg-muted"
)}
>
{base.label}
{baseItem.title}
</Link>
))}
</div>

View File

@@ -2,7 +2,7 @@
import * as React from "react"
import Link, { type LinkProps } from "next/link"
import { useRouter } from "next/navigation"
import { usePathname, useRouter } from "next/navigation"
import { PAGES_NEW } from "@/lib/docs"
import { showMcpDocs } from "@/lib/flags"
@@ -19,7 +19,7 @@ const TOP_LEVEL_SECTIONS = [
{ name: "Get Started", href: "/docs" },
{
name: "Components",
href: "/docs/components",
href: "/docs/components/radix",
},
{
name: "Directory",
@@ -39,6 +39,61 @@ const TOP_LEVEL_SECTIONS = [
},
]
type PageTreeNode = (typeof source.pageTree)["children"][number]
type PageTreeFolder = Extract<PageTreeNode, { type: "folder" }>
type PageTreePage = Extract<PageTreeNode, { type: "page" }>
// Recursively find all pages in a folder tree.
function getAllPagesFromFolder(folder: PageTreeFolder): PageTreePage[] {
const pages: PageTreePage[] = []
for (const child of folder.children) {
if (child.type === "page") {
pages.push(child)
} else if (child.type === "folder") {
pages.push(...getAllPagesFromFolder(child))
}
}
return pages
}
// Get the pages from a folder, handling nested base folders (radix/base).
function getPagesFromFolder(
folder: PageTreeFolder,
currentBase: string
): PageTreePage[] {
// For the components folder, find the base subfolder.
if (folder.$id === "components" || folder.name === "Components") {
for (const child of folder.children) {
if (child.type === "folder") {
// Match by $id or by name.
const isRadix = child.$id === "radix" || child.name === "Radix UI"
const isBase = child.$id === "base" || child.name === "Base UI"
if (
(currentBase === "radix" && isRadix) ||
(currentBase === "base" && isBase)
) {
return child.children.filter(
(c): c is PageTreePage => c.type === "page"
)
}
}
}
// Fallback: return all pages from nested folders.
return getAllPagesFromFolder(folder).filter(
(page) => !page.url.endsWith("/components")
)
}
// For other folders, return direct page children.
return folder.children.filter(
(child): child is PageTreePage => child.type === "page"
)
}
export function MobileNav({
tree,
items,
@@ -49,6 +104,12 @@ export function MobileNav({
className?: string
}) {
const [open, setOpen] = React.useState(false)
const pathname = usePathname()
// Determine current base from pathname.
const currentBase = pathname.includes("/docs/components/base/")
? "base"
: "radix"
return (
<Popover open={open} onOpenChange={setOpen}>
@@ -125,31 +186,30 @@ export function MobileNav({
<div className="flex flex-col gap-8">
{tree?.children?.map((group, index) => {
if (group.type === "folder") {
const pages = getPagesFromFolder(group, currentBase)
return (
<div key={index} className="flex flex-col gap-4">
<div className="text-muted-foreground text-sm font-medium">
{group.name}
</div>
<div className="flex flex-col gap-3">
{group.children.map((item) => {
if (item.type === "page") {
if (!showMcpDocs && item.url.includes("/mcp")) {
return null
}
return (
<MobileLink
key={`${item.url}-${index}`}
href={item.url}
onOpenChange={setOpen}
className="flex items-center gap-2"
>
{item.name}{" "}
{PAGES_NEW.includes(item.url) && (
<span className="flex size-2 rounded-full bg-blue-500" />
)}
</MobileLink>
)
{pages.map((item) => {
if (!showMcpDocs && item.url.includes("/mcp")) {
return null
}
return (
<MobileLink
key={`${item.url}-${index}`}
href={item.url}
onOpenChange={setOpen}
className="flex items-center gap-2"
>
{item.name}{" "}
{PAGES_NEW.includes(item.url) && (
<span className="flex size-2 rounded-full bg-blue-500" />
)}
</MobileLink>
)
})}
</div>
</div>

View File

@@ -40,7 +40,7 @@ npx shadcn@latest add accordion
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-accordion
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -39,7 +39,7 @@ npx shadcn@latest add alert-dialog
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-alert-dialog
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -38,7 +38,7 @@ npx shadcn@latest add aspect-ratio
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-aspect-ratio
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -38,7 +38,7 @@ npx shadcn@latest add avatar
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-avatar
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -30,7 +30,7 @@ npx shadcn@latest add button-group
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-slot
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -52,7 +52,7 @@ npx shadcn@latest add button
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-slot
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -37,7 +37,7 @@ npx shadcn@latest add checkbox
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-checkbox
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -38,7 +38,7 @@ npx shadcn@latest add collapsible
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-collapsible
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -37,7 +37,7 @@ npx shadcn@latest add context-menu
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-context-menu
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -38,7 +38,7 @@ npx shadcn@latest add dialog
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-dialog
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -38,7 +38,7 @@ npx shadcn@latest add dropdown-menu
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-dropdown-menu
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -1,245 +0,0 @@
---
title: Form
description: Building forms with React Hook Form and Zod.
links:
doc: https://react-hook-form.com
---
import { InfoIcon } from "lucide-react"
<Callout icon={<InfoIcon />} title="We are not actively developing this component anymore.">
The Form component is an abstraction over the `react-hook-form` library. Going forward, we recommend using the [`<Field />`](/docs/components/field) component to build forms. See the [Form](/docs/forms) documentation for more information.
</Callout>
Forms are tricky. They are one of the most common things you'll build in a web application, but also one of the most complex.
Well-designed HTML forms are:
- Well-structured and semantically correct.
- Easy to use and navigate (keyboard).
- Accessible with ARIA attributes and proper labels.
- Has support for client and server side validation.
- Well-styled and consistent with the rest of the application.
In this guide, we will take a look at building forms with [`react-hook-form`](https://react-hook-form.com/) and [`zod`](https://zod.dev). We're going to use a `<FormField>` component to compose accessible forms using Radix UI components.
## Features
The `<Form />` component is a wrapper around the `react-hook-form` library. It provides a few things:
- Composable components for building forms.
- A `<FormField />` component for building controlled form fields.
- Form validation using `zod`.
- Handles accessibility and error messages.
- Uses `React.useId()` for generating unique IDs.
- Applies the correct `aria` attributes to form fields based on states.
- Built to work with all Radix UI components.
- Bring your own schema library. We use `zod` but you can use anything you want.
- **You have full control over the markup and styling.**
## Anatomy
```tsx showLineNumbers
<Form>
<FormField
control={...}
name="..."
render={() => (
<FormItem>
<FormLabel />
<FormControl>
{ /* Your form field */}
</FormControl>
<FormDescription />
<FormMessage />
</FormItem>
)}
/>
</Form>
```
## Example
```tsx showLineNumbers
const form = useForm()
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input placeholder="shadcn" {...field} />
</FormControl>
<FormDescription>This is your public display name.</FormDescription>
<FormMessage />
</FormItem>
)}
/>
```
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">CLI</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
<Steps>
### Command
```bash
npx shadcn@latest add form
```
</Steps>
</TabsContent>
<TabsContent value="manual">
<Steps>
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-label @radix-ui/react-slot react-hook-form @hookform/resolvers zod
```
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource
name="form"
title="components/ui/form.tsx"
styleName="base-nova"
/>
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</CodeTabs>
## Usage
### Create a form schema
Define the shape of your form using a Zod schema. You can read more about using Zod in the [Zod documentation](https://zod.dev).
```tsx showLineNumbers title="components/example-form.tsx" {3,5-7}
"use client"
import { z } from "zod"
const formSchema = z.object({
username: z.string().min(2).max(50),
})
```
### Define a form
Use the `useForm` hook from `react-hook-form` to create a form.
```tsx showLineNumbers title="components/example-form.tsx" {3-4,14-20,22-27}
"use client"
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import { z } from "zod"
const formSchema = z.object({
username: z.string().min(2, {
message: "Username must be at least 2 characters.",
}),
})
export function ProfileForm() {
// 1. Define your form.
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
username: "",
},
})
// 2. Define a submit handler.
function onSubmit(values: z.infer<typeof formSchema>) {
// Do something with the form values.
// ✅ This will be type-safe and validated.
console.log(values)
}
}
```
Since `FormField` is using a controlled component, you need to provide a default value for the field. See the [React Hook Form docs](https://react-hook-form.com/docs/usecontroller) to learn more about controlled components.
### Build your form
We can now use the `<Form />` components to build our form.
```tsx showLineNumbers title="components/example-form.tsx" {7-17,28-50}
"use client"
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import { z } from "zod"
import { Button } from "@/components/ui/button"
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form"
import { Input } from "@/components/ui/input"
const formSchema = z.object({
username: z.string().min(2, {
message: "Username must be at least 2 characters.",
}),
})
export function ProfileForm() {
// ...
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input placeholder="shadcn" {...field} />
</FormControl>
<FormDescription>
This is your public display name.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Submit</Button>
</form>
</Form>
)
}
```
### Done
That's it. You now have a fully accessible form that is type-safe with client-side validation.

View File

@@ -35,7 +35,7 @@ npx shadcn@latest add hover-card
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-hover-card
npm install @base-ui/react
```
<Steps>

View File

@@ -37,7 +37,7 @@ npx shadcn@latest add label
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-label
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -37,7 +37,7 @@ npx shadcn@latest add menubar
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-menubar
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -37,7 +37,7 @@ npx shadcn@latest add navigation-menu
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-navigation-menu
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -37,7 +37,7 @@ npx shadcn@latest add popover
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-popover
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -37,7 +37,7 @@ npx shadcn@latest add progress
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-progress
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -37,7 +37,7 @@ npx shadcn@latest add radio-group
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-radio-group
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -37,7 +37,7 @@ npx shadcn@latest add scroll-area
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-scroll-area
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -38,7 +38,7 @@ npx shadcn@latest add select
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-select
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -36,7 +36,7 @@ npx shadcn@latest add separator
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-separator
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -37,7 +37,7 @@ npx shadcn@latest add sheet
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-dialog
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -37,7 +37,7 @@ npx shadcn@latest add slider
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-slider
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -37,7 +37,7 @@ npx shadcn@latest add switch
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-switch
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -37,7 +37,7 @@ npx shadcn@latest add tabs
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-tabs
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -33,7 +33,7 @@ npx shadcn@latest add toggle-group
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-toggle-group
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -37,7 +37,7 @@ npx shadcn@latest add toggle
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-toggle
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -37,7 +37,7 @@ npx shadcn@latest add tooltip
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-tooltip
npm install @base-ui/react
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -40,7 +40,7 @@ npx shadcn@latest add accordion
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-accordion
npm install radix-ui
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -39,7 +39,7 @@ npx shadcn@latest add alert-dialog
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-alert-dialog
npm install radix-ui
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -38,7 +38,7 @@ npx shadcn@latest add aspect-ratio
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-aspect-ratio
npm install radix-ui
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -38,7 +38,7 @@ npx shadcn@latest add avatar
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-avatar
npm install radix-ui
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -30,7 +30,7 @@ npx shadcn@latest add button-group
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-slot
npm install radix-ui
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -52,7 +52,7 @@ npx shadcn@latest add button
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-slot
npm install radix-ui
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -37,7 +37,7 @@ npx shadcn@latest add checkbox
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-checkbox
npm install radix-ui
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -38,7 +38,7 @@ npx shadcn@latest add collapsible
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-collapsible
npm install radix-ui
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -37,7 +37,7 @@ npx shadcn@latest add context-menu
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-context-menu
npm install radix-ui
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -38,7 +38,7 @@ npx shadcn@latest add dialog
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-dialog
npm install radix-ui
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -38,7 +38,7 @@ npx shadcn@latest add dropdown-menu
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-dropdown-menu
npm install radix-ui
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -1,245 +0,0 @@
---
title: Form
description: Building forms with React Hook Form and Zod.
links:
doc: https://react-hook-form.com
---
import { InfoIcon } from "lucide-react"
<Callout icon={<InfoIcon />} title="We are not actively developing this component anymore.">
The Form component is an abstraction over the `react-hook-form` library. Going forward, we recommend using the [`<Field />`](/docs/components/field) component to build forms. See the [Form](/docs/forms) documentation for more information.
</Callout>
Forms are tricky. They are one of the most common things you'll build in a web application, but also one of the most complex.
Well-designed HTML forms are:
- Well-structured and semantically correct.
- Easy to use and navigate (keyboard).
- Accessible with ARIA attributes and proper labels.
- Has support for client and server side validation.
- Well-styled and consistent with the rest of the application.
In this guide, we will take a look at building forms with [`react-hook-form`](https://react-hook-form.com/) and [`zod`](https://zod.dev). We're going to use a `<FormField>` component to compose accessible forms using Radix UI components.
## Features
The `<Form />` component is a wrapper around the `react-hook-form` library. It provides a few things:
- Composable components for building forms.
- A `<FormField />` component for building controlled form fields.
- Form validation using `zod`.
- Handles accessibility and error messages.
- Uses `React.useId()` for generating unique IDs.
- Applies the correct `aria` attributes to form fields based on states.
- Built to work with all Radix UI components.
- Bring your own schema library. We use `zod` but you can use anything you want.
- **You have full control over the markup and styling.**
## Anatomy
```tsx showLineNumbers
<Form>
<FormField
control={...}
name="..."
render={() => (
<FormItem>
<FormLabel />
<FormControl>
{ /* Your form field */}
</FormControl>
<FormDescription />
<FormMessage />
</FormItem>
)}
/>
</Form>
```
## Example
```tsx showLineNumbers
const form = useForm()
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input placeholder="shadcn" {...field} />
</FormControl>
<FormDescription>This is your public display name.</FormDescription>
<FormMessage />
</FormItem>
)}
/>
```
## Installation
<CodeTabs>
<TabsList>
<TabsTrigger value="cli">CLI</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
<Steps>
### Command
```bash
npx shadcn@latest add form
```
</Steps>
</TabsContent>
<TabsContent value="manual">
<Steps>
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-label @radix-ui/react-slot react-hook-form @hookform/resolvers zod
```
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource
name="form"
title="components/ui/form.tsx"
styleName="radix-nova"
/>
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</CodeTabs>
## Usage
### Create a form schema
Define the shape of your form using a Zod schema. You can read more about using Zod in the [Zod documentation](https://zod.dev).
```tsx showLineNumbers title="components/example-form.tsx" {3,5-7}
"use client"
import { z } from "zod"
const formSchema = z.object({
username: z.string().min(2).max(50),
})
```
### Define a form
Use the `useForm` hook from `react-hook-form` to create a form.
```tsx showLineNumbers title="components/example-form.tsx" {3-4,14-20,22-27}
"use client"
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import { z } from "zod"
const formSchema = z.object({
username: z.string().min(2, {
message: "Username must be at least 2 characters.",
}),
})
export function ProfileForm() {
// 1. Define your form.
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
username: "",
},
})
// 2. Define a submit handler.
function onSubmit(values: z.infer<typeof formSchema>) {
// Do something with the form values.
// ✅ This will be type-safe and validated.
console.log(values)
}
}
```
Since `FormField` is using a controlled component, you need to provide a default value for the field. See the [React Hook Form docs](https://react-hook-form.com/docs/usecontroller) to learn more about controlled components.
### Build your form
We can now use the `<Form />` components to build our form.
```tsx showLineNumbers title="components/example-form.tsx" {7-17,28-50}
"use client"
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import { z } from "zod"
import { Button } from "@/components/ui/button"
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form"
import { Input } from "@/components/ui/input"
const formSchema = z.object({
username: z.string().min(2, {
message: "Username must be at least 2 characters.",
}),
})
export function ProfileForm() {
// ...
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input placeholder="shadcn" {...field} />
</FormControl>
<FormDescription>
This is your public display name.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Submit</Button>
</form>
</Form>
)
}
```
### Done
That's it. You now have a fully accessible form that is type-safe with client-side validation.

View File

@@ -35,7 +35,7 @@ npx shadcn@latest add hover-card
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-hover-card
npm install radix-ui
```
<Steps>

View File

@@ -37,7 +37,7 @@ npx shadcn@latest add label
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-label
npm install radix-ui
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -37,7 +37,7 @@ npx shadcn@latest add menubar
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-menubar
npm install radix-ui
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -37,7 +37,7 @@ npx shadcn@latest add navigation-menu
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-navigation-menu
npm install radix-ui
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -37,7 +37,7 @@ npx shadcn@latest add popover
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-popover
npm install radix-ui
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -37,7 +37,7 @@ npx shadcn@latest add progress
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-progress
npm install radix-ui
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -37,7 +37,7 @@ npx shadcn@latest add radio-group
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-radio-group
npm install radix-ui
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -37,7 +37,7 @@ npx shadcn@latest add scroll-area
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-scroll-area
npm install radix-ui
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -38,7 +38,7 @@ npx shadcn@latest add select
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-select
npm install radix-ui
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -36,7 +36,7 @@ npx shadcn@latest add separator
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-separator
npm install radix-ui
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -37,7 +37,7 @@ npx shadcn@latest add sheet
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-dialog
npm install radix-ui
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -37,7 +37,7 @@ npx shadcn@latest add slider
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-slider
npm install radix-ui
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -37,7 +37,7 @@ npx shadcn@latest add switch
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-switch
npm install radix-ui
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -37,7 +37,7 @@ npx shadcn@latest add tabs
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-tabs
npm install radix-ui
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -33,7 +33,7 @@ npx shadcn@latest add toggle-group
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-toggle-group
npm install radix-ui
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -37,7 +37,7 @@ npx shadcn@latest add toggle
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-toggle
npm install radix-ui
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -37,7 +37,7 @@ npx shadcn@latest add tooltip
<Step>Install the following dependencies:</Step>
```bash
npm install @radix-ui/react-tooltip
npm install radix-ui
```
<Step>Copy and paste the following code into your project.</Step>

View File

@@ -3,7 +3,7 @@ title: Forms
description: Build forms with React and shadcn/ui.
---
import { ClipboardListIcon, InfoIcon } from "lucide-react"
import { ClipboardListIcon } from "lucide-react"
## Pick Your Framework
@@ -40,6 +40,5 @@ Start by selecting your framework. Then follow the instructions to learn how to
</svg>
<p className="mt-2 font-medium">useActionState</p>
<p className="text-muted-foreground mt-1 text-xs">(Coming Soon)</p>
</LinkedCard>
</div>

View File

@@ -1,89 +0,0 @@
import { NextResponse } from "next/server"
import type { NextRequest } from "next/server"
// List of all component names.
const COMPONENT_NAMES = [
"accordion",
"alert",
"alert-dialog",
"aspect-ratio",
"avatar",
"badge",
"breadcrumb",
"button",
"button-group",
"calendar",
"card",
"carousel",
"chart",
"checkbox",
"collapsible",
"combobox",
"command",
"context-menu",
"data-table",
"date-picker",
"dialog",
"drawer",
"dropdown-menu",
"empty",
"field",
"form",
"hover-card",
"input",
"input-group",
"input-otp",
"item",
"kbd",
"label",
"menubar",
"native-select",
"navigation-menu",
"pagination",
"popover",
"progress",
"radio-group",
"resizable",
"scroll-area",
"select",
"separator",
"sheet",
"sidebar",
"skeleton",
"slider",
"sonner",
"spinner",
"switch",
"table",
"tabs",
"textarea",
"toast",
"toggle",
"toggle-group",
"tooltip",
"typography",
]
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl
// Match /docs/components/:component (not /docs/components/radix/:component or /docs/components/base/:component).
const match = pathname.match(/^\/docs\/components\/([^/]+)$/)
if (match) {
const segment = match[1]
// Redirect component pages to radix version (default).
if (COMPONENT_NAMES.includes(segment)) {
return NextResponse.redirect(
new URL(`/docs/components/radix/${segment}`, request.url),
301
)
}
}
return NextResponse.next()
}
export const config = {
matcher: "/docs/components/:path*",
}

View File

@@ -1,5 +1,67 @@
import { createMDX } from "fumadocs-mdx/next"
// List of all component names for redirects.
const COMPONENT_NAMES = [
"accordion",
"alert",
"alert-dialog",
"aspect-ratio",
"avatar",
"badge",
"breadcrumb",
"button",
"button-group",
"calendar",
"card",
"carousel",
"chart",
"checkbox",
"collapsible",
"combobox",
"command",
"context-menu",
"data-table",
"date-picker",
"dialog",
"drawer",
"dropdown-menu",
"empty",
"field",
"hover-card",
"input",
"input-group",
"input-otp",
"item",
"kbd",
"label",
"menubar",
"native-select",
"navigation-menu",
"pagination",
"popover",
"progress",
"radio-group",
"resizable",
"scroll-area",
"select",
"separator",
"sheet",
"sidebar",
"skeleton",
"slider",
"sonner",
"spinner",
"switch",
"table",
"tabs",
"textarea",
"toast",
"toggle",
"toggle-group",
"tooltip",
"typography",
]
/** @type {import('next').NextConfig} */
const nextConfig = {
devIndicators: false,
@@ -29,7 +91,33 @@ const nextConfig = {
turbopackFileSystemCacheForDev: true,
},
redirects() {
// Generate redirects for all components: /docs/components/:name → /docs/components/radix/:name.
const componentRedirects = COMPONENT_NAMES.map((name) => ({
source: `/docs/components/${name}`,
destination: `/docs/components/radix/${name}`,
permanent: true,
}))
return [
// Form redirects to /docs/forms.
{
source: "/docs/components/form",
destination: "/docs/forms",
permanent: true,
},
{
source: "/docs/components/radix/form",
destination: "/docs/forms",
permanent: true,
},
{
source: "/docs/components/base/form",
destination: "/docs/forms",
permanent: true,
},
// Component redirects (default to radix).
...componentRedirects,
// Other redirects.
{
source: "/components",
destination: "/docs/components",