--- name: vercel-development description: Vercel and Next.js deployment best practices including server components, edge functions, AI SDK integration, and performance optimization. --- # Vercel Development Best Practices ## Overview This skill provides comprehensive guidelines for developing and deploying applications on Vercel, with a focus on Next.js, React Server Components, Edge Functions, and the Vercel AI SDK. ## Core Principles - Write concise, technical TypeScript code with accurate examples - Use functional and declarative programming patterns; avoid classes - Minimize 'use client', 'useEffect', and 'setState'; favor React Server Components (RSC) - Implement responsive design with Tailwind CSS using mobile-first approach - Optimize for Core Web Vitals and performance ## Project Structure ``` my-app/ ├── app/ # App Router pages and layouts │ ├── (auth)/ # Route groups │ ├── api/ # API routes │ ├── layout.tsx # Root layout │ └── page.tsx # Home page ├── components/ # React components │ ├── ui/ # UI primitives │ └── features/ # Feature components ├── lib/ # Utility functions ├── hooks/ # Custom React hooks ├── types/ # TypeScript types ├── public/ # Static assets └── vercel.json # Vercel configuration ``` ## Next.js App Router Guidelines ### File Naming Conventions - Use lowercase with dashes for directories (e.g., `components/auth-wizard`) - Prefer named exports for components and functions - Use `page.tsx` for route pages, `layout.tsx` for layouts - Use `loading.tsx` for loading states, `error.tsx` for error boundaries ### Server Components (Default) ```typescript // app/users/page.tsx import { getUsers } from '@/lib/data'; export default async function UsersPage() { const users = await getUsers(); return (

Users

); } ``` ### Client Components (When Needed) ```typescript 'use client'; import { useState } from 'react'; export function Counter() { const [count, setCount] = useState(0); return ( ); } ``` ## TypeScript Standards ### Type Definitions ```typescript // Use interfaces over types for object shapes interface User { id: string; name: string; email: string; createdAt: Date; } // Use types for unions and complex types type Status = 'pending' | 'active' | 'inactive'; // Avoid enums; use const objects instead const STATUS = { PENDING: 'pending', ACTIVE: 'active', INACTIVE: 'inactive', } as const; type StatusValue = typeof STATUS[keyof typeof STATUS]; ``` ### Component Props ```typescript interface ButtonProps { children: React.ReactNode; variant?: 'primary' | 'secondary' | 'ghost'; size?: 'sm' | 'md' | 'lg'; disabled?: boolean; onClick?: () => void; } export function Button({ children, variant = 'primary', size = 'md', disabled = false, onClick, }: ButtonProps) { // Implementation } ``` ## API Routes ### Route Handlers ```typescript // app/api/users/route.ts import { NextRequest, NextResponse } from 'next/server'; import { z } from 'zod'; const CreateUserSchema = z.object({ name: z.string().min(1), email: z.string().email(), }); export async function GET(request: NextRequest) { const users = await getUsers(); return NextResponse.json(users); } export async function POST(request: NextRequest) { try { const body = await request.json(); const validated = CreateUserSchema.parse(body); const user = await createUser(validated); return NextResponse.json(user, { status: 201 }); } catch (error) { if (error instanceof z.ZodError) { return NextResponse.json( { error: 'Validation failed', details: error.errors }, { status: 400 } ); } return NextResponse.json( { error: 'Internal server error' }, { status: 500 } ); } } ``` ### Edge Runtime ```typescript // app/api/edge-function/route.ts export const runtime = 'edge'; export async function GET(request: Request) { return new Response(JSON.stringify({ message: 'Hello from the edge!' }), { headers: { 'Content-Type': 'application/json' }, }); } ``` ## Vercel AI SDK Integration ### Streaming Chat UI ```typescript 'use client'; import { useChat } from 'ai/react'; export function Chat() { const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({ api: '/api/chat', }); return (
{messages.map(message => (

{message.content}

))}
); } ``` ### AI API Route ```typescript // app/api/chat/route.ts import { openai } from '@ai-sdk/openai'; import { streamText } from 'ai'; export async function POST(request: Request) { const { messages } = await request.json(); const result = await streamText({ model: openai('gpt-4-turbo'), messages, system: 'You are a helpful assistant.', }); return result.toDataStreamResponse(); } ``` ### Error Handling for AI ```typescript import { openai } from '@ai-sdk/openai'; import { streamText } from 'ai'; export async function POST(request: Request) { try { const { messages } = await request.json(); const result = await streamText({ model: openai('gpt-4-turbo'), messages, }); return result.toDataStreamResponse(); } catch (error) { // Handle rate limiting if (error.message?.includes('rate limit')) { return new Response('Rate limit exceeded. Please try again later.', { status: 429, }); } // Handle quota exceeded if (error.message?.includes('quota')) { return new Response('API quota exceeded.', { status: 402 }); } // Fallback to alternative model console.error('Primary model failed:', error); return new Response('Service temporarily unavailable.', { status: 503 }); } } ``` ## Data Fetching ### Server-Side Data Fetching ```typescript // Fetch data in Server Components async function getData() { const res = await fetch('https://api.example.com/data', { next: { revalidate: 3600 }, // Cache for 1 hour }); if (!res.ok) { throw new Error('Failed to fetch data'); } return res.json(); } export default async function Page() { const data = await getData(); return
{/* Render data */}
; } ``` ### URL State Management ```typescript // Use URL query parameters for server state import { useSearchParams, useRouter } from 'next/navigation'; export function Filters() { const searchParams = useSearchParams(); const router = useRouter(); const updateFilter = (key: string, value: string) => { const params = new URLSearchParams(searchParams); params.set(key, value); router.push(`?${params.toString()}`); }; return (/* Filter UI */); } ``` ## Performance Optimization ### Image Optimization ```typescript import Image from 'next/image'; export function Hero() { return ( Hero image ); } ``` ### Dynamic Imports ```typescript import dynamic from 'next/dynamic'; // Lazy load heavy components const HeavyChart = dynamic(() => import('@/components/heavy-chart'), { loading: () =>
Loading chart...
, ssr: false, // Disable SSR if needed }); ``` ### Suspense Boundaries ```typescript import { Suspense } from 'react'; export default function Page() { return (

Dashboard

}>
); } ``` ## Error Handling ### Error Boundaries ```typescript // app/error.tsx 'use client'; export default function Error({ error, reset, }: { error: Error & { digest?: string }; reset: () => void; }) { return (

Something went wrong!

); } ``` ### Global Error Handler ```typescript // app/global-error.tsx 'use client'; export default function GlobalError({ error, reset, }: { error: Error & { digest?: string }; reset: () => void; }) { return (

Something went wrong!

); } ``` ## Vercel Configuration ### vercel.json ```json { "framework": "nextjs", "regions": ["iad1"], "crons": [ { "path": "/api/cron/cleanup", "schedule": "0 0 * * *" } ], "headers": [ { "source": "/api/(.*)", "headers": [ { "key": "Access-Control-Allow-Origin", "value": "*" } ] } ] } ``` ### Environment Variables ```typescript // Use environment variables for sensitive data const apiKey = process.env.API_KEY; const publicUrl = process.env.NEXT_PUBLIC_APP_URL; // Validate required env vars if (!apiKey) { throw new Error('API_KEY environment variable is required'); } ``` ## Deployment ### Preview Deployments - Every pull request gets a preview deployment - Use preview URLs for testing and review - Share preview links with stakeholders ### Production Deployment ```bash # Connect repo to Vercel (one-click from GitHub) # Or use Vercel CLI vercel --prod ``` ### Edge Config ```typescript import { get } from '@vercel/edge-config'; export async function getFeatureFlag(flag: string) { const flags = await get('featureFlags'); return flags?.[flag] ?? false; } ``` ## UI and Styling ### Tailwind CSS Setup ```typescript // Use mobile-first responsive design export function Card({ children }: { children: React.ReactNode }) { return (
{children}
); } ``` ### Shadcn UI Components ```typescript import { Button } from '@/components/ui/button'; import { Dialog, DialogContent, DialogTrigger } from '@/components/ui/dialog'; export function ConfirmDialog() { return (

Are you sure?

); } ``` ## Accessibility ### Best Practices ```typescript export function AccessibleButton() { return ( ); } ``` ## Common Pitfalls to Avoid 1. Using 'use client' unnecessarily 2. Not implementing proper error boundaries 3. Ignoring Core Web Vitals optimization 4. Not using TypeScript strictly 5. Hardcoding environment variables 6. Missing Suspense boundaries for async components 7. Not optimizing images 8. Ignoring accessibility requirements