Skip to main content

nextjs-server-components

Use when next.js Server Components for optimal performance. Use when building data-intensive Next.js applications.

소스 정보

저장소
TheBushidoCollective/han
최근 소스 활동
2026년 2월 11일 17:47
감지된 SKILL.md 언어
영어
스타
198
포크
22

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
nextjs-server-components
user-invocable
false
description
Use when next.js Server Components for optimal performance. Use when building data-intensive Next.js applications.
allowed-tools
["Bash","Read"]
# Next.js Server Components Master Server Components in Next.js to build high-performance applications with server-side rendering and data fetching. ## Server Components Basics In Next.js App Router, all components are Server Components by default: ```typescript // app/posts/page.tsx (Server Component by default) async function getPosts() { const res = await fetch('https://api.example.com/posts', { next: { revalidate: 3600 } // Cache for 1 hour }); if (!res.ok) throw new Error('Failed to fetch posts'); return res.json(); } export default async function Posts() { const posts = await getPosts(); return ( <div> <h1>Blog Posts</h1> {posts.map((post: Post) => ( <article key={post.id}> <h2>{post.title}</h2> <p>{post.content}</p> <span>{new Date(post.date).toLocaleDateString()}</span> </article> ))} </div> ); } // Direct database access (server-only) import { db } from '@/lib/db'; export default async function Users() { const users = await db.user.findMany({ select: { id: true, name: true, email: true } }); return ( <div> {users.map(user => ( <div key={user.id}> {user.name} - {user.email} </div> ))} </div> ); } ``` ## Server vs Client Components Decision Tree ```typescript // Use Server Components when: // - Fetching data // - Accessing backend resources directly // - Keeping sensitive information on server // - Keeping large dependencies on server // Server Component (default) export default async function ServerComp() { const data = await fetchData(); return <div>{data}</div>; } // Use Client Components when: // - Using interactivity (onClick, onChange, etc.) // - Using state or lifecycle hooks (useState, useEffect) // - Using browser-only APIs (localStorage, window, etc.) // - Using custom hooks that depend on state/effects // - Using React Context // Client Component 'use client'; import { useState } from 'react'; export default function ClientComp() { const [count, setCount] = useState(0); return ( <button onClick={() => setCount(count + 1)}> Count: {count} </button> ); } // Composition: Server Component with Client Component export default async function Page() { const data = await fetchData(); // Server-side return ( <div> <ServerContent data={data} /> <InteractiveButton /> {/* Client Component */} </div> ); } ``` ## Server Actions for Mutations ```typescript // app/actions.ts - Server Actions 'use server'; import { revalidatePath } from 'next/cache'; import { redirect } from 'next/navigation'; export async function createPost(formData: FormData) { const title = formData.get('title') as string; const content = formData.get('content') as string; const post = await db.post.create({ data: { title, content } }); revalidatePath('/posts'); redirect(`/posts/${post.id}`); } export async function updatePost(id: string, formData: FormData) { const title = formData.get('title') as string; const content = formData.get('content') as string; await db.post.update({ where: { id }, data: { title, content } }); revalidatePath(`/posts/${id}`); return { success: true }; } export async function deletePost(id: string) { await db.post.delete({ where: { id } }); revalidatePath('/posts'); } // app/posts/new/page.tsx - Using Server Actions export default function NewPost() { return ( <form action={createPost}> <input name="title" placeholder="Title" required /> <textarea name="content" placeholder="Content" required /> <button type="submit">Create Post</button> </form> ); } // With client-side enhancement 'use client'; import { useFormStatus, useFormState } from 'react-dom'; import { createPost } from './actions'; function SubmitButton() { const { pending } = useFormStatus(); return ( <button type="submit" disabled={pending}> {pending ? 'Creating...' : 'Create Post'} </button> ); } export default function NewPostForm() { const [state, formAction] = useFormState(createPost, null); return ( <form action={formAction}> <input name="title" required /> <textarea name="content" required /> {state?.error && <p className="error">{state.error}</p>} <SubmitButton /> </form> ); } ``` ## Data Fetching Patterns ```typescript // Parallel data fetching async function getUser(id: string) { const res = await fetch(`/api/users/${id}`); return res.json(); } async function getUserPosts(id: string) { const res = await fetch(`/api/users/${id}/posts`); return res.json(); } export default async function UserProfile({ params }: { params: { id: string } }) { // Fetch in parallel const [user, posts] = await Promise.all([ getUser(params.id), getUserPosts(params.id) ]); return ( <div> <UserInfo user={user} /> <UserPosts posts={posts} /> </div> ); } // Sequential data fetching (when needed) export default async function Dashboard() { // Fetch user first const user = await getUser(); // Then fetch user-specific data const settings = await getUserSettings(user.id); const notifications = await getUserNotifications(user.id); return ( <div> <UserHeader user={user} settings={settings} /> <Notifications items={notifications} /> </div> ); } // Waterfall optimization with Suspense import { Suspense } from 'react'; export default function Page() { return ( <div> {/* User loads first */} <Suspense fallback={<UserSkeleton />}> <User /> </Suspense> {/* These load in parallel */} <div className="grid"> <Suspense fallback={<SettingsSkeleton />}> <Settings /> </Suspense> <Suspense fallback={<NotificationsSkeleton />}> <Notifications /> </Suspense> </div> </div> ); } ``` ## Streaming with Suspense ```typescript // app/dashboard/page.tsx import { Suspense } from 'react'; export default function Dashboard() { return ( <div> <h1>Dashboard</h1> {/* Fast component renders immediately */} <QuickStats /> {/* Slow components stream in */} <Suspense fallback={<ChartSkeleton />}> <RevenueChart /> </Suspense> <Suspense fallback={<TableSkeleton />}> <RecentOrders /> </Suspense> <Suspense fallback={<ActivitySkeleton />}> <ActivityFeed /> </Suspense> </div> ); } async function RevenueChart() { // Slow data fetch const data = await fetchRevenueData(); return <Chart data={data} />; } async function RecentOrders() { const orders = await fetchOrders({ limit: 10 }); return ( <table> {orders.map(order => ( <tr key={order.id}> <td>{order.id}</td> <td>{order.total}</td> </tr> ))} </table> ); } // Nested Suspense for granular streaming async function ActivityFeed() { return ( <div> <h2>Activity</h2> <Suspense fallback={<div>Loading comments...</div>}> <Comments /> </Suspense> <Suspense fallback={<div>Loading likes...</div>}> <Likes /> </Suspense> </div> ); } ``` ## Server Component Patterns ```typescript // Pattern 1: Data fetching in Server Component async function fetchProduct(id: string) { const product = await db.product.findUnique({ where: { id } }); return product; } export default async function ProductPage({ params }: { params: { id: string } }) { const product = await fetchProduct(params.id); return ( <div> <ProductDetails product={product} /> <AddToCartButton productId={product.id} /> {/* Client Component */} </div> ); } // Pattern 2: Server Component as data provider async function ProductWithReviews({ id }: { id: string }) { const [product, reviews] = await Promise.all([ fetchProduct(id), fetchReviews(id) ]); return ( <> <ProductInfo product={product} /> <ReviewList reviews={reviews} /> <ReviewForm productId={id} /> {/* Client Component */} </> ); } // Pattern 3: Composing Server and Client Components export default async function Page() { const data = await fetchData(); return ( <ClientWrapper> {/* Server Component children work inside Client Components */} <ServerContent data={data} /> </ClientWrapper> ); } // Pattern 4: Props passing from Server to Client export default async function Layout({ children }: { children: React.ReactNode }) { const user = await getUser(); return ( <div> <ClientHeader user={user} /> {/* Pass server data to client */} <main>{children}</main> </div> ); } ``` ## Client Component Patterns ```typescript // Pattern 1: Minimal Client Component 'use client'; import { useState } from 'react'; export function Counter({ initialCount = 0 }: { initialCount?: number }) { const [count, setCount] = useState(initialCount); return ( <button onClick={() => setCount(count + 1)}> Count: {count} </button> ); } // Pattern 2: Client Component with Server Component children 'use client'; import { useState } from 'react'; export function Tabs({ children }: { children: React.ReactNode }) { const [activeTab, setActiveTab] = useState(0); return ( <div> <div className="tabs"> <button onClick={() => setActiveTab(0)}>Tab 1</button> <button onClick={() => setActiveTab(1)}>Tab 2</button> </div> <div>{children}</div> </div> ); } // Usage: Server Component children work export default async function Page() { const data = await fetchData(); return ( <Tabs> <ServerContent data={data} /> </Tabs> ); } // Pattern 3: Client wrapper with context 'use client'; import { createContext, useContext, useState } from 'react'; const ThemeContext = createContext<{ theme: string; setTheme: (theme: string) => void; } | null>(null); export function ThemeProvider({ children }: { children: React.ReactNode }) { const [theme, setTheme] = useState('light'); return ( <ThemeContext.Provider value={{ theme, setTheme }}>
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기