--- name: nextjs-web-client description: Guide for building Next.js 15+ React 19+ frontend components with App Router, Shadcn UI, and React Hook Form. Use when creating UI components, pages, layouts, forms, data tables, or client-side interactivity in a Next.js application. --- # Next.js Web Client Development ## Server vs Client Components **Default to Server Components** unless you need: - Event handlers (onClick, onChange) - Browser APIs (window, localStorage) - React hooks (useState, useEffect) ### Component Split Pattern For components needing server data + client interactivity: ``` components/ ├── AccountForm.tsx # Re-exports server component ├── AccountForm-server.tsx # Fetches data, passes to client └── AccountForm-client.tsx # 'use client', handles interactions ``` ```typescript // AccountForm-server.tsx import { AccountFormClient } from './AccountForm-client' export async function AccountForm() { const data = await fetchData() return } // AccountForm-client.tsx 'use client' export function AccountFormClient({ data }: Props) { const [state, setState] = useState(data) return
...
} ``` ## Shadcn UI Install components via CLI: ```bash npx shadcn@latest add button input form dialog table card ``` Components copy to `src/components/ui/` - customize directly. ## Forms with React Hook Form ```typescript 'use client' import { useForm } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from '@/components/ui/form' import { Input } from '@/components/ui/input' import { Button } from '@/components/ui/button' export function CreateForm() { const form = useForm({ resolver: zodResolver(FormSchema), defaultValues: { name: '' }, }) return (
( Name )} /> ) } ``` ## Dynamic Imports Use for conditionally rendered components: ```typescript import dynamic from 'next/dynamic' const EditDialog = dynamic(() => import('./EditDialog')) const SettingsTab = dynamic(() => import('./tabs/SettingsTab')) // Only load when needed {showEdit && } {tab === 'settings' && } ``` ## Loading & Error States ```typescript // loading.tsx (in route folder) export default function Loading() { return } // error.tsx (in route folder) 'use client' export default function Error({ error, reset }: { error: Error; reset: () => void }) { return (

Something went wrong

) } // With Suspense }> ``` ## Data Tables ```typescript import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table' export function DataTable({ items }: { items: Item[] }) { return ( Name Status Actions {items.map((item) => ( {item.name} {item.status} ))}
) } ``` ## Naming Conventions | Type | Case | Example | |------|------|---------| | Components | PascalCase | `AccountCard.tsx` | | Hooks | use-kebab | `use-account-form.ts` | | Pages | kebab-case dirs | `app/(dashboard)/accounts/page.tsx` | | Route groups | (parentheses) | `(auth)`, `(dashboard)` |