Self-paced

Explore our extensive collection of courses designed to help you master various subjects and skills. Whether you're a beginner or an advanced learner, there's something here for everyone.

Bootcamp

Learn live

Join us for our free workshops, webinars, and other events to learn more about our programs and get started on your journey to becoming a developer.

Upcoming live events

Learning library

For all the self-taught geeks out there, here is our content library with most of the learning materials we have produced throughout the years.

It makes sense to start learning by reading and watching videos about fundamentals and how things work.

Search from all Lessons


← Back to Lessons

Building Accessible UIs with Shadcn UI in React

Why Choose Shadcn UI?
Essential Components Examples

Shadcn UI provides a unique way to build React interfaces. Instead of installing a typical component library package, you copy beautifully designed and accessible component code directly into your project. This gives you full control over styling and behavior.

Here's a simple example using Shadcn UI's Button component:

Loading...

Why Choose Shadcn UI?

Shadcn UI stands out because it combines:

  • Accessibility: Built using Radix UI primitives, ensuring components are accessible out-of-the-box.
  • Styling: Uses Tailwind CSS for utility-first styling, making customization intuitive.
  • Control: You own the code. Components are copied to your codebase (e.g., via a CLI command like npx shadcn-ui@latest add button), not installed as a dependency. This means no version conflicts and complete freedom to modify.

Key Differences from Traditional Libraries:

FeatureTraditional LibrariesShadcn UI
InstallationPackage DependencyCopy-paste code into project
UpdatesManage versionsUpdate code manually (if needed)
CustomizationOften LimitedFull code access
OwnershipExternal dependencyYou own the component code

Essential Components Examples

Shadcn UI offers a variety of components. You add them to your project as needed using its CLI tool.

Cards

Use Cards to structure content sections:

1import { 2 Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle 3} from "@/components/ui/card"; // Assuming default path 4import { Button } from "@/components/ui/button"; 5 6function ProfileCard() { 7 return ( 8 <Card className="w-[350px]"> 9 <CardHeader> 10 <CardTitle>User Profile</CardTitle> 11 <CardDescription>View and manage your information.</CardDescription> 12 </CardHeader> 13 <CardContent> 14 <p>Details about the user go here.</p> 15 </CardContent> 16 <CardFooter className="flex justify-end space-x-2"> 17 <Button variant="outline">Cancel</Button> 18 <Button>Save Changes</Button> 19 </CardFooter> 20 </Card> 21 ); 22}

Dialogs (Modals)

Create accessible modal dialogs for actions or information:

1import { 2 Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger 3} from "@/components/ui/dialog"; // Assuming default path 4import { Button } from "@/components/ui/button"; 5 6function DeleteConfirmation() { 7 return ( 8 <Dialog> 9 <DialogTrigger asChild> 10 <Button variant="destructive">Delete Account</Button> 11 </DialogTrigger> 12 <DialogContent> 13 <DialogHeader> 14 <DialogTitle>Are you absolutely sure?</DialogTitle> 15 <DialogDescription> 16 This action cannot be undone. This will permanently delete the account. 17 </DialogDescription> 18 </DialogHeader> 19 <DialogFooter> 20 <Button variant="outline">Cancel</Button> 21 <Button variant="destructive">Confirm Deletion</Button> 22 </DialogFooter> 23 </DialogContent> 24 </Dialog> 25 ); 26}

Form Integration

Shadcn UI components integrate smoothly with form libraries like React Hook Form, often using Zod for validation.

1import { zodResolver } from "@hookform/resolvers/zod"; 2import { useForm } from "react-hook-form"; 3import * as z from "zod"; 4import { Button } from "@/components/ui/button"; 5import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"; 6import { Input } from "@/components/ui/input"; 7 8// 1. Define validation schema 9const formSchema = z.object({ 10 username: z.string().min(3, "Username must be at least 3 characters.").max(50), 11}); 12 13function ProfileForm() { 14 // 2. Initialize form 15 const form = useForm<z.infer<typeof formSchema>>({ 16 resolver: zodResolver(formSchema), 17 defaultValues: { username: "" }, 18 }); 19 20 // 3. Handle submission 21 function onSubmit(values: z.infer<typeof formSchema>) { 22 console.log("Form submitted:", values); 23 // Add actual submission logic here 24 } 25 26 // 4. Build the form UI 27 return ( 28 <Form {...form}> 29 <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6 w-[350px]"> 30 <FormField 31 control={form.control} 32 name="username" 33 render={({ field }) => ( 34 <FormItem> 35 <FormLabel>Username</FormLabel> 36 <FormControl> 37 <Input placeholder="Enter your username" {...field} /> 38 </FormControl> 39 <FormMessage /> 40 </FormItem> 41 )} 42 /> 43 <Button type="submit">Update Profile</Button> 44 </form> 45 </Form> 46 ); 47}

Customizing Shadcn UI

Theming with CSS Variables

Customize the look and feel by modifying CSS variables in your global CSS file (e.g., globals.css or index.css):

1/* Example from globals.css */ 2@tailwind base; 3@tailwind components; 4@tailwind utilities; 5 6@layer base { 7 :root { 8 --background: 0 0% 100%; /* Light mode background */ 9 --foreground: 222.2 84% 4.9%; /* Light mode text */ 10 /* ... other core variables ... */ 11 --primary: 222.2 47.4% 11.2%; 12 --primary-foreground: 210 40% 98%; 13 /* ... more variables for borders, inputs, rings etc. ... */ 14 --radius: 0.5rem; /* Border radius */ 15 } 16 17 .dark { 18 --background: 222.2 84% 4.9%; /* Dark mode background */ 19 --foreground: 210 40% 98%; /* Dark mode text */ 20 /* ... override other variables for dark mode ... */ 21 --primary: 210 40% 98%; 22 --primary-foreground: 222.2 47.4% 11.2%; 23 /* ... */ 24 } 25}

Modifying Component Code

Since you own the component code, you can directly edit the files (usually within a components/ui directory) to change structure, styles, or behavior. For example, adding a custom variant to the Button:

1// Inside components/ui/button.jsx (example path) 2import { cva } from "class-variance-authority"; 3 4export const buttonVariants = cva( 5 "inline-flex items-center justify-center ...", // Base classes 6 { 7 variants: { 8 variant: { 9 default: "bg-primary text-primary-foreground hover:bg-primary/90", 10 destructive: "...", 11 outline: "...", 12 secondary: "...", 13 ghost: "...", 14 link: "...", 15 // Add your custom variant 16 success: "bg-green-600 text-white hover:bg-green-700", 17 }, 18 size: { 19 default: "h-10 px-4 py-2", 20 sm: "h-9 rounded-md px-3", 21 lg: "h-11 rounded-md px-8", 22 icon: "h-10 w-10", 23 }, 24 }, 25 defaultVariants: { 26 variant: "default", 27 size: "default", 28 }, 29 } 30); 31 32// ... rest of the Button component ...

Best Practices

  • Organization: Keep Shadcn UI components (usually in components/ui) separate from your custom application components.
  • Accessibility: While Shadcn/Radix provide a strong foundation, always test your implementation for accessibility (keyboard navigation, screen readers, semantic HTML).
  • Updates: Periodically check the Shadcn UI documentation for updates or improvements to components you use. You'll need to manually copy changes if desired.

Conclusion

Shadcn UI offers a pragmatic approach for React developers wanting control and customization without building everything from scratch. By leveraging Tailwind CSS and Radix UI, it provides a foundation for beautiful, accessible, and maintainable user interfaces directly within your project codebase.

Additional Resources

  • Shadcn UI Documentation
  • Radix UI Primitives (Underlying accessible components)
  • Tailwind CSS Documentation