skills/nextjs-frontend/SKILL.md
Expert Next.js, React.js, and Vue.js frontend development skill. ALWAYS trigger for ANY task involving Next.js (App Router, Pages Router), React.js, Vue.js, TypeScript frontend, server components, client components, SSR, SSG, ISR, SEO optimization, metadata API, Zustand/Redux/Pinia state management, React Query/SWR data fetching, form handling (React Hook Form + Zod), routing, middleware, image optimization, performance (Core Web Vitals), internationalization (i18n), or deploying to Vercel/Netlify. Also triggers for: "build a website", "create a web app", "Next.js project", "React page", "Vue component", "frontend architecture", "landing page", "dashboard UI".
npx skillsauth add thesaifalitai/claude-setup nextjs-frontendInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
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.
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 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); }}
/>
);
}
// ✅ 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,
});
}
// 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 });
}
'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>
);
}
// 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 }],
},
};
}
// ✅ 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.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).*)'],
};
# 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
// 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;
priority on above-fold images)next/image for all imagesnext/font for web fonts (no FOUT)<Suspense> for slow datadevelopment
Use when building Vue 3 applications with Composition API, Nuxt 3, or Quasar. Invoke for Pinia, TypeScript, PWA, Capacitor mobile apps, Vite configuration.
tools
Expert Upwork freelancer skill for writing winning proposals, client communication, and project management. ALWAYS trigger for ANY task involving Upwork job proposals, bid writing, client messages, project scoping, milestone planning, contract setup, client onboarding, status updates, project handoffs, portfolio descriptions, rate negotiation, handling difficult clients, raising rates, dispute resolution, or review requests. Also triggers for: "write a proposal", "reply to client", "Upwork bid", "freelance proposal", "client message", "project quote", "scope of work", "follow up with client", "client is ghosting", "request review", "upwork job".
development
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.
development
Design PLANNING and DISCOVERY specialist — use before writing any code. Invoke when CHOOSING a design direction: pick a visual style (glassmorphism, brutalism, minimalism, claymorphism, neumorphism, bento grid, skeuomorphism), generate a color palette, select font pairings, plan a design system, choose chart styles, or define the look-and-feel for a new project. Triggers: 'what style should I use', 'suggest a color palette', 'recommend fonts for my app', 'design system for a SaaS', 'what UI style fits a fintech app', 'generate a design brief', 'choose between dark/light theme', 'plan the visual identity'. Covers 50 styles, 97 palettes, 57 font pairings across React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind, shadcn/ui stacks. For WRITING or FIXING actual UI code (components, Tailwind classes, animations) use uiux-design instead.