Skill Library

advanced Code Development

Next.js Fullstack Developer

Expert-level Next.js 14/15 development with App Router, React Server Components, TypeScript, Tailwind CSS, and modern fullstack patterns including API routes, authentication, and database integration.

When to Use This Skill

  • Building new Next.js applications
  • Migrating from Pages Router to App Router
  • Implementing server-side rendering strategies
  • Creating API routes and server actions
  • Setting up authentication and authorization
  • Optimizing Next.js application performance

How to use this skill

1. Copy the AI Core Logic from the Instructions tab below.

2. Paste it into your AI's System Instructions or as your first message.

3. Provide your raw data or requirements as requested by the AI.

#nextjs#react#typescript#fullstack#tailwind#web-development

System Directives

## Core Principles ### Project Structure ``` my-next-app/ ├── src/ │ ├── app/ # App Router │ │ ├── (auth)/ # Route groups │ │ │ ├── login/ │ │ │ └── register/ │ │ ├── api/ # API Routes │ │ │ └── [...]/ │ │ ├── dashboard/ │ │ │ ├── page.tsx │ │ │ ├── layout.tsx │ │ │ └── loading.tsx │ │ ├── layout.tsx # Root layout │ │ ├── page.tsx # Home page │ │ ├── error.tsx # Error boundary │ │ ├── not-found.tsx # 404 page │ │ └── global-error.tsx # Global error │ ├── components/ │ │ ├── ui/ # shadcn/ui components │ │ └── features/ # Feature components │ ├── lib/ │ │ ├── actions/ # Server actions │ │ ├── db/ # Database utilities │ │ └── utils.ts # Utility functions │ ├── hooks/ # Custom React hooks │ ├── types/ # TypeScript types │ └── styles/ # Global styles ├── public/ # Static assets └── next.config.js ``` ## App Router Patterns ### Page Components ```tsx // src/app/dashboard/page.tsx import { Suspense } from 'react'; import { getUser } from '@/lib/actions/user'; import { DashboardSkeleton } from '@/components/skeletons'; import { Dashboard } from '@/components/features/dashboard'; // Metadata for SEO export const metadata = { title: 'Dashboard | MyApp', description: 'Your personal dashboard' }; // Server Component by default export default async function DashboardPage() { const user = await getUser(); return ( <Suspense fallback={<DashboardSkeleton />}> <Dashboard user={user} /> </Suspense> ); } ``` ### Layouts ```tsx // src/app/layout.tsx import { Inter } from 'next/font/google'; import { ThemeProvider } from '@/components/theme-provider'; import { Toaster } from '@/components/ui/toaster'; import './globals.css'; const inter = Inter({ subsets: ['latin'] }); export const metadata = { title: { template: '%s | MyApp', default: 'MyApp' }, description: 'A modern Next.js application' }; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html lang="en" suppressHydrationWarning> <body className={inter.className}> <ThemeProvider attribute="class" defaultTheme="system" enableSystem> {children} <Toaster /> </ThemeProvider> </body> </html> ); } ``` ### Loading States ```tsx // src/app/dashboard/loading.tsx import { Skeleton } from '@/components/ui/skeleton'; export default function DashboardLoading() { return ( <div className="space-y-4"> <Skeleton className="h-8 w-[250px]" /> <Skeleton className="h-[400px] w-full" /> </div> ); } ``` ### Error Handling ```tsx // src/app/dashboard/error.tsx 'use client'; import { useEffect } from 'react'; import { Button } from '@/components/ui/button'; export default function DashboardError({ error, reset }: { error: Error & { digest?: string }; reset: () => void; }) { useEffect(() => { console.error(error); }, [error]); return ( <div className="flex flex-col items-center justify-center min-h-[400px] space-y-4"> <h2 className="text-xl font-semibold">Something went wrong!</h2> <Button onClick={() => reset()}>Try again</Button> </div> ); } ``` ## Server Actions ### Form Actions ```tsx // src/lib/actions/user.ts 'use server'; import { revalidatePath } from 'next/cache'; import { redirect } from 'next/navigation'; import { z } from 'zod'; import { db } from '@/lib/db'; const UserSchema = z.object({ name: z.string().min(2).max(50), email: z.string().email() }); export async function updateUser(formData: FormData) { const validatedFields = UserSchema.safeParse({ name: formData.get('name'), email: formData.get('email') }); if (!validatedFields.success) { return { errors: validatedFields.error.flatten().fieldErrors, message: 'Invalid fields' }; } const { name, email } = validatedFields.data; try { await db.user.update({ where: { id: userId }, data: { name, email } }); } catch (error) { return { message: 'Database error' }; } revalidatePath('/dashboard'); redirect('/dashboard'); } ``` ### Using Server Actions ```tsx // src/components/features/user-form.tsx 'use client'; import { useFormState, useFormStatus } from 'react-dom'; import { updateUser } from '@/lib/actions/user'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; function SubmitButton() { const { pending } = useFormStatus(); return ( <Button type="submit" disabled={pending}> {pending ? 'Saving...' : 'Save Changes'} </Button> ); } export function UserForm({ user }: { user: User }) { const [state, action] = useFormState(updateUser, null); return ( <form action={action} className="space-y-4"> <div> <Input name="name" defaultValue={user.name} placeholder="Name" /> {state?.errors?.name && <p className="text-sm text-destructive">{state.errors.name}</p>} </div> <div> <Input name="email" type="email" defaultValue={user.email} placeholder="Email" /> {state?.errors?.email && <p className="text-sm text-destructive">{state.errors.email}</p>} </div> <SubmitButton /> </form> ); } ``` ## API Routes ```tsx // src/app/api/users/route.ts import { NextRequest, NextResponse } from 'next/server'; import { z } from 'zod'; import { db } from '@/lib/db'; import { auth } from '@/lib/auth'; export async function GET(request: NextRequest) { const session = await auth(); if (!session) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } const searchParams = request.nextUrl.searchParams; const page = parseInt(searchParams.get('page') || '1'); const limit = parseInt(searchParams.get('limit') || '10'); const users = await db.user.findMany({ take: limit, skip: (page - 1) * limit, orderBy: { createdAt: 'desc' } }); return NextResponse.json({ users, page, limit }); } export async function POST(request: NextRequest) { const session = await auth(); if (!session?.user.isAdmin) { return NextResponse.json({ error: 'Forbidden' }, { status: 403 }); } const body = await request.json(); const validated = UserSchema.safeParse(body); if (!validated.success) { return NextResponse.json({ error: validated.error.flatten() }, { status: 400 }); } const user = await db.user.create({ data: validated.data }); return NextResponse.json(user, { status: 201 }); } ``` ## Data Fetching Patterns ### Server Component Fetching ```tsx // Direct database access in Server Components async function UserList() { const users = await db.user.findMany({ orderBy: { createdAt: 'desc' }, take: 10 }); return ( <ul> {users.map((user) => ( <li key={user.id}>{user.name}</li> ))} </ul> ); } ``` ### Parallel Data Fetching ```tsx export default async function DashboardPage() { // Fetch in parallel const [user, posts, notifications] = await Promise.all([ getUser(), getPosts(), getNotifications() ]); return <Dashboard user={user} posts={posts} notifications={notifications} />; } ``` ### Caching Strategies ```tsx // Revalidate every hour export const revalidate = 3600; // Or use cache with tags import { unstable_cache } from 'next/cache'; const getCachedUser = unstable_cache( async (id: string) => db.user.findUnique({ where: { id } }), ['user'], { tags: ['user'], revalidate: 3600 } ); ``` ## TypeScript Best Practices ```tsx // src/types/index.ts export interface User { id: string; name: string; email: string; role: 'user' | 'admin'; createdAt: Date; } export type UserWithPosts = User & { posts: Post[]; }; // Component props types interface DashboardProps { user: User; children?: React.ReactNode; } // Server action return types type ActionResult<T> = { success: true; data: T } | { success: false; error: string }; ``` ## Performance Optimization ### Image Optimization ```tsx import Image from 'next/image'; function Avatar({ user }: { user: User }) { return ( <Image src={user.avatarUrl} alt={`${user.name}'s avatar`} width={40} height={40} className="rounded-full" placeholder="blur" blurDataURL="/placeholder-avatar.jpg" /> ); } ``` ### Dynamic Imports ```tsx import dynamic from 'next/dynamic'; const HeavyChart = dynamic(() => import('@/components/features/heavy-chart'), { loading: () => <ChartSkeleton />, ssr: false // Disable SSR for client-only components }); ``` ## Best Practices Summary 1. **Server Components First**: Default to Server Components, add 'use client' only when needed 2. **Colocate Files**: Keep related files together (page, loading, error) 3. **Validate Inputs**: Always validate with Zod or similar 4. **Type Everything**: Leverage TypeScript fully 5. **Use Server Actions**: Prefer over API routes for form submissions 6. **Optimize Images**: Always use next/image 7. **Cache Strategically**: Use appropriate revalidation times 8. **Handle Errors**: Implement error.tsx at appropriate levels 9. **SEO Metadata**: Export metadata from every page ## Related Resources - [Next.js Documentation](https://nextjs.org/docs) - [React Server Components](https://react.dev/reference/react/use-server) - [Vercel AI SDK](https://sdk.vercel.ai/) - [shadcn/ui](https://ui.shadcn.com/)

Procedural Integration

This skill is formatted as a set of persistent system instructions. When integrated, it provides the AI model with specialized workflows and knowledge constraints for Code Development.

Skill Actions


Model Compatibility
🤖 Claude Opus🤖 Claude 3.5 Sonnet🤖 GPT-4
Code Execution: Required
MCP Tools: Optional
Footprint ~2,557 tokens