230
169
Source

shadcn/ui component patterns for Next.js 16 applications. This skill should be used when adding UI components, customizing component styles, composing primitives, or integrating forms with react-hook-form. Covers installation, customization, composition patterns, and Atlas-specific conventions using Tailwind CSS v4.

Install

mkdir -p .claude/skills/shadcn-ui && curl -L -o skill.zip "https://mcp.directory/api/skills/download/278" && unzip -o skill.zip -d .claude/skills/shadcn-ui && rm skill.zip

Installs to .claude/skills/shadcn-ui

About this skill

shadcn/ui Component Usage

Purpose

Provide comprehensive patterns for implementing shadcn/ui components in Next.js 16 applications with Atlas-specific conventions. Focus on composition over props, accessibility-first design, and type-safe integration with tRPC and react-hook-form.

When To Use This Skill

Component Implementation:

  • Adding new shadcn/ui components to the project
  • Customizing component variants and styles
  • Building composite components from primitives (Dialog, Form, Table)
  • Implementing responsive mobile/desktop patterns

Form Integration:

  • Integrating react-hook-form with shadcn Form components
  • Validating forms with Zod schemas
  • Connecting forms to tRPC mutations
  • Handling form state and errors

Data Display:

  • Creating data tables with sorting and filtering
  • Building card layouts and list views
  • Implementing skeleton loading states

Interactive Patterns:

  • Building modal dialogs and drawers (sheets)
  • Implementing toast notifications
  • Creating dropdown menus and popovers
  • Adding tooltips and hover states

Accessibility:

  • Ensuring keyboard navigation works correctly
  • Adding proper ARIA labels (especially icon buttons)
  • Implementing focus management
  • Meeting WCAG 2.1 AAA standards (44px minimum touch targets)

Core Principles

1. Copy-Not-Import Philosophy

shadcn/ui components are copied into the project, not imported as dependencies. Customize directly in src/components/ui/.

// ✅ Customize directly
// src/components/ui/button.tsx
const buttonVariants = cva("...", {
  variants: {
    variant: {
      default: "bg-primary text-primary-foreground",
      // Add your custom variant
      atlas: "bg-blue-600 text-white hover:bg-blue-700",
    },
  },
});

2. Composition Over Props

Build complex components by composing primitives rather than adding props:

// ❌ Avoid: Too many props
<Dialog
  title="Delete Item"
  description="Are you sure?"
  showCloseButton={true}
  size="lg"
/>

// ✅ Prefer: Composition
<Dialog>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Delete Item</DialogTitle>
      <DialogDescription>Are you sure?</DialogDescription>
    </DialogHeader>
  </DialogContent>
</Dialog>

3. className Customization

Extend styles via className prop using Tailwind utilities:

<Button variant="default" className="w-full sm:w-auto shadow-lg">
  Submit
</Button>

4. Accessibility First

All components are accessible by default (built on Radix UI):

  • Keyboard navigation
  • Screen reader support
  • ARIA attributes
  • Focus management

Icon buttons require labels for accessibility:

// ❌ Inaccessible
<Button size="icon">
  <TrashIcon />
</Button>

// ✅ Accessible
<Button size="icon" aria-label="Delete item">
  <TrashIcon />
</Button>

Quick Reference

Common Components

ComponentUse CaseKey Features
ButtonActions, triggersVariants, sizes, loading state
InputText entryTypes, validation states
FormForm validationreact-hook-form integration
DialogModalsPortal, overlay, animations
SheetSide panelsMobile-friendly drawers
TableData displaySemantic HTML, responsive
SelectDropdownsSearchable, keyboard nav
CheckboxBoolean inputIndeterminate state
LabelForm labelsAuto-linked to inputs
TextareaMulti-line inputAuto-resize support
TabsNavigationKeyboard accessible
CardContent containersHeader, content, footer
BadgeStatus labelsVariants for states
SkeletonLoading statesPlaceholder UI
SeparatorVisual dividersHorizontal/vertical
Dropdown MenuActions menuNested menus, shortcuts
AlertNotificationsInfo, warning, error
ProgressLoading indicatorsDeterminate/indeterminate
TooltipHover hintsDelay, positioning

Button Variants & Sizes

<Button variant="default">Primary Action</Button>
<Button variant="secondary">Secondary Action</Button>
<Button variant="outline">Outlined</Button>
<Button variant="ghost">Subtle</Button>
<Button variant="destructive">Delete</Button>
<Button variant="link">Link Style</Button>

<Button size="sm">Small</Button>      {/* 32px mobile, 40px desktop */}
<Button size="default">Default</Button> {/* 44px mobile, 36px desktop */}
<Button size="lg">Large</Button>      {/* 48px mobile, 40px desktop */}
<Button size="icon" aria-label="Add"> {/* 44x44px - WCAG 2.1 AAA */}
  <PlusIcon />
</Button>

Loading States

<Button loading={isPending} loadingText="Saving...">
  Save Changes
</Button>

// Or manually:
<Button disabled={isPending}>
  {isPending && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
  Save Changes
</Button>

Form Integration

Basic Form Pattern

"use client";

import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import {
  Form,
  FormControl,
  FormDescription,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";

const schema = z.object({
  email: z.string().email(),
  name: z.string().min(2),
});

type FormValues = z.infer<typeof schema>;

export function MyFormClient() {
  const form = useForm<FormValues>({
    resolver: zodResolver(schema),
    defaultValues: { email: "", name: "" },
  });

  const onSubmit = form.handleSubmit(async (data) => {
    // Handle submission
  });

  return (
    <Form {...form}>
      <form onSubmit={onSubmit} className="space-y-4">
        <FormField
          control={form.control}
          name="email"
          render={({ field }) => (
            <FormItem>
              <FormLabel>Email</FormLabel>
              <FormControl>
                <Input type="email" placeholder="[email protected]" {...field} />
              </FormControl>
              <FormDescription>We'll never share your email.</FormDescription>
              <FormMessage />
            </FormItem>
          )}
        />

        <Button type="submit" loading={form.formState.isSubmitting}>
          Submit
        </Button>
      </form>
    </Form>
  );
}

Form with tRPC Mutation

"use client";

import { api } from "@/lib/api/react";
import { toast } from "sonner";

export function CreateStackFormClient() {
  const form = useForm<FormValues>({
    resolver: zodResolver(schema),
    defaultValues: {
      /* ... */
    },
  });

  const utils = api.useUtils();
  const createMutation = api.stacks.create.useMutation({
    onSuccess: () => {
      toast.success("Stack created successfully");
      utils.stacks.list.invalidate(); // Refetch list
      form.reset();
    },
    onError: (error) => {
      toast.error(error.message);
    },
  });

  const onSubmit = form.handleSubmit((data) => {
    createMutation.mutate(data);
  });

  return (
    <Form {...form}>
      <form onSubmit={onSubmit} className="space-y-4">
        {/* Form fields */}
        <Button
          type="submit"
          loading={createMutation.isPending}
          disabled={createMutation.isPending}
        >
          Create Stack
        </Button>
      </form>
    </Form>
  );
}

Common Form Field Patterns

Number Input:

<FormField
  control={form.control}
  name="quantity"
  render={({ field }) => (
    <FormItem>
      <FormLabel>Quantity</FormLabel>
      <FormControl>
        <Input
          type="number"
          inputMode="numeric"
          placeholder="100"
          {...field}
          onChange={(e) => field.onChange(e.target.valueAsNumber)}
        />
      </FormControl>
      <FormMessage />
    </FormItem>
  )}
/>

Date Input:

<FormField
  control={form.control}
  name="scheduledFor"
  render={({ field: { value, onChange, ...field } }) => (
    <FormItem>
      <FormLabel>Scheduled Date</FormLabel>
      <FormControl>
        <Input
          type="date"
          {...field}
          value={value instanceof Date ? value.toISOString().split("T")[0] : ""}
          onChange={(e) => {
            const dateStr = e.target.value;
            if (dateStr) onChange(new Date(dateStr));
          }}
        />
      </FormControl>
      <FormMessage />
    </FormItem>
  )}
/>

Select/Dropdown:

import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";

<FormField
  control={form.control}
  name="status"
  render={({ field }) => (
    <FormItem>
      <FormLabel>Status</FormLabel>
      <Select onValueChange={field.onChange} defaultValue={field.value}>
        <FormControl>
          <SelectTrigger>
            <SelectValue placeholder="Select a status" />
          </SelectTrigger>
        </FormControl>
        <SelectContent>
          <SelectItem value="active">Active</SelectItem>
          <SelectItem value="inactive">Inactive</SelectItem>
        </SelectContent>
      </Select>
      <FormMessage />
    </FormItem>
  )}
/>;

Textarea:

import { Textarea } from "@/components/ui/textarea";

<FormField
  control={form.control}
  name="notes"
  render={({ field }) => (
    <FormItem>
      <FormLabel>Notes</FormLabel>
      <FormControl>
        <Textarea placeholder="Optional notes..." maxLength={1000} {...field} />
      </FormControl>
      <FormMessage />
    </FormItem>
  )}
/>;

Content truncated.

You might also like

flutter-development

aj-geddes

Build beautiful cross-platform mobile apps with Flutter and Dart. Covers widgets, state management with Provider/BLoC, navigation, API integration, and material design.

1,5731,370

ui-ux-pro-max

nextlevelbuilder

"UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 8 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient."

1,1161,191

drawio-diagrams-enhanced

jgtolentino

Create professional draw.io (diagrams.net) diagrams in XML format (.drawio files) with integrated PMP/PMBOK methodologies, extensive visual asset libraries, and industry-standard professional templates. Use this skill when users ask to create flowcharts, swimlane diagrams, cross-functional flowcharts, org charts, network diagrams, UML diagrams, BPMN, project management diagrams (WBS, Gantt, PERT, RACI), risk matrices, stakeholder maps, or any other visual diagram in draw.io format. This skill includes access to custom shape libraries for icons, clipart, and professional symbols.

1,4181,109

godot

bfollington

This skill should be used when working on Godot Engine projects. It provides specialized knowledge of Godot's file formats (.gd, .tscn, .tres), architecture patterns (component-based, signal-driven, resource-based), common pitfalls, validation tools, code templates, and CLI workflows. The `godot` command is available for running the game, validating scripts, importing resources, and exporting builds. Use this skill for tasks involving Godot game development, debugging scene/resource files, implementing game systems, or creating new Godot components.

1,197748

nano-banana-pro

garg-aayush

Generate and edit images using Google's Nano Banana Pro (Gemini 3 Pro Image) API. Use when the user asks to generate, create, edit, modify, change, alter, or update images. Also use when user references an existing image file and asks to modify it in any way (e.g., "modify this image", "change the background", "replace X with Y"). Supports both text-to-image generation and image-to-image editing with configurable resolution (1K default, 2K, or 4K for high resolution). DO NOT read the image file first - use this skill directly with the --input-image parameter.

1,155684

pdf-to-markdown

aliceisjustplaying

Convert entire PDF documents to clean, structured Markdown for full context loading. Use this skill when the user wants to extract ALL text from a PDF into context (not grep/search), when discussing or analyzing PDF content in full, when the user mentions "load the whole PDF", "bring the PDF into context", "read the entire PDF", or when partial extraction/grepping would miss important context. This is the preferred method for PDF text extraction over page-by-page or grep approaches.

1,318617

Stay ahead of the MCP ecosystem

Get weekly updates on new skills and servers.