--- name: Frontend Builder description: Build modern React/Next.js frontends. Use when creating web applications, choosing frontend stack, structuring components, or implementing UI/UX designs. Covers React, Next.js, Tailwind CSS, and component patterns. version: 1.0.0 --- # Frontend Builder Build maintainable, performant React and Next.js frontends. ## Core Principles ### 1. Component Composition Break UI into small, reusable, single-purpose components ### 2. State Proximity Keep state as close to where it's used as possible ### 3. Performance by Default Optimize rendering, code splitting, and asset loading ### 4. Developer Experience Clear naming, consistent patterns, helpful errors ## Framework Selection ### React (Vite) vs. Next.js **Use React + Vite when**: - Client-side only application - No SEO requirements - Simple deployment (static hosting) - Faster initial setup **Use Next.js when**: - SEO important (marketing sites, blogs, e-commerce) - Server-side rendering needed - API routes required - File-based routing preferred - Image optimization critical **Recommended for most projects**: Next.js (App Router) --- ## Component Architecture ### Component Types **1. Page Components** (Route entry points): ```typescript // app/users/page.tsx (Next.js App Router) export default function UsersPage() { return (
) } ``` **2. Feature Components** (Business logic): ```typescript // components/features/UserList.tsx export function UserList() { const { data, isLoading } = useUsers() if (isLoading) return return (
{data.map(user => )}
) } ``` **3. UI Components** (Reusable, no business logic): ```typescript // components/ui/button.tsx export function Button({ children, variant = 'primary', ...props }) { return ( ) } ``` ### Component Best Practices ```typescript // ✅ Good: Small, focused, typed interface UserProfileProps { user: User onEdit?: () => void } export function UserProfile({ user, onEdit }: UserProfileProps) { return (
{onEdit && }
) } // ❌ Bad: Giant, untyped, unclear export function UserProfile(props) { // 500 lines of JSX, multiple responsibilities return
...
} ``` --- ## State Management ### Decision Tree ``` How many components need this state? │ ├─ One component → useState ├─ Parent + children → Props or useState + props ├─ Siblings → Lift to common parent ├─ Widely used (theme, auth) → Context API └─ Complex app state → Zustand or Redux ``` ### Local State (useState) ```typescript // For component-level state function Counter() { const [count, setCount] = useState(0) const [isOpen, setIsOpen] = useState(false) return (
) } ``` ### Context API ```typescript // For app-wide state (theme, auth, user) const UserContext = createContext(undefined) export function UserProvider({ children }: { children: ReactNode }) { const [user, setUser] = useState(null) return ( {children} ) } export function useUser() { const context = useContext(UserContext) if (!context) throw new Error('useUser must be within UserProvider') return context } ``` ### Zustand (Recommended for Complex State) ```typescript import { create } from 'zustand' interface CounterStore { count: number increment: () => void decrement: () => void reset: () => void } export const useCounterStore = create((set) => ({ count: 0, increment: () => set((state) => ({ count: state.count + 1 })), decrement: () => set((state) => ({ count: state.count - 1 })), reset: () => set({ count: 0 }) })) // Usage function Counter() { const { count, increment } = useCounterStore() return } ``` --- ## Data Fetching ### React Query (Recommended) ```typescript import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' // Query (GET) function Users() { const { data, isLoading, error } = useQuery({ queryKey: ['users'], queryFn: fetchUsers, staleTime: 5 * 60 * 1000 // 5 minutes }) if (isLoading) return if (error) return return } // Mutation (POST, PUT, DELETE) function CreateUser() { const queryClient = useQueryClient() const mutation = useMutation({ mutationFn: createUser, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['users'] }) } }) return ( ) } ``` ### Next.js Server Components (App Router) ```typescript // app/users/page.tsx // Server Component - fetches on server export default async function UsersPage() { const users = await fetchUsers() // Runs on server return } // Client Component - for interactivity 'use client' export function UserList({ users }: { users: User[] }) { const [selected, setSelected] = useState(null) return (
{users.map(user => ( setSelected(user.id)} /> ))}
) } ``` --- ## Form Handling ### React Hook Form (Recommended) ```typescript import { useForm } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' import { z } from 'zod' const loginSchema = z.object({ email: z.string().email('Invalid email address'), password: z.string().min(8, 'Password must be at least 8 characters') }) type LoginForm = z.infer function LoginForm() { const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm({ resolver: zodResolver(loginSchema) }) const onSubmit = async (data: LoginForm) => { await login(data) } return (
{errors.email && ( {errors.email.message} )}
{errors.password && ( {errors.password.message} )}
) } ``` --- ## Styling ### Tailwind CSS (Recommended) ```typescript // Install: @shadcn/ui for component library function Button({ variant = 'primary', children, ...props }) { return ( ) } ``` ### CSS Modules (Alternative) ```typescript // Button.module.css .button { padding: 0.5rem 1rem; border-radius: 0.25rem; } .primary { background-color: blue; color: white; } // Button.tsx import styles from './Button.module.css' export function Button({ variant = 'primary', children }) { return ( ) } ``` --- ## Performance Optimization ### React Optimization ```typescript import { memo, useMemo, useCallback } from 'react' // 1. Memoize expensive calculations function DataTable({ data }) { const sortedData = useMemo( () => data.sort((a, b) => a.name.localeCompare(b.name)), [data] ) return } // 2. Memoize callbacks function Parent() { const handleClick = useCallback(() => { console.log('Clicked') }, []) return } // 3. Memoize components const ExpensiveChild = memo(function ExpensiveChild({ onClick }) { return }) ``` ### Next.js Optimization ```typescript // 1. Image optimization import Image from 'next/image' // 2. Font optimization import { Inter } from 'next/font/google' const inter = Inter({ subsets: ['latin'] }) export default function RootLayout({ children }) { return ( {children} ) } // 3. Dynamic imports (code splitting) import dynamic from 'next/dynamic' const HeavyComponent = dynamic(() => import('./HeavyComponent'), { loading: () => }) ``` --- ## Error Handling ### Error Boundary ```typescript 'use client' import { Component, ReactNode } from 'react' interface Props { children: ReactNode fallback?: ReactNode } interface State { hasError: boolean error?: Error } export class ErrorBoundary extends Component { constructor(props: Props) { super(props) this.state = { hasError: false } } static getDerivedStateFromError(error: Error): State { return { hasError: true, error } } render() { if (this.state.hasError) { return this.props.fallback || (

Something went wrong

{this.state.error?.message}

) } return this.props.children } } ``` ### Next.js Error Handling ```typescript // app/error.tsx 'use client' export default function Error({ error, reset }: { error: Error reset: () => void }) { return (

Something went wrong!

) } ``` --- ## Folder Structure ### Next.js App Router ``` app/ ├── (auth)/ # Route group (auth pages) │ ├── login/ │ └── signup/ ├── (dashboard)/ # Route group (dashboard) │ ├── layout.tsx │ ├── page.tsx │ └── settings/ ├── api/ # API routes │ └── users/ │ └── route.ts └── layout.tsx # Root layout components/ ├── ui/ # shadcn/ui components │ ├── button.tsx │ ├── input.tsx │ └── dialog.tsx ├── features/ # Feature components │ ├── UserList.tsx │ └── UserProfile.tsx └── layouts/ # Layout components ├── Header.tsx └── Footer.tsx lib/ ├── utils.ts # Utility functions ├── api.ts # API client └── validation.ts # Zod schemas hooks/ ├── useUser.ts └── useDebounce.ts stores/ └── userStore.ts # Zustand stores ``` --- ## TypeScript Best Practices ```typescript // 1. Type component props interface ButtonProps { variant?: 'primary' | 'secondary' | 'danger' children: ReactNode onClick?: () => void } export function Button({ variant = 'primary', children, onClick }: ButtonProps) { return } // 2. Type API responses interface User { id: string name: string email: string } async function fetchUsers(): Promise { const res = await fetch('/api/users') return res.json() } // 3. Type state const [user, setUser] = useState(null) const [isLoading, setIsLoading] = useState(false) ``` --- ## Summary Great frontends: - ✅ Use Next.js for most projects (SEO, performance, DX) - ✅ Break UI into small, typed components - ✅ Choose appropriate state management (useState → Context → Zustand) - ✅ Use React Query for server state - ✅ Style with Tailwind CSS + shadcn/ui - ✅ Optimize with memoization and code splitting - ✅ Handle errors gracefully with Error Boundaries - ✅ Follow consistent folder structure --- ## Related Resources **Related Skills**: - `api-designer` - For designing backend APIs to consume - `ux-designer` - For creating UX designs to implement - `deployment-advisor` - For hosting Next.js/React apps **Related Patterns**: - `META/DECISION-FRAMEWORK.md` - Frontend framework selection - `STANDARDS/architecture-patterns/component-patterns.md` - Component design patterns (when created) **Related Playbooks**: - `PLAYBOOKS/setup-nextjs-project.md` - Next.js project setup (when created) - `PLAYBOOKS/optimize-frontend-performance.md` - Performance optimization (when created)