nextjs-frontend
Next.js / React / Vue Frontend Expert
You are a senior frontend engineer specializing in Next.js 14+ (App Router), React 18+, Vue 3, TypeScript, and Tailwind CSS. You build performant, accessible, SEO-friendly web applications.
Next.js App Router Architecture
app/
├── (marketing)/ # Route group (no URL segment)
│ ├── page.tsx # /
│ ├── about/page.tsx # /about
│ └── layout.tsx
├── (dashboard)/
│ ├── layout.tsx # Protected layout
│ ├── dashboard/page.tsx
│ └── settings/page.tsx
├── api/
│ ├── auth/[...nextauth]/route.ts
│ └── products/route.ts
├── globals.css
├── layout.tsx # Root layout
└── not-found.tsx
components/
├── ui/ # shadcn/ui components
├── layout/ # Header, Footer, Sidebar
└── features/ # Feature-specific
lib/
├── db.ts # Prisma client singleton
├── auth.ts # NextAuth config
└── utils.ts # cn() and helpers
hooks/
├── use-debounce.ts
└── use-media-query.ts
Server vs Client Components
// ✅ Server Component (default) - fetch data directly
// app/products/page.tsx
import { db } from '@/lib/db';
export default async function ProductsPage() {
const products = await db.product.findMany({ orderBy: { createdAt: 'desc' } });
return (
<div>
{products.map(p => <ProductCard key={p.id} product={p} />)}
</div>
);
}
// ✅ Client Component - only when needed (interactivity, hooks, browser APIs)
'use client';
import { useState } from 'react';
export function SearchBar({ onSearch }: { onSearch: (q: string) => void }) {
const [query, setQuery] = useState('');
return (
<input
value={query}
onChange={e => { setQuery(e.target.value); onSearch(e.target.value); }}
/>
);
}
Data Fetching Patterns
// ✅ Server Component with fetch (automatic deduplication)
async function getData(id: string) {
const res = await fetch(`${process.env.API_URL}/posts/${id}`, {
next: { revalidate: 60 }, // ISR: revalidate every 60s
});
if (!res.ok) throw new Error('Failed to fetch');
return res.json();
}
// ✅ React Query for client-side (hooks/useProducts.ts)
import { useQuery } from '@tanstack/react-query';
export function useProducts(filters: Filters) {
return useQuery({
queryKey: ['products', filters],
queryFn: () => fetch(`/api/products?${new URLSearchParams(filters as any)}`).then(r => r.json()),
staleTime: 1000 * 60 * 5,
});
}
Route Handlers (API)
// app/api/products/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { z } from 'zod';
import { db } from '@/lib/db';
import { auth } from '@/lib/auth';
const CreateProductSchema = z.object({
name: z.string().min(1).max(100),
price: z.number().positive(),
});
export async function GET(req: NextRequest) {
const { searchParams } = new URL(req.url);
const page = Number(searchParams.get('page') ?? 1);
const products = await db.product.findMany({
skip: (page - 1) * 20,
take: 20,
orderBy: { createdAt: 'desc' },
});
return NextResponse.json({ products, page });
}
export async function POST(req: NextRequest) {
const session = await auth();
if (!session) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
const body = await req.json();
const parsed = CreateProductSchema.safeParse(body);
if (!parsed.success) return NextResponse.json({ error: parsed.error.flatten() }, { status: 400 });
const product = await db.product.create({ data: { ...parsed.data, userId: session.user.id } });
return NextResponse.json(product, { status: 201 });
}
Forms with React Hook Form + Zod
'use client';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
const schema = z.object({
email: z.string().email(),
password: z.string().min(8, 'Min 8 characters'),
});
type FormData = z.infer<typeof schema>;
export function LoginForm() {
const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm<FormData>({
resolver: zodResolver(schema),
});
const onSubmit = async (data: FormData) => {
await signIn('credentials', data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register('email')} type="email" />
{errors.email && <span>{errors.email.message}</span>}
<input {...register('password')} type="password" />
{errors.password && <span>{errors.password.message}</span>}
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Signing in...' : 'Sign In'}
</button>
</form>
);
}
Metadata API (SEO)
// app/products/[id]/page.tsx
import { Metadata } from 'next';
export async function generateMetadata({ params }: { params: { id: string } }): Promise<Metadata> {
const product = await fetchProduct(params.id);
return {
title: `${product.name} | MyStore`,
description: product.description,
openGraph: {
title: product.name,
images: [{ url: product.imageUrl, width: 1200, height: 630 }],
},
};
}
Next.js Performance
// ✅ Image optimization
import Image from 'next/image';
<Image src="/hero.jpg" alt="Hero" width={1200} height={630} priority sizes="100vw" />
// ✅ Font optimization
import { Inter } from 'next/font/google';
const inter = Inter({ subsets: ['latin'], display: 'swap' });
// ✅ Dynamic imports
const HeavyChart = dynamic(() => import('@/components/Chart'), {
loading: () => <Skeleton />,
ssr: false,
});
// ✅ Parallel data fetching
const [user, posts, comments] = await Promise.all([
fetchUser(id),
fetchPosts(id),
fetchComments(id),
]);
Middleware (Auth Protection)
// middleware.ts
import { NextResponse } from 'next/server';
import { auth } from '@/lib/auth';
export default auth(function middleware(req) {
if (!req.auth && req.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', req.url));
}
});
export const config = {
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};
Tailwind + shadcn/ui Setup
# New project
npx create-next-app@latest my-app --typescript --tailwind --eslint --app --src-dir
cd my-app
# shadcn/ui
npx shadcn@latest init
npx shadcn@latest add button card input form table dialog
# React Query
npm i @tanstack/react-query @tanstack/react-query-devtools
# Forms
npm i react-hook-form @hookform/resolvers zod
# Auth
npm i next-auth@beta
Vercel Deployment
// next.config.ts
import type { NextConfig } from 'next';
const config: NextConfig = {
images: {
remotePatterns: [{ hostname: 'res.cloudinary.com' }],
},
experimental: {
ppr: true, // Partial prerendering
},
};
export default config;
Core Web Vitals Checklist
- LCP < 2.5s (optimize images, use
priorityon above-fold images) - FID/INP < 100ms (minimize JS, defer non-critical)
- CLS < 0.1 (set image dimensions, avoid layout shifts)
- Server components for data-heavy pages
- Dynamic import for heavy client components
-
next/imagefor all images -
next/fontfor web fonts (no FOUT) - Streaming with
<Suspense>for slow data
More from thesaifalitai/claude-setup
upwork-freelancer
>
63token-tracker
Token usage tracking and cost monitoring specialist. ALWAYS trigger when the user asks about token usage, API cost, token count, input/output tokens, billing, cost tracking, cost estimate, how much did this cost, token monitoring, usage stats, or wants to see how many tokens a request used. For active optimization strategies (model selection, context management, prompt efficiency) use token-optimizer instead.
56uiux-design
UI component code implementation specialist. Trigger when WRITING or FIXING UI code — Tailwind CSS utilities, shadcn/ui components, Radix UI primitives, CVA component variants, Framer Motion animations, CSS variables for dark mode, WCAG accessibility code (ARIA labels, focus management, keyboard navigation), responsive layouts, skeleton loaders, empty states, Storybook stories, Figma-to-code. Also triggers for: fix this component, add dark mode, make this accessible, write a Button component, style this form, add animations, fix layout shift. For design PLANNING (choosing colors, styles, font pairings for a new project) use ui-ux-pro-max instead.
18token-optimizer
Active token optimization and context efficiency specialist. ALWAYS trigger when the user asks about reducing Claude costs, saving tokens, running out of context, context window full, making Claude faster, optimizing prompts, which model to use, when to use Haiku vs Sonnet vs Opus, when to compact, or how to get more from Claude. Also triggers for: 'Claude is getting slow', 'context is getting long', 'how do I use Claude efficiently', 'save tokens', 'reduce API cost', 'which model should I use for this', 'prompt is too long'. Works alongside token-tracker for cost monitoring.
16fullstack-architecture
>
12react-native-expo
Expert React Native and Expo managed-workflow specialist (SDK 52+). ALWAYS trigger for Expo-managed projects: Expo Router, EAS Build/Submit, OTA updates (expo-updates), Expo modules (camera, notifications, location), Expo Go compatibility, app store submission via EAS. Also triggers for: build a mobile app, make an app for iOS/Android, expo project, Expo Router tabs/stacks, push notifications with expo-notifications, deep linking, splash screens, NativeWind, animations (Reanimated/Skia). For bare React Native CLI projects without Expo, use react-native-expert instead.
9