Boneyard
0xGF/boneyard
Use boneyard-js to add, configure, debug, or rebuild skeleton screens.
Comprehensive React 19 patterns expert covering Server Components, Actions, use() hook, useOptimistic, useFormStatus, useFormState, React Compiler, concurrent features, Suspense, and modern…
$ npx skills add jh941213/my-cc-harness --skill react-patterns -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install jh941213/my-cc-harness react-patterns --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/jh941213/my-cc-harness.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/react-patterns .claude/skills/react-patterns && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "react-patterns" agent skill from https://github.com/jh941213/my-cc-harness/tree/main/skills/react-patterns into .claude/skills/react-patterns/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "react-patterns", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/jh941213/my-cc-harness/tree/main/skills/react-patternsType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add jh941213/my-cc-harness --skill react-patterns -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install jh941213/my-cc-harness react-patterns --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/jh941213/my-cc-harness.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/react-patterns .agents/skills/react-patterns && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "react-patterns" agent skill from https://github.com/jh941213/my-cc-harness/tree/main/skills/react-patterns into .agents/skills/react-patterns/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "react-patterns", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add jh941213/my-cc-harness --skill react-patterns -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install jh941213/my-cc-harness react-patterns --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/jh941213/my-cc-harness.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/react-patterns .cursor/skills/react-patterns && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "react-patterns" agent skill from https://github.com/jh941213/my-cc-harness/tree/main/skills/react-patterns into .cursor/skills/react-patterns/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "react-patterns", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/jh941213/my-cc-harness.git --path skills/react-patterns--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add jh941213/my-cc-harness --skill react-patterns -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install jh941213/my-cc-harness react-patterns --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/jh941213/my-cc-harness.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/react-patterns .gemini/skills/react-patterns && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "react-patterns" agent skill from https://github.com/jh941213/my-cc-harness/tree/main/skills/react-patterns into .gemini/skills/react-patterns/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "react-patterns", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install jh941213/my-cc-harness react-patternsInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add jh941213/my-cc-harness --skill react-patterns -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/jh941213/my-cc-harness.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/react-patterns .github/skills/react-patterns && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "react-patterns" agent skill from https://github.com/jh941213/my-cc-harness/tree/main/skills/react-patterns into .github/skills/react-patterns/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "react-patterns", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add jh941213/my-cc-harness --skill react-patterns -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install jh941213/my-cc-harness react-patterns --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/jh941213/my-cc-harness.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/react-patterns .opencode/skills/react-patterns && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "react-patterns" agent skill from https://github.com/jh941213/my-cc-harness/tree/main/skills/react-patterns into .opencode/skills/react-patterns/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "react-patterns", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
react-patternsComprehensive React 19 patterns expert covering Server Components, Actions, use() hook, useOptimistic, useFormStatus, useFormState, React Compiler, concurrent features, Suspense, and modern…
React Patterns is an agent skill from jh941213/my-cc-harness. Comprehensive React 19 patterns expert covering Server Components, Actions, use() hook, useOptimistic, useFormStatus, useFormState, React Compiler, concurrent features, Suspense, and modern TypeScript development. 트리거: "React", "컴포넌트", "hooks", "JSX/TSX", "상태 관리" 안티-트리거: "Vue", "Svelte", "Angular", "백엔드"
Its SKILL.md is about 7.8k tokens, which your agent loads only when the skill is triggered. The skill folder holds 2 other files (for example `learn.md` and `reference.md`).
It sits in Frontend & Design, covering React components. It works with React, TypeScript, Angular and Svelte. The licence is MIT.
9 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit e9210e2. It shows what the files ask for, not the result of running them.
Pre-approves these tools, so the agent can use them without asking each time:
ReadWriteEditBashGrepGlobFrom allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
npmFrom the folder's file list and the shell code blocks in SKILL.md.
Links to these hosts (documentation or services it may open):
react.devreact-typescript-cheatsheet.netlify.appFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
React Patterns loads about 7.8k tokens when it runs. Until then it costs about 80 tokens; SKILL.md has 698 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check noted patterns worth knowing about, such as sudo or a known installer.
allowed-tools: Read, Write, Edit, Bash, Grep, GlobAutomated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from jh941213/my-cc-harness at commit e9210e2, republished under its MIT licence (© jh941213). 698 words, ~7,846 tokens.
.claude/skills/react-patterns/SKILL.md (or your agent's skills folder). This skill also uses 2 other files; get the full folder from GitHub.Expert guide for building modern React 19 applications with new concurrent features, Server Components, Actions, and advanced patterns.
Basic state declaration and updates:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
);
}State with initializer function (expensive computation):
const [state, setState] = useState(() => {
const initialState = computeExpensiveValue();
return initialState;
});Multiple state variables:
function UserProfile() {
const [name, setName] = useState('');
const [age, setAge] = useState(0);
const [email, setEmail] = useState('');
return (
<form>
<input value={name} onChange={e => setName(e.target.value)} />
<input type="number" value={age} onChange={e => setAge(Number(e.target.value))} />
<input type="email" value={email} onChange={e => setEmail(e.target.value)} />
</form>
);
}Basic effect with cleanup:
import { useEffect } from 'react';
function ChatRoom({ roomId }: { roomId: string }) {
useEffect(() => {
const connection = createConnection(roomId);
connection.connect();
// Cleanup function
return () => {
connection.disconnect();
};
}, [roomId]); // Dependency array
return <div>Connected to {roomId}</div>;
}Effect with multiple dependencies:
function ChatRoom({ roomId, serverUrl }: { roomId: string; serverUrl: string }) {
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => connection.disconnect();
}, [roomId, serverUrl]); // Re-run when either changes
return <h1>Welcome to {roomId}</h1>;
}Effect for subscriptions:
function StatusBar() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
function handleOnline() {
setIsOnline(true);
}
function handleOffline() {
setIsOnline(false);
}
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []); // Empty array = run once on mount
return <h1>{isOnline ? '✅ Online' : '❌ Disconnected'}</h1>;
}Storing mutable values without re-renders:
import { useRef } from 'react';
function Timer() {
const intervalRef = useRef<NodeJS.Timeout | null>(null);
const startTimer = () => {
intervalRef.current = setInterval(() => {
console.log('Tick');
}, 1000);
};
const stopTimer = () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
};
return (
<>
<button onClick={startTimer}>Start</button>
<button onClick={stopTimer}>Stop</button>
</>
);
}DOM element references:
function TextInput() {
const inputRef = useRef<HTMLInputElement>(null);
const focusInput = () => {
inputRef.current?.focus();
};
return (
<>
<input ref={inputRef} type="text" />
<button onClick={focusInput}>Focus Input</button>
</>
);
}Extract reusable logic into custom hooks:
// useOnlineStatus.ts
import { useState, useEffect } from 'react';
export function useOnlineStatus() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
function handleOnline() {
setIsOnline(true);
}
function handleOffline() {
setIsOnline(false);
}
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
return isOnline;
}
// Usage in components
function StatusBar() {
const isOnline = useOnlineStatus();
return <h1>{isOnline ? '✅ Online' : '❌ Disconnected'}</h1>;
}
function SaveButton() {
const isOnline = useOnlineStatus();
return (
<button disabled={!isOnline}>
{isOnline ? 'Save' : 'Reconnecting...'}
</button>
);
}Custom hook with parameters:
// useChatRoom.ts
import { useEffect } from 'react';
interface ChatOptions {
serverUrl: string;
roomId: string;
}
export function useChatRoom({ serverUrl, roomId }: ChatOptions) {
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => connection.disconnect();
}, [serverUrl, roomId]);
}
// Usage
function ChatRoom({ roomId }: { roomId: string }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');
useChatRoom({ serverUrl, roomId });
return (
<>
<input value={serverUrl} onChange={e => setServerUrl(e.target.value)} />
<h1>Welcome to {roomId}</h1>
</>
);
}Basic component with props:
interface ButtonProps {
variant?: 'primary' | 'secondary';
size?: 'sm' | 'md' | 'lg';
onClick?: () => void;
children: React.ReactNode;
}
function Button({ variant = 'primary', size = 'md', onClick, children }: ButtonProps) {
return (
<button
className={`btn btn-${variant} btn-${size}`}
onClick={onClick}
>
{children}
</button>
);
}Composition with children:
interface CardProps {
children: React.ReactNode;
className?: string;
}
function Card({ children, className = '' }: CardProps) {
return (
<div className={`card ${className}`}>
{children}
</div>
);
}
// Usage
function UserProfile() {
return (
<Card>
<h2>John Doe</h2>
<p>Software Engineer</p>
</Card>
);
}Shared state between siblings:
function Parent() {
const [activeIndex, setActiveIndex] = useState(0);
return (
<>
<Panel
isActive={activeIndex === 0}
onShow={() => setActiveIndex(0)}
>
Panel 1 content
</Panel>
<Panel
isActive={activeIndex === 1}
onShow={() => setActiveIndex(1)}
>
Panel 2 content
</Panel>
</>
);
}
interface PanelProps {
isActive: boolean;
onShow: () => void;
children: React.ReactNode;
}
function Panel({ isActive, onShow, children }: PanelProps) {
return (
<div>
<button onClick={onShow}>Show</button>
{isActive && <div>{children}</div>}
</div>
);
}❌ Bad - Using effect for derived state:
function TodoList({ todos }: { todos: Todo[] }) {
const [visibleTodos, setVisibleTodos] = useState<Todo[]>([]);
useEffect(() => {
setVisibleTodos(todos.filter(t => !t.completed));
}, [todos]); // Unnecessary effect
return <ul>{/* ... */}</ul>;
}✅ Good - Compute during render:
function TodoList({ todos }: { todos: Todo[] }) {
const visibleTodos = todos.filter(t => !t.completed); // Direct computation
return <ul>{/* ... */}</ul>;
}import { useMemo } from 'react';
function DataTable({ data }: { data: Item[] }) {
const sortedData = useMemo(() => {
return [...data].sort((a, b) => a.name.localeCompare(b.name));
}, [data]); // Only recompute when data changes
return <table>{/* render sortedData */}</table>;
}import { useCallback } from 'react';
function Parent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log('Clicked', count);
}, [count]); // Recreate only when count changes
return <ExpensiveChild onClick={handleClick} />;
}interface UserProps {
id: string;
name: string;
email: string;
age?: number; // Optional
}
function User({ id, name, email, age }: UserProps) {
return (
<div>
<h2>{name}</h2>
<p>{email}</p>
{age && <p>Age: {age}</p>}
</div>
);
}interface ListProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
}
function List<T>({ items, renderItem }: ListProps<T>) {
return (
<ul>
{items.map((item, index) => (
<li key={index}>{renderItem(item)}</li>
))}
</ul>
);
}
// Usage
<List
items={users}
renderItem={(user) => <span>{user.name}</span>}
/>function Form() {
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
// Handle form submission
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
console.log(e.target.value);
};
return (
<form onSubmit={handleSubmit}>
<input onChange={handleChange} />
</form>
);
}function ControlledInput() {
const [value, setValue] = useState('');
return (
<input
value={value}
onChange={e => setValue(e.target.value)}
/>
);
}function Greeting({ isLoggedIn }: { isLoggedIn: boolean }) {
return (
<div>
{isLoggedIn ? (
<UserGreeting />
) : (
<GuestGreeting />
)}
</div>
);
}function UserList({ users }: { users: User[] }) {
return (
<ul>
{users.map(user => (
<li key={user.id}>
{user.name}
</li>
))}
</ul>
);
}❌ Missing Dependencies:
useEffect(() => {
// Uses 'count' but doesn't include it in deps
console.log(count);
}, []); // Wrong!❌ Mutating State:
const [items, setItems] = useState([]);
items.push(newItem); // Wrong! Mutates state
setItems(items); // Won't trigger re-render✅ Correct Approach:
setItems([...items, newItem]); // Create new array❌ Using use() outside of render:
// Wrong!
function handleClick() {
const data = use(promise); // Error: use() can only be called in render
}✅ Correct usage:
function Component({ promise }) {
const data = use(promise); // Correct: called during render
return <div>{data}</div>;
}❌ Forgetting 'use server' directive:
// Wrong - missing 'use server'
export async function myAction() {
// This will run on the client!
}✅ Correct Server Action:
'use server'; // Must be at the top
export async function myAction() {
// Now runs on the server
}❌ Mixing Server and Client logic incorrectly:
// Wrong - trying to use browser APIs in Server Component
export default async function ServerComponent() {
const width = window.innerWidth; // Error: window is not defined
return <div>{width}</div>;
}✅ Correct separation:
// Server Component for data
export default async function ServerComponent() {
const data = await fetchData();
return <ClientComponent data={data} />;
}
// Client Component for browser APIs
'use client';
function ClientComponent({ data }) {
const [width, setWidth] = useState(window.innerWidth);
// Handle resize logic...
return <div>{width}</div>;
}The use() hook reads the value from a resource like a Promise or Context:
import { use } from 'react';
// Reading a Promise in a component
function MessageComponent({ messagePromise }) {
const message = use(messagePromise);
return <p>{message}</p>;
}
// Reading Context conditionally
function Button() {
if (condition) {
const theme = use(ThemeContext);
return <button className={theme}>Click</button>;
}
return <button>Click</button>;
}Manage optimistic UI updates for async operations:
import { useOptimistic } from 'react';
function TodoList({ todos, addTodo }) {
const [optimisticTodos, addOptimisticTodo] = useOptimistic(
todos,
(state, newTodo) => [...state, newTodo]
);
const handleSubmit = async (formData) => {
const newTodo = { id: Date.now(), text: formData.get('text') };
// Optimistically add to UI
addOptimisticTodo(newTodo);
// Actually add to backend
await addTodo(newTodo);
};
return (
<form action={handleSubmit}>
{optimisticTodos.map(todo => (
<div key={todo.id}>{todo.text}</div>
))}
<input type="text" name="text" />
<button type="submit">Add Todo</button>
</form>
);
}Access form submission status from child components:
import { useFormStatus } from 'react';
function SubmitButton() {
const { pending, data } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? 'Submitting...' : 'Submit'}
</button>
);
}
function ContactForm() {
return (
<form action={submitForm}>
<input name="email" type="email" />
<SubmitButton />
</form>
);
}Manage form state with error handling:
import { useFormState } from 'react';
async function submitAction(prevState: string | null, formData: FormData) {
const email = formData.get('email') as string;
if (!email.includes('@')) {
return 'Invalid email address';
}
await submitToDatabase(email);
return null;
}
function EmailForm() {
const [state, formAction] = useFormState(submitAction, null);
return (
<form action={formAction}>
<input name="email" type="email" />
<button type="submit">Subscribe</button>
{state && <p className="error">{state}</p>}
</form>
);
}Define server-side functions for form handling:
// app/actions.ts
'use server';
import { redirect } from 'next/navigation';
import { revalidatePath } from 'next/cache';
export async function createPost(formData: FormData) {
const title = formData.get('title') as string;
const content = formData.get('content') as string;
// Validate input
if (!title || !content) {
return { error: 'Title and content are required' };
}
// Save to database
const post = await db.post.create({
data: { title, content }
});
// Update cache and redirect
revalidatePath('/posts');
redirect(`/posts/${post.id}`);
}Components that run exclusively on the server:
// app/posts/page.tsx - Server Component
async function PostsPage() {
// Server-side data fetching
const posts = await db.post.findMany({
orderBy: { createdAt: 'desc' },
take: 10
});
return (
<div>
<h1>Latest Posts</h1>
<PostsList posts={posts} />
</div>
);
}
// Client Component for interactivity
'use client';
function PostsList({ posts }: { posts: Post[] }) {
const [selectedId, setSelectedId] = useState<number | null>(null);
return (
<ul>
{posts.map(post => (
<li
key={post.id}
onClick={() => setSelectedId(post.id)}
className={selectedId === post.id ? 'selected' : ''}
>
{post.title}
</li>
))}
</ul>
);
}React Compiler automatically optimizes your components:
// Before React Compiler - manual memoization needed
const ExpensiveComponent = memo(function ExpensiveComponent({
data,
onUpdate
}) {
const processedData = useMemo(() => {
return data.map(item => ({
...item,
computed: expensiveCalculation(item)
}));
}, [data]);
const handleClick = useCallback((id) => {
onUpdate(id);
}, [onUpdate]);
return (
<div>
{processedData.map(item => (
<Item
key={item.id}
item={item}
onClick={handleClick}
/>
))}
</div>
);
});
// After React Compiler - no manual optimization needed
function ExpensiveComponent({ data, onUpdate }) {
const processedData = data.map(item => ({
...item,
computed: expensiveCalculation(item)
}));
const handleClick = (id) => {
onUpdate(id);
};
return (
<div>
{processedData.map(item => (
<Item
key={item.id}
item={item}
onClick={handleClick}
/>
))}
</div>
);
}# Install React Compiler
npm install -D babel-plugin-react-compiler@latest
# Install ESLint plugin for validation
npm install -D eslint-plugin-react-hooks@latest// babel.config.js
module.exports = {
plugins: [
'babel-plugin-react-compiler', // Must run first!
// ... other plugins
],
};// vite.config.js for Vite users
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [
react({
babel: {
plugins: ['babel-plugin-react-compiler'],
},
}),
],
});// babel.config.js with compiler options
module.exports = {
plugins: [
[
'babel-plugin-react-compiler',
{
// Enable compilation for specific files
target: '18', // or '19'
// Debug mode for development
debug: process.env.NODE_ENV === 'development'
}
],
],
};
// Incremental adoption with overrides
module.exports = {
plugins: [],
overrides: [
{
test: './src/components/**/*.{js,jsx,ts,tsx}',
plugins: ['babel-plugin-react-compiler']
}
]
};// Server Component for data fetching
async function ProductPage({ id }: { id: string }) {
const product = await fetchProduct(id);
const related = await fetchRelatedProducts(id);
return (
<div>
<ProductDetails product={product} />
<ProductGallery images={product.images} />
<RelatedProducts products={related} />
</div>
);
}
// Client Component for interactivity
'use client';
function ProductDetails({ product }: { product: Product }) {
const [quantity, setQuantity] = useState(1);
const [isAdded, setIsAdded] = useState(false);
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
<p>${product.price}</p>
<QuantitySelector
value={quantity}
onChange={setQuantity}
/>
<AddToCartButton
productId={product.id}
quantity={quantity}
onAdded={() => setIsAdded(true)}
/>
{isAdded && <p>Added to cart!</p>}
</div>
);
}'use server';
import { z } from 'zod';
const checkoutSchema = z.object({
items: z.array(z.object({
productId: z.string(),
quantity: z.number().min(1)
})),
shippingAddress: z.object({
street: z.string().min(1),
city: z.string().min(1),
zipCode: z.string().regex(/^\d{5}$/)
}),
paymentMethod: z.enum(['credit', 'paypal', 'apple'])
});
export async function processCheckout(
prevState: any,
formData: FormData
) {
// Extract and validate data
const rawData = {
items: JSON.parse(formData.get('items') as string),
shippingAddress: {
street: formData.get('street'),
city: formData.get('city'),
zipCode: formData.get('zipCode')
},
paymentMethod: formData.get('paymentMethod')
};
const result = checkoutSchema.safeParse(rawData);
if (!result.success) {
return {
error: 'Validation failed',
fieldErrors: result.error.flatten().fieldErrors
};
}
try {
// Process payment
const order = await createOrder(result.data);
// Update inventory
await updateInventory(result.data.items);
// Send confirmation
await sendConfirmationEmail(order);
// Revalidate cache
revalidatePath('/orders');
return { success: true, orderId: order.id };
} catch (error) {
return { error: 'Payment failed' };
}
}import { useTransition, useState } from 'react';
function SearchableList({ items }: { items: Item[] }) {
const [query, setQuery] = useState('');
const [isPending, startTransition] = useTransition();
const [filteredItems, setFilteredItems] = useState(items);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
// Update input immediately
setQuery(e.target.value);
// Transition the filter operation
startTransition(() => {
setFilteredItems(
items.filter(item =>
item.name.toLowerCase().includes(e.target.value.toLowerCase())
)
);
});
};
return (
<div>
<input
type="text"
value={query}
onChange={handleChange}
placeholder="Search items..."
/>
{isPending && <div className="loading">Filtering...</div>}
<ul>
{filteredItems.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</div>
);
}import { useDeferredValue, useMemo } from 'react';
function DataGrid({ data }: { data: DataRow[] }) {
const [searchTerm, setSearchTerm] = useState('');
const deferredSearchTerm = useDeferredValue(searchTerm);
const filteredData = useMemo(() => {
return data.filter(row =>
Object.values(row).some(value =>
String(value).toLowerCase().includes(deferredSearchTerm.toLowerCase())
)
);
}, [data, deferredSearchTerm]);
return (
<div>
<input
value={searchTerm}
onChange={e => setSearchTerm(e.target.value)}
placeholder="Search..."
className={searchTerm !== deferredSearchTerm ? 'stale' : ''}
/>
<DataGridRows
data={filteredData}
isStale={searchTerm !== deferredSearchTerm}
/>
</div>
);
}import { render, screen, fireEvent } from '@testing-library/react';
import { jest } from '@jest/globals';
import ContactForm from './ContactForm';
// Mock server action
const mockSubmitForm = jest.fn();
describe('ContactForm', () => {
it('submits form with server action', async () => {
render(<ContactForm />);
fireEvent.change(screen.getByLabelText('Email'), {
target: { value: 'test@example.com' }
});
fireEvent.click(screen.getByText('Submit'));
expect(mockSubmitForm).toHaveBeenCalledWith(
expect.any(FormData)
);
});
it('shows loading state during submission', async () => {
mockSubmitForm.mockImplementation(() => new Promise(resolve => setTimeout(resolve, 100)));
render(<ContactForm />);
fireEvent.click(screen.getByText('Submit'));
expect(screen.getByText('Submitting...')).toBeInTheDocument();
await waitFor(() => {
expect(screen.getByText('Submit')).toBeInTheDocument();
});
});
});import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { jest } from '@jest/globals';
import TodoList from './TodoList';
describe('useOptimistic', () => {
it('shows optimistic update immediately', async () => {
const mockAddTodo = jest.fn(() => new Promise(resolve => setTimeout(resolve, 100)));
render(
<TodoList
todos={[]}
addTodo={mockAddTodo}
/>
);
fireEvent.change(screen.getByPlaceholderText('Add a todo'), {
target: { value: 'New todo' }
});
fireEvent.click(screen.getByText('Add'));
// Optimistic update appears immediately
expect(screen.getByText('New todo')).toBeInTheDocument();
// Wait for actual submission
await waitFor(() => {
expect(mockAddTodo).toHaveBeenCalledWith({
id: expect.any(Number),
text: 'New todo'
});
});
});
});// Good: Clean, idiomatic React
function ProductCard({ product, onAddToCart }) {
const [quantity, setQuantity] = useState(1);
const handleAdd = () => {
onAddToCart(product.id, quantity);
};
return (
<div>
<h3>{product.name}</h3>
<p>${product.price}</p>
<input
type="number"
value={quantity}
onChange={e => setQuantity(Number(e.target.value))}
min="1"
/>
<button onClick={handleAdd}>Add to Cart</button>
</div>
);
}
// Avoid: Manual optimization
function ProductCard({ product, onAddToCart }) {
const [quantity, setQuantity] = useState(1);
const handleAdd = useCallback(() => {
onAddToCart(product.id, quantity);
}, [product.id, quantity, onAddToCart]);
return (
<div>
<h3>{product.name}</h3>
<p>${product.price}</p>
<QuantityInput
value={quantity}
onChange={setQuantity}
/>
<button onClick={handleAdd}>Add to Cart</button>
</div>
);
}// Good: Server Component for static content
async function ProductPage({ id }: { id: string }) {
const product = await fetchProduct(id);
return (
<article>
<header>
<h1>{product.name}</h1>
<p>{product.description}</p>
</header>
<img
src={product.imageUrl}
alt={product.name}
width={600}
height={400}
/>
<PriceDisplay price={product.price} />
<AddToCartForm productId={product.id} />
</article>
);
}
// Client Component only for interactivity
'use client';
function AddToCartForm({ productId }: { productId: string }) {
const [isAdding, setIsAdding] = useState(false);
async function handleSubmit() {
setIsAdding(true);
await addToCart(productId);
setIsAdding(false);
}
return (
<form action={handleSubmit}>
<button type="submit" disabled={isAdding}>
{isAdding ? 'Adding...' : 'Add to Cart'}
</button>
</form>
);
}npm install react@19 react-dom@19Adopt Server Components:
Replace Manual Optimistic Updates:
// Before
function TodoList({ todos, addTodo }) {
const [optimisticTodos, setOptimisticTodos] = useState(todos);
const handleAdd = async (text) => {
const newTodo = { id: Date.now(), text };
setOptimisticTodos([...optimisticTodos, newTodo]);
await addTodo(newTodo);
};
}
// After
function TodoList({ todos, addTodo }) {
const [optimisticTodos, addOptimisticTodo] = useOptimistic(
todos,
(state, newTodo) => [...state, newTodo]
);
const handleAdd = async (formData) => {
const newTodo = { id: Date.now(), text: formData.get('text') };
addOptimisticTodo(newTodo);
await addTodo(newTodo);
};
}© jh941213, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 2 other files in skills/react-patterns of jh941213/my-cc-harness.
Open the folder on GitHubat commit e9210e2
React Patterns next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| React Patterns this skilljh941213/my-cc-harness | 126 | — | ~7.8k | Automated safety check: Notes | MIT | |
| Boneyard0xGF/boneyard | 7.5k | — | ~2.2k | Automated safety check: Notes | MIT | |
| Markstream InstallSimon-He95/markstream-vue | 3k | — | ~2.2k | Automated safety check: Pass | MIT | |
| Solar Iconssaoudi-h/solar-icons | 182 | — | ~2.1k | Automated safety check: Pass | MIT | |
| Tailwind To Stylexshadcn-labs/shadcn-cssinjs | 122 | — | ~2.7k | Automated safety check: Pass | MIT | |
| Component ForgeEliasOulkadi/shokunin | 114 | — | ~3.6k | Automated safety check: Notes | MIT |
0xGF/boneyard
Use boneyard-js to add, configure, debug, or rebuild skeleton screens.
Simon-He95/markstream-vue
Install and wire markstream-vue, markstream-react, markstream-vue2, markstream-angular, or markstream-svelte into an existing repository.
saoudi-h/solar-icons
Add Solar Icons via @solar-icons/cli to any React, Vue, Svelte, Solid, Angular, React Native, Nuxt, Static, vanilla JS, or Laravel Blade project.
shadcn-labs/shadcn-cssinjs
Migrate TailwindCSS to StyleX. An agent skill from shadcn-labs/shadcn-cssinjs.
EliasOulkadi/shokunin
Build production-grade components for React, Vue 3, and Svelte 5 with all states (loading, empty, error, success, idle), TypeScript strict, WCAG 2.2 accessibility, server components (RSC), and…
Community-Access/accessibility-agents
Reference data, not a reviewer. An agent skill from Community-Access/accessibility-agents.
jh941213/my-cc-harness
REST 및 GraphQL API 설계 원칙 가이드. An agent skill from jh941213/my-cc-harness.
jh941213/my-cc-harness
Build scalable design systems with Tailwind CSS, design tokens, component libraries, and responsive patterns.
jh941213/my-cc-harness
Generate/update architecture docs — ARCHITECTURE.md (codemap), architecture diagrams (C4 mermaid), ADRs (MADR), data model ERD.
jh941213/my-cc-harness
A skill your agent uses when starting substantial work in a repo (implementation, fixes, deploys, debugging), when first exploring a new repo, or when finishing work that produced reusable knowledge.
jh941213/my-cc-harness
Ralph Loop based autonomous development loop. An agent skill from jh941213/my-cc-harness.
jh941213/my-cc-harness
Python asyncio 및 async/await 패턴 가이드. An agent skill from jh941213/my-cc-harness.
Works with
Categories
Comprehensive React 19 patterns expert covering Server Components, Actions, use() hook, useOptimistic, useFormStatus, useFormState, React Compiler, concurrent features, Suspense, and modern…. React Patterns is an agent skill from jh941213/my-cc-harness. Comprehensive React 19 patterns expert covering Server Components, Actions, use() hook, useOptimistic, useFormStatus, useFormState, React Compiler, concurrent features, Suspense, and modern TypeScript development.
React Patterns fits situations like: tasks that involve React components.
Run `npx skills add jh941213/my-cc-harness --skill react-patterns -a claude-code`. Or copy the skill folder (skills/react-patterns in jh941213/my-cc-harness) into .claude/skills/react-patterns in your project. Claude Code loads it when a task matches its description.
Run `npx skills add jh941213/my-cc-harness --skill react-patterns -a codex`. Or copy the skill folder (skills/react-patterns in jh941213/my-cc-harness) into .agents/skills/react-patterns in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add jh941213/my-cc-harness --skill react-patterns -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/react-patterns, .gemini/skills/react-patterns, .github/skills/react-patterns and .opencode/skills/react-patterns in your project.
Going by SKILL.md and its folder, React Patterns needs the command-line tools its instructions call (npm). Our summary lists: Node.js. Its frontmatter pre-approves these tools: Read, Write, Edit, Bash, Grep, Glob.
SKILL.md names 2 domains. As links in the text: react.dev and react-typescript-cheatsheet.netlify.app. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found notes only (pre-approves every shell command (allowed-tools: bash)), nothing it rates as a warning. It is not a guarantee. Review the folder before installing.
React Patterns is published under the MIT licence (declared in SKILL.md). It allows redistribution, so the full SKILL.md is shown on this page.
About 7.8k tokens (SKILL.md is roughly 31k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with React Patterns: Boneyard (0xGF/boneyard, 7.5k stars), Markstream Install (Simon-He95/markstream-vue, 3k stars), Solar Icons (saoudi-h/solar-icons, 182 stars) and Tailwind To Stylex (shadcn-labs/shadcn-cssinjs, 122 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
jh941213 (a GitHub user) maintains it in jh941213/my-cc-harness, which has 126 GitHub stars. The repository holds 66 skills in this directory. The repository was last updated on August 3, 2026.
Source: jh941213/my-cc-harness on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.