Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Next.js 15 애플리케이션을 위한 프론트엔드 개발 가이드라인. React 19, TypeScript, Shadcn/ui, Tailwind CSS를 사용한 모던 패턴. Server Components, Client Components, App Router, 파일 구조, Shadcn/ui 컴포넌트, 성능 최적화, TypeScript 모범 사례 포함. 컴포넌트, 페이지, 기능 생성, 데이터 페칭, 스타일링, 라우팅, 프론트엔드 코드 작업 시 사용.
.claude/skills/aiskillstore-frontend-dev-guidelines/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 49% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 34% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 168% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 161% | 0% |
| case-18 | ✓→✓ | = Same ✓ | 106% | 0% |
> 📋 OPINIONATED SCAFFOLD: Modern Next.js + React 19 + shadcn/ui stack > > Default Stack: > - Framework: Next.js 14+ (App Router) > - UI Library: React 19 > - Components: shadcn/ui (Radix primitives) > - Styling: Tailwind CSS > - Forms: React Hook Form + Zod validation > - State: React Context + TanStack Query for server state > - Data Fetching: Server Components, Server Actions, TanStack Query > - Language: TypeScript > - Deployment: Vercel > > To customize: Run /customize-scaffold frontend or use the scaffold-customizer agent > to adapt for Vue, Angular, Svelte, vanilla React, or other frameworks/UI libraries.
Modern frontend development with Next.js 14+ App Router, React 19, Server Components, and shadcn/ui. Focus on performance, type safety, and excellent UX with Supabase integration.
app/[route]/page.tsxloading.tsx for loading stateerror.tsx for error handling'use client' ONLY if using hooks/eventscn() for conditional classestypescript// ✅ Server Component (default) export default async function Page() { const data = await getData() // Direct fetch, no hooks return <div>{data.title}</div> } // ❌ Don't add 'use client' unless needed 'use client' // Only add if using hooks/events!
typescript// ✅ Client Component (interactivity) 'use client' import { useState } from 'react' import { Button } from '@/components/ui/button' export function Counter() { const [count, setCount] = useState(0) return <Button onClick={() => setCount(count + 1)}>{count}</Button> }
typescript// app/actions.ts 'use server' import { revalidatePath } from 'next/cache' import { createServerClient } from '@/lib/supabase/server' export async function createPost(formData: FormData) { const supabase = createServerClient() const { data, error } = await supabase .from('posts') .insert({ title: formData.get('title') }) if (error) throw error revalidatePath('/posts') return { success: true, data } } // In component: <form action={createPost}> <input name="title" /> <button type="submit">Create</button> </form>
typescriptimport { Button } from '@/components/ui/button' import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card' import { Input } from '@/components/ui/input' export function Example() { return ( <Card> <CardHeader> <CardTitle>Title</CardTitle> </CardHeader> <CardContent> <Input placeholder="Type..." /> <Button>Submit</Button> </CardContent> </Card> ) }
typescriptimport { cn } from '@/lib/utils' export function Component({ className, variant }: Props) { return ( <div className={cn( 'rounded-lg bg-white p-4 shadow', variant === 'primary' && 'border-2 border-blue-500', className )}> Content </div> ) }
typescript'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) }) export function LoginForm() { const form = useForm({ resolver: zodResolver(schema) }) async function onSubmit(values: z.infer<typeof schema>) { // Handle submission } return ( <form onSubmit={form.handleSubmit(onSubmit)}> <input {...form.register('email')} /> <input {...form.register('password')} type="password" /> <button type="submit">Login</button> </form> ) }
typescript// Server Component import { createServerClient } from '@/lib/supabase/server' export default async function Page() { const supabase = createServerClient() const { data } = await supabase.from('posts').select('*') return <div>{/* Render data */}</div> } // Client Component 'use client' import { createClient } from '@/lib/supabase/client' export function ClientComponent() { const supabase = createClient() // Use with hooks... }
app/
├── (auth)/ # Route group (auth pages)
│ ├── login/
│ │ └── page.tsx
│ └── register/
│ └── page.tsx
├── dashboard/
│ ├── page.tsx # /dashboard
│ ├── loading.tsx # Loading UI
│ ├── error.tsx # Error UI
│ └── posts/
│ ├── page.tsx # /dashboard/posts
│ └── [id]/
│ └── page.tsx # /dashboard/posts/[id]
├── api/
│ └── webhook/
│ └── route.ts # API route
├── layout.tsx # Root layout
└── page.tsx # Home page
components/
├── ui/ # shadcn/ui components
│ ├── button.tsx
│ ├── card.tsx
│ └── input.tsx
└── dashboard/ # Feature components
├── post-list.tsx
└── post-card.tsx
lib/
├── supabase/
│ ├── client.ts # Client-side
│ └── server.ts # Server-side
└── utils.ts # cn() utility> 📝 Note: This is a scaffold skill with instructional examples. Generate additional resources as needed for your specific project following these patterns.
When you need guidance on a specific topic, ask Claude to generate examples:
How to request: "Show me examples of topic] with Next.js + shadcn/ui"
typescript// Next.js import { Metadata } from 'next' import Link from 'next/link' import Image from 'next/image' import { redirect, notFound } from 'next/navigation' import { revalidatePath, revalidateTag } from 'next/cache' // React (Client Components) 'use client' import { useState, useEffect, useCallback, useMemo, useTransition } from 'react' // shadcn/ui import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Card, CardHeader, CardTitle, CardContent, CardFooter } from '@/components/ui/card' import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from '@/components/ui/form' // Tailwind import { cn } from '@/lib/utils' // Supabase import { createClient } from '@/lib/supabase/client' import { createServerClient } from '@/lib/supabase/server' // Forms import { useForm } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' import { z } from 'zod'
typescriptexport const metadata: Metadata = { title: 'Page Title', description: 'Page description', } // Dynamic metadata export async function generateMetadata({ params }): Promise<Metadata> { return { title: `Post ${params.id}`, } }
tsx// app/dashboard/loading.tsx export default function Loading() { return <div>Loading...</div> }
tsx// app/dashboard/error.tsx 'use client' export default function Error({ error, reset, }: { error: Error reset: () => void }) { return ( <div> <h2>Something went wrong!</h2> <button onClick={reset}>Try again</button> </div> ) }
typescript// app/api/posts/route.ts import { NextResponse } from 'next/server' export async function GET(request: Request) { const posts = await getPosts() return NextResponse.json(posts) } export async function POST(request: Request) { const body = await request.json() const post = await createPost(body) return NextResponse.json(post, { status: 201 }) }
❌ Using 'use client' on every component ❌ Not using Server Components for data fetching ❌ Inline styles instead of Tailwind ❌ Not using shadcn/ui components ❌ useState for server data (use Server Components) ❌ Direct database queries in Client Components ❌ Missing loading/error states ❌ Not using TypeScript properly
Not using Next.js? Adapt the patterns:
Not using shadcn/ui? Replace with:
Keep the principles:
Skill Status: SCAFFOLD ✅ Line Count: < 500 ✅ Progressive Disclosure: Instructional patterns + generate on-demand ✅
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 18,525 | 16,209 | -13% | 1 | 1 | 0% | 4,325 | 6,445 | +49% | 0 | 0 | — |
case-02 | fail→pass | 16,662 | 11,437 | -31% | 1 | 1 | 0% | 4,126 | 5,516 | +34% | 0 | 0 | — |
case-03 | fail→fail | 18,980 | 31,951 | +68% | 1 | 1 | 0% | 3,747 | 5,826 | +55% | 0 | 0 | — |
case-18 | pass→pass | 12,627 | 10,947 | -13% | 1 | 1 | 0% | 2,316 | 4,781 | +106% | 0 | 0 | — |
case-04 | pass→pass | 14,779 | 13,881 | -6% | 1 | 1 | 0% | 2,954 | 5,786 | +96% | 0 | 0 | — |
case-05 | pass→pass | 13,307 | 11,169 | -16% | 1 | 1 | 0% | 2,886 | 5,443 | +89% | 0 | 0 | — |
case-06 | pass→pass | 8,177 | 4,692 | -43% | 1 | 1 | 0% | 1,753 | 3,760 | +114% | 0 | 0 | — |
case-07 | fail→pass | 10,991 | 13,527 | +23% | 1 | 1 | 0% | 2,151 | 5,763 | +168% | 0 | 0 | — |
case-08 | fail→fail | 9,708 | 8,145 | -16% | 1 | 1 | 0% | 2,070 | 4,572 | +121% | 0 | 0 | — |
case-09 | fail→pass | 9,914 | 11,501 | +16% | 1 | 1 | 0% | 2,060 | 5,377 | +161% | 0 | 0 | — |
case-10 | pass→pass | 3,627 | 3,840 | +6% | 1 | 1 | 0% | 756 | 3,554 | +370% | 0 | 0 | — |
case-11 | pass→pass | 3,384 | 6,246 | +85% | 1 | 1 | 0% | 653 | 3,792 | +481% | 0 | 0 | — |
case-12 | pass→pass | 8,825 | 8,471 | -4% | 1 | 1 | 0% | 1,933 | 4,700 | +143% | 0 | 0 | — |
case-13 | pass→pass | 14,312 | 10,682 | -25% | 1 | 1 | 0% | 2,526 | 4,839 | +92% | 0 | 0 | — |
case-14 | pass→pass | 13,969 | 12,037 | -14% | 1 | 1 | 0% | 2,416 | 5,222 | +116% | 0 | 0 | — |
case-15 | pass→pass | 8,861 | 8,557 | -3% | 1 | 1 | 0% | 1,676 | 4,513 | +169% | 0 | 0 | — |
case-16 | pass→pass | 13,298 | 12,723 | -4% | 1 | 1 | 0% | 2,174 | 5,124 | +136% | 0 | 0 | — |
case-17 | pass→pass | 9,489 | 9,896 | +4% | 1 | 1 | 0% | 1,848 | 4,798 | +160% | 0 | 0 | — |
case-19 | pass→pass | 11,025 | 27,782 | +152% | 1 | 1 | 0% | 2,029 | 4,473 | +120% | 0 | 0 | — |
case-20 | pass→pass | 18,325 | 17,921 | -2% | 1 | 1 | 0% | 3,907 | 6,741 | +73% | 0 | 0 | — |
case-21 | pass→pass | 16,574 | 18,327 | +11% | 1 | 1 | 0% | 3,639 | 5,957 | +64% | 0 | 0 | — |
case-22 | pass→pass | 17,240 | 14,395 | -17% | 1 | 1 | 0% | 3,478 | 5,908 | +70% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +18 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
| Model | Method | Date | Lift |
|---|---|---|---|
| gemini-3.6-flash | verified | 8/21/2026 | +9% |
Other measured skills in the registry, with their headline benchmark lift.