Builds user interfaces with React using components, hooks, state management, and modern patterns. Use when creating React components, managing state, handling side effects, optimizing performance, or integrating with frameworks.
Builds React components with hooks, state management, and modern patterns. Use when creating UI components, managing state with useState/useReducer, handling side effects with useEffect, or optimizing performance with memoization.
/plugin marketplace add mgd34msu/goodvibes-plugin/plugin install goodvibes@goodvibes-marketThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Library for building user interfaces with components, declarative rendering, and hooks-based state management.
Create with Vite (recommended):
npm create vite@latest my-app -- --template react-ts
cd my-app
npm install
npm run dev
Or with a meta-framework:
npx create-next-app@latest # Next.js
npx create-remix@latest # Remix
// Basic component
function Greeting({ name }: { name: string }) {
return <h1>Hello, {name}!</h1>;
}
// Arrow function with destructured props
const Button = ({ onClick, children }: {
onClick: () => void;
children: React.ReactNode;
}) => {
return <button onClick={onClick}>{children}</button>;
};
// Usage
<Greeting name="World" />
<Button onClick={() => alert('Clicked!')}>Click me</Button>
interface CardProps {
title: string;
description?: string;
children: React.ReactNode;
variant?: 'default' | 'outlined';
}
function Card({ title, description, children, variant = 'default' }: CardProps) {
return (
<div className={`card card--${variant}`}>
<h2>{title}</h2>
{description && <p>{description}</p>}
{children}
</div>
);
}
const name = 'React';
const items = ['Apple', 'Banana', 'Cherry'];
function App() {
return (
<div>
{/* Variables */}
<h1>{name}</h1>
{/* Expressions */}
<p>{2 + 2}</p>
<p>{name.toUpperCase()}</p>
{/* Conditional rendering */}
{name && <p>Hello, {name}</p>}
{items.length > 0 ? <List items={items} /> : <p>No items</p>}
{/* Lists */}
<ul>
{items.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</div>
);
}
// Named fragment
import { Fragment } from 'react';
function List() {
return (
<Fragment>
<li>Item 1</li>
<li>Item 2</li>
</Fragment>
);
}
// Shorthand syntax
function List() {
return (
<>
<li>Item 1</li>
<li>Item 2</li>
</>
);
}
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={() => setCount((c) => c - 1)}>Decrement</button>
</div>
);
}
// With objects
const [user, setUser] = useState({ name: '', email: '' });
setUser((prev) => ({ ...prev, name: 'John' }));
// Lazy initialization
const [data, setData] = useState(() => expensiveComputation());
import { useEffect, useState } from 'react';
function DataFetcher({ url }: { url: string }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
let cancelled = false;
async function fetchData() {
setLoading(true);
const response = await fetch(url);
const json = await response.json();
if (!cancelled) {
setData(json);
setLoading(false);
}
}
fetchData();
// Cleanup function
return () => {
cancelled = true;
};
}, [url]); // Dependency array
if (loading) return <p>Loading...</p>;
return <pre>{JSON.stringify(data, null, 2)}</pre>;
}
import { useRef, useEffect } from 'react';
function TextInput() {
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
// Focus on mount
inputRef.current?.focus();
}, []);
return <input ref={inputRef} type="text" />;
}
// Mutable ref (doesn't trigger re-render)
function Timer() {
const countRef = useRef(0);
useEffect(() => {
const id = setInterval(() => {
countRef.current += 1;
}, 1000);
return () => clearInterval(id);
}, []);
}
import { useMemo, useCallback, useState } from 'react';
function ExpensiveList({ items, filter }: { items: Item[]; filter: string }) {
// Memoize expensive computation
const filteredItems = useMemo(() => {
return items.filter((item) =>
item.name.toLowerCase().includes(filter.toLowerCase())
);
}, [items, filter]);
// Memoize callback
const handleClick = useCallback((id: string) => {
console.log('Clicked:', id);
}, []);
return (
<ul>
{filteredItems.map((item) => (
<ListItem key={item.id} item={item} onClick={handleClick} />
))}
</ul>
);
}
import { useReducer } from 'react';
interface State {
count: number;
error: string | null;
}
type Action =
| { type: 'increment' }
| { type: 'decrement' }
| { type: 'reset'; payload: number }
| { type: 'error'; payload: string };
function reducer(state: State, action: Action): State {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + 1 };
case 'decrement':
return { ...state, count: state.count - 1 };
case 'reset':
return { ...state, count: action.payload };
case 'error':
return { ...state, error: action.payload };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0, error: null });
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
<button onClick={() => dispatch({ type: 'reset', payload: 0 })}>Reset</button>
</div>
);
}
import { createContext, useContext, useState } from 'react';
interface ThemeContextType {
theme: 'light' | 'dark';
toggleTheme: () => void;
}
const ThemeContext = createContext<ThemeContextType | null>(null);
function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState<'light' | 'dark'>('light');
const toggleTheme = () => {
setTheme((prev) => (prev === 'light' ? 'dark' : 'light'));
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
}
// Usage
function ThemedButton() {
const { theme, toggleTheme } = useTheme();
return (
<button onClick={toggleTheme}>
Current theme: {theme}
</button>
);
}
// useFetch
function useFetch<T>(url: string) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const controller = new AbortController();
async function fetchData() {
try {
setLoading(true);
const response = await fetch(url, { signal: controller.signal });
if (!response.ok) throw new Error('Failed to fetch');
const json = await response.json();
setData(json);
} catch (err) {
if (err instanceof Error && err.name !== 'AbortError') {
setError(err);
}
} finally {
setLoading(false);
}
}
fetchData();
return () => controller.abort();
}, [url]);
return { data, loading, error };
}
// useLocalStorage
function useLocalStorage<T>(key: string, initialValue: T) {
const [value, setValue] = useState<T>(() => {
const stored = localStorage.getItem(key);
return stored ? JSON.parse(stored) : initialValue;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue] as const;
}
// useDebounce
function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}
function Form() {
const [value, setValue] = useState('');
// Input change
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value);
};
// Form submit
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
console.log('Submitted:', value);
};
// Button click
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
console.log('Clicked at:', e.clientX, e.clientY);
};
// Keyboard
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
console.log('Enter pressed');
}
};
return (
<form onSubmit={handleSubmit}>
<input
value={value}
onChange={handleChange}
onKeyDown={handleKeyDown}
/>
<button type="submit" onClick={handleClick}>
Submit
</button>
</form>
);
}
function Container({ children }: { children: React.ReactNode }) {
return <div className="container">{children}</div>;
}
// Usage
<Container>
<h1>Title</h1>
<p>Content</p>
</Container>
interface MousePosition {
x: number;
y: number;
}
function MouseTracker({
render,
}: {
render: (position: MousePosition) => React.ReactNode;
}) {
const [position, setPosition] = useState({ x: 0, y: 0 });
const handleMouseMove = (e: React.MouseEvent) => {
setPosition({ x: e.clientX, y: e.clientY });
};
return <div onMouseMove={handleMouseMove}>{render(position)}</div>;
}
// Usage
<MouseTracker
render={({ x, y }) => (
<p>
Mouse position: {x}, {y}
</p>
)}
/>
const TabsContext = createContext<{
activeTab: string;
setActiveTab: (tab: string) => void;
} | null>(null);
function Tabs({ children, defaultTab }: {
children: React.ReactNode;
defaultTab: string;
}) {
const [activeTab, setActiveTab] = useState(defaultTab);
return (
<TabsContext.Provider value={{ activeTab, setActiveTab }}>
<div className="tabs">{children}</div>
</TabsContext.Provider>
);
}
function TabList({ children }: { children: React.ReactNode }) {
return <div className="tab-list">{children}</div>;
}
function Tab({ id, children }: { id: string; children: React.ReactNode }) {
const context = useContext(TabsContext);
if (!context) throw new Error('Tab must be used within Tabs');
return (
<button
className={context.activeTab === id ? 'active' : ''}
onClick={() => context.setActiveTab(id)}
>
{children}
</button>
);
}
function TabPanels({ children }: { children: React.ReactNode }) {
return <div className="tab-panels">{children}</div>;
}
function TabPanel({ id, children }: { id: string; children: React.ReactNode }) {
const context = useContext(TabsContext);
if (!context) throw new Error('TabPanel must be used within Tabs');
return context.activeTab === id ? <div>{children}</div> : null;
}
// Usage
<Tabs defaultTab="tab1">
<TabList>
<Tab id="tab1">Tab 1</Tab>
<Tab id="tab2">Tab 2</Tab>
</TabList>
<TabPanels>
<TabPanel id="tab1">Content 1</TabPanel>
<TabPanel id="tab2">Content 2</TabPanel>
</TabPanels>
</Tabs>
import { memo } from 'react';
interface ItemProps {
item: { id: string; name: string };
onClick: (id: string) => void;
}
const ListItem = memo(function ListItem({ item, onClick }: ItemProps) {
console.log('Rendering:', item.name);
return <li onClick={() => onClick(item.id)}>{item.name}</li>;
});
// With custom comparison
const ListItem = memo(
function ListItem({ item, onClick }: ItemProps) {
return <li onClick={() => onClick(item.id)}>{item.name}</li>;
},
(prevProps, nextProps) => {
return prevProps.item.id === nextProps.item.id;
}
);
import { lazy, Suspense } from 'react';
const HeavyComponent = lazy(() => import('./HeavyComponent'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
);
}
import { Component, ErrorInfo, ReactNode } from 'react';
interface Props {
children: ReactNode;
fallback?: ReactNode;
}
interface State {
hasError: boolean;
error: Error | null;
}
class ErrorBoundary extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error };
}
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error('Error caught:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return this.props.fallback || <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
// Usage
<ErrorBoundary fallback={<ErrorPage />}>
<MyComponent />
</ErrorBoundary>
| Mistake | Fix |
|---|---|
| Mutating state directly | Use spread operator or setState |
| Missing dependency in useEffect | Add all dependencies |
| Unnecessary useEffect | Derive state during render |
| Prop drilling | Use Context or state management |
| Not cleaning up effects | Return cleanup function |
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.