Skip to main content

nextjs-server-components

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

Informations de source

Dépôt
TheBushidoCollective/han
Dernière activité de la source
11 février 2026 à 17:47
Langue détectée de SKILL.md
anglais
Étoiles
200
Forks
24

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
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 }}>
Voir sur GitHub
Ce SKILL.md est tres volumineux, SkillsMP affiche donc ici seulement la premiere section. Voir sur GitHub