重要前提
安装AI Skills的关键前提是:必须科学上网,且开启TUN模式,这一点至关重要,直接决定安装能否顺利完成,在此郑重提醒三遍:科学上网,科学上网,科学上网。查看完整安装教程 →
server-components by davepoon/buildwithclaude
npx skills add https://github.com/davepoon/buildwithclaude --skill server-componentsReact 服务端组件允许组件在服务器端渲染,从而减少客户端 JavaScript 代码量并实现直接数据访问。在 Next.js App Router 中,默认情况下所有组件都是服务端组件。
服务端组件仅在服务器端运行:
// app/users/page.tsx (服务端组件 - 默认)
async function UsersPage() {
const users = await db.user.findMany() // 直接访问数据库
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)
}
优势:
添加 'use client' 指令以实现交互功能:
// components/counter.tsx
'use client'
import { useState } from 'react'
export function Counter() {
const [count, setCount] = useState(0)
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
)
}
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
在以下场景使用客户端组件:
useState、useEffect、useReduceronClick、onChange)window、document)将组件树视为具有"客户端边界":
服务端组件 (page.tsx)
├── 服务端组件 (header.tsx)
├── 客户端组件 ('use client') ← 边界
│ ├── 客户端组件 (子组件)
│ └── 客户端组件 (子组件)
└── 服务端组件 (footer.tsx)
关键规则:
children 传递给客户端组件在服务端组件中获取数据,然后传递给客户端:
// app/products/page.tsx (服务端)
import { ProductList } from './product-list'
export default async function ProductsPage() {
const products = await getProducts()
return <ProductList products={products} />
}
// app/products/product-list.tsx (客户端)
'use client'
export function ProductList({ products }: { products: Product[] }) {
const [filter, setFilter] = useState('')
const filtered = products.filter(p =>
p.name.includes(filter)
)
return (
<>
<input onChange={e => setFilter(e.target.value)} />
{filtered.map(p => <ProductCard key={p.id} product={p} />)}
</>
)
}
通过 children 属性传递服务端组件:
// components/client-wrapper.tsx
'use client'
export function ClientWrapper({ children }: { children: React.ReactNode }) {
const [isOpen, setIsOpen] = useState(false)
return (
<div>
<button onClick={() => setIsOpen(!isOpen)}>切换</button>
{isOpen && children} {/* 服务端组件内容 */}
</div>
)
}
// app/page.tsx (服务端)
import { ClientWrapper } from '@/components/client-wrapper'
import { ServerContent } from '@/components/server-content'
export default function Page() {
return (
<ClientWrapper>
<ServerContent /> {/* 在服务器端渲染! */}
</ClientWrapper>
)
}
使用多个子组件插槽:
// components/dashboard-shell.tsx
'use client'
interface Props {
sidebar: React.ReactNode
main: React.ReactNode
}
export function DashboardShell({ sidebar, main }: Props) {
const [collapsed, setCollapsed] = useState(false)
return (
<div className="flex">
{!collapsed && <aside>{sidebar}</aside>}
<main>{main}</main>
</div>
)
}
服务端组件可以是异步的:
// app/posts/page.tsx
export default async function PostsPage() {
const posts = await fetch('https://api.example.com/posts')
.then(res => res.json())
return (
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
并行获取多个资源:
export default async function DashboardPage() {
const [user, posts, analytics] = await Promise.all([
getUser(),
getPosts(),
getAnalytics(),
])
return (
<Dashboard user={user} posts={posts} analytics={analytics} />
)
}
流式渲染慢速组件:
import { Suspense } from 'react'
export default function Page() {
return (
<div>
<Header /> {/* 立即渲染 */}
<Suspense fallback={<PostsSkeleton />}>
<SlowPosts /> {/* 准备就绪时流式渲染 */}
</Suspense>
</div>
)
}
在以下场景使用服务端组件:
在以下场景使用客户端组件:
useState、useReducer)useEffect)'use client' - 这会增加包体积有关详细模式,请参阅:
references/server-vs-client.md - 完整对比指南references/composition-patterns.md - 高级组合模式examples/data-fetching-patterns.md - 数据获取示例每周安装量
49
代码仓库
GitHub 星标数
2.6K
首次出现
2026年1月22日
安全审计
已安装于
opencode43
gemini-cli42
claude-code42
codex40
cursor40
github-copilot36
React Server Components (RSC) allow components to render on the server, reducing client-side JavaScript and enabling direct data access. In Next.js App Router, all components are Server Components by default.
Server Components run only on the server:
// app/users/page.tsx (Server Component - default)
async function UsersPage() {
const users = await db.user.findMany() // Direct DB access
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)
}
Benefits:
Add 'use client' directive for interactivity:
// components/counter.tsx
'use client'
import { useState } from 'react'
export function Counter() {
const [count, setCount] = useState(0)
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
)
}
Use Client Components for:
useState, useEffect, useReduceronClick, onChange)window, document)Think of the component tree as having a "client boundary":
Server Component (page.tsx)
├── Server Component (header.tsx)
├── Client Component ('use client') ← boundary
│ ├── Client Component (child)
│ └── Client Component (child)
└── Server Component (footer.tsx)
Key rules:
children to Client ComponentsFetch data in Server Component, pass to Client:
// app/products/page.tsx (Server)
import { ProductList } from './product-list'
export default async function ProductsPage() {
const products = await getProducts()
return <ProductList products={products} />
}
// app/products/product-list.tsx (Client)
'use client'
export function ProductList({ products }: { products: Product[] }) {
const [filter, setFilter] = useState('')
const filtered = products.filter(p =>
p.name.includes(filter)
)
return (
<>
<input onChange={e => setFilter(e.target.value)} />
{filtered.map(p => <ProductCard key={p.id} product={p} />)}
</>
)
}
Pass Server Components through children prop:
// components/client-wrapper.tsx
'use client'
export function ClientWrapper({ children }: { children: React.ReactNode }) {
const [isOpen, setIsOpen] = useState(false)
return (
<div>
<button onClick={() => setIsOpen(!isOpen)}>Toggle</button>
{isOpen && children} {/* Server Component content */}
</div>
)
}
// app/page.tsx (Server)
import { ClientWrapper } from '@/components/client-wrapper'
import { ServerContent } from '@/components/server-content'
export default function Page() {
return (
<ClientWrapper>
<ServerContent /> {/* Renders on server! */}
</ClientWrapper>
)
}
Use multiple children slots:
// components/dashboard-shell.tsx
'use client'
interface Props {
sidebar: React.ReactNode
main: React.ReactNode
}
export function DashboardShell({ sidebar, main }: Props) {
const [collapsed, setCollapsed] = useState(false)
return (
<div className="flex">
{!collapsed && <aside>{sidebar}</aside>}
<main>{main}</main>
</div>
)
}
Server Components can be async:
// app/posts/page.tsx
export default async function PostsPage() {
const posts = await fetch('https://api.example.com/posts')
.then(res => res.json())
return (
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
Fetch multiple resources in parallel:
export default async function DashboardPage() {
const [user, posts, analytics] = await Promise.all([
getUser(),
getPosts(),
getAnalytics(),
])
return (
<Dashboard user={user} posts={posts} analytics={analytics} />
)
}
Stream slow components:
import { Suspense } from 'react'
export default function Page() {
return (
<div>
<Header /> {/* Renders immediately */}
<Suspense fallback={<PostsSkeleton />}>
<SlowPosts /> {/* Streams when ready */}
</Suspense>
</div>
)
}
Use Server Component when:
Use Client Component when:
useState, useReducer)useEffect)'use client' unnecessarily - it increases bundle sizeFor detailed patterns, see:
references/server-vs-client.md - Complete comparison guidereferences/composition-patterns.md - Advanced compositionexamples/data-fetching-patterns.md - Data fetching examplesWeekly Installs
49
Repository
GitHub Stars
2.6K
First Seen
Jan 22, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode43
gemini-cli42
claude-code42
codex40
cursor40
github-copilot36
UI组件模式实战指南:构建可复用React组件库与设计系统
10,700 周安装