一键导入
nextjs-client-boundary
This skill should be used when deciding about "use client", client components, or client boundaries. Guides when to use client components.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
This skill should be used when deciding about "use client", client components, or client boundaries. Guides when to use client components.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
This skill should be used when implementing code, creating files, or writing new features. Provides complete file structure, naming patterns, and import rules.
Decompose large requirements into phased task files that fit AI context windows using Gherkin + State Machine approach.
This skill should be used when working with database, Drizzle ORM, entities, schemas, migrations, and seeds. It provides Rails-style DB conventions, local sqlite/libSQL file defaults, and Effect-friendly data patterns.
Use this skill when generating enterprise admin, back-office, or operations module UI. Activates on: new module screen, admin panel, dashboard, operations list, approval queue, entity detail, multi-step form wizard, RBAC console, audit log, analytics cockpit, support console, settings page, or any layout described as "enterprise", "compact", or "admin-heavy". Provides recipe selection, page anatomy contract, density mode, state checklist, and AI prompt template.
This skill should be used when performing browser testing, verifying in browser, or runtime checks. Guides browser verification patterns.
This skill should be used when working with Chakra UI, UI components, or the design system. Guides Chakra UI v3 and Ark UI patterns.
| name | nextjs-client-boundary |
| description | This skill should be used when deciding about "use client", client components, or client boundaries. Guides when to use client components. |
Use this skill when deciding where to place "use client" directives.
Server Components are the default. Add "use client" only when necessary.
// NEEDS "use client" - uses useState
'use client';
import { useState } from 'react';
export function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
// NEEDS "use client" - has onClick
'use client';
export function SubmitButton({ onSubmit }) {
return <button onClick={onSubmit}>Submit</button>;
}
// NEEDS "use client" - uses window/localStorage
'use client';
import { useEffect, useState } from 'react';
export function ThemeToggle() {
const [theme, setTheme] = useState('light');
useEffect(() => {
const saved = localStorage.getItem('theme');
if (saved) setTheme(saved);
}, []);
return <button onClick={() => {
const newTheme = theme === 'light' ? 'dark' : 'light';
localStorage.setItem('theme', newTheme);
setTheme(newTheme);
}}>Toggle</button>;
}
// NEEDS "use client" - uses client-only library
'use client';
import { motion } from 'framer-motion';
export function AnimatedCard({ children }) {
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
>
{children}
</motion.div>
);
}
// NO "use client" - just renders data
export function UserCard({ user }) {
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}
// NO "use client" - fetches data on server
export async function UserList() {
const users = await db.user.findMany();
return (
<ul>
{users.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
);
}
// NO "use client" - uses server action via form action
import { createUser } from './actions';
export function CreateUserForm() {
return (
<form action={createUser}>
<input name="name" />
<button type="submit">Create</button>
</form>
);
}
┌─────────────────────────────────────┐
│ Screen (Server) │
│ ├─ Header (Server) │
│ ├─ Content (Server) │
│ │ ├─ UserCard (Server) │
│ │ └─ Actions │
│ │ └─ LikeButton (Client) ←── │ "use client" here
│ └─ Footer (Server) │
└─────────────────────────────────────┘
// BAD - entire page is client
'use client';
export function UserPage() {
const [user, setUser] = useState(null);
// Everything is now client-side
}
// GOOD - screen is server, only interactive parts are client
// UserScreen.tsx (Server)
export async function UserScreen({ userId }) {
const user = await getUser(userId);
return (
<div>
<UserInfo user={user} /> {/* Server */}
<UserActions userId={userId} /> {/* Client */}
</div>
);
}
// UserActions.tsx (Client)
'use client';
export function UserActions({ userId }) {
return (
<div>
<FollowButton userId={userId} />
<MessageButton userId={userId} />
</div>
);
}
| Directory | Default | Notes |
|---|---|---|
screens/ | Server | Page-level composition, data fetching |
components/ | Server | Pure UI, can have client leaves |
containers/ | Client | Logic binding, hooks, event handlers |
hooks/ | Client | All hooks are client-side |
actions/ | Server | Server actions with "use server" |
// ParentScreen.tsx (Server)
export async function ParentScreen() {
const data = await fetchData();
return <ClientChild initialData={data} />;
}
// ClientChild.tsx (Client)
'use client';
export function ClientChild({ initialData }) {
const [data, setData] = useState(initialData);
// Interactive logic here
}
// ClientWrapper.tsx (Client)
'use client';
export function ClientWrapper({ children }) {
const [isOpen, setIsOpen] = useState(false);
return (
<div>
<button onClick={() => setIsOpen(!isOpen)}>Toggle</button>
{isOpen && children}
</div>
);
}
// Usage in Server Component
export function Page() {
return (
<ClientWrapper>
<ServerContent /> {/* This stays server! */}
</ClientWrapper>
);
}
// WRONG - Server actions work without "use client"
'use client'; // <- Remove this
import { submitForm } from './actions';
export function Form() {
return <form action={submitForm}>...</form>;
}
// WRONG - Can't pass functions from client to server
'use client';
export function Parent() {
const handleClick = () => console.log('clicked');
return <ServerComponent onClick={handleClick} />; // Error!
}
// WRONG - Screens should be server components
'use client'; // <- Remove, move logic to container
export function UserScreen() {
const [users, setUsers] = useState([]);
// ...
}
// RIGHT - Screen fetches data, container handles interactivity
export async function UserScreen() {
const users = await fetchUsers();
return <UserListContainer initialUsers={users} />;
}