frontend-patterns
React component patterns, state management, accessibility, and frontend best practices. Use when building UI components or implementing client-side features.
What this skill does
# Frontend Patterns
Comprehensive patterns for React components, state management, accessibility, and modern frontend development.
## When to Use
- Building React components
- Managing client state
- Implementing user interactions
- Handling forms and validation
- Optimizing performance
- Ensuring accessibility
## Component Patterns
### Component Structure
```typescript
// ✅ CORRECT: Well-structured component
'use client'
import { useState, useEffect } from 'react'
import { Button } from '@/components/ui/Button'
import { LoadingSpinner } from '@/components/ui/LoadingSpinner'
interface MarketCardProps {
id: string
name: string
description: string
endDate: Date
onBet?: (marketId: string, outcome: string) => void
}
export function MarketCard({
id,
name,
description,
endDate,
onBet
}: MarketCardProps) {
const [isLoading, setIsLoading] = useState(false)
const handleBet = async (outcome: string) => {
setIsLoading(true)
try {
await onBet?.(id, outcome)
} finally {
setIsLoading(false)
}
}
return (
<div className="border rounded-lg p-4" data-testid="market-card">
<h3 className="text-xl font-bold">{name}</h3>
<p className="text-gray-600">{description}</p>
<div className="mt-4 flex gap-2">
<Button
onClick={() => handleBet('yes')}
disabled={isLoading}
aria-label={`Bet yes on ${name}`}
>
{isLoading ? <LoadingSpinner /> : 'Yes'}
</Button>
<Button
onClick={() => handleBet('no')}
disabled={isLoading}
aria-label={`Bet no on ${name}`}
>
{isLoading ? <LoadingSpinner /> : 'No'}
</Button>
</div>
</div>
)
}
```
### Compound Component Pattern
```typescript
// Flexible, composable components
interface TabsContextValue {
activeTab: string
setActiveTab: (tab: string) => void
}
const TabsContext = createContext<TabsContextValue | null>(null)
export 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>
)
}
export function TabList({ children }: { children: React.ReactNode }) {
return <div className="tab-list" role="tablist">{children}</div>
}
export function Tab({ value, children }: {
value: string
children: React.ReactNode
}) {
const context = useContext(TabsContext)
if (!context) throw new Error('Tab must be used within Tabs')
const { activeTab, setActiveTab } = context
const isActive = activeTab === value
return (
<button
role="tab"
aria-selected={isActive}
className={isActive ? 'tab-active' : 'tab'}
onClick={() => setActiveTab(value)}
>
{children}
</button>
)
}
export function TabPanel({ value, children }: {
value: string
children: React.ReactNode
}) {
const context = useContext(TabsContext)
if (!context) throw new Error('TabPanel must be used within Tabs')
if (context.activeTab !== value) return null
return <div role="tabpanel">{children}</div>
}
// Usage
<Tabs defaultTab="markets">
<TabList>
<Tab value="markets">Markets</Tab>
<Tab value="portfolio">Portfolio</Tab>
<Tab value="history">History</Tab>
</TabList>
<TabPanel value="markets">
<MarketsList />
</TabPanel>
<TabPanel value="portfolio">
<Portfolio />
</TabPanel>
<TabPanel value="history">
<TransactionHistory />
</TabPanel>
</Tabs>
```
### Custom Hooks Pattern
```typescript
// Reusable logic extraction
export function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState(value)
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value)
}, delay)
return () => {
clearTimeout(handler)
}
}, [value, delay])
return debouncedValue
}
// Usage
function SearchMarkets() {
const [query, setQuery] = useState('')
const debouncedQuery = useDebounce(query, 500)
useEffect(() => {
if (debouncedQuery) {
searchMarkets(debouncedQuery)
}
}, [debouncedQuery])
return (
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search markets"
/>
)
}
```
### Async State Management
```typescript
// useAsync hook for data fetching
type AsyncState<T> =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: T }
| { status: 'error'; error: Error }
export function useAsync<T>(asyncFn: () => Promise<T>, deps: any[] = []) {
const [state, setState] = useState<AsyncState<T>>({ status: 'idle' })
useEffect(() => {
let cancelled = false
setState({ status: 'loading' })
asyncFn()
.then(data => {
if (!cancelled) {
setState({ status: 'success', data })
}
})
.catch(error => {
if (!cancelled) {
setState({ status: 'error', error })
}
})
return () => {
cancelled = true
}
}, deps)
return state
}
// Usage
function MarketDetails({ id }: { id: string }) {
const state = useAsync(() => fetchMarket(id), [id])
if (state.status === 'loading') {
return <LoadingSpinner />
}
if (state.status === 'error') {
return <ErrorMessage error={state.error.message} />
}
if (state.status === 'success') {
return <MarketCard {...state.data} />
}
return null
}
```
## State Management Patterns
### React Context for Global State
```typescript
// Context setup
interface AppContextValue {
user: User | null
setUser: (user: User | null) => void
theme: 'light' | 'dark'
toggleTheme: () => void
}
const AppContext = createContext<AppContextValue | null>(null)
export function AppProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<User | null>(null)
const [theme, setTheme] = useState<'light' | 'dark'>('light')
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light')
}
useEffect(() => {
// Persist theme
localStorage.setItem('theme', theme)
document.documentElement.classList.toggle('dark', theme === 'dark')
}, [theme])
return (
<AppContext.Provider value={{ user, setUser, theme, toggleTheme }}>
{children}
</AppContext.Provider>
)
}
export function useApp() {
const context = useContext(AppContext)
if (!context) {
throw new Error('useApp must be used within AppProvider')
}
return context
}
```
### Zustand for Complex State
```typescript
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
interface CartStore {
items: CartItem[]
addItem: (item: CartItem) => void
removeItem: (id: string) => void
clearCart: () => void
total: number
}
export const useCartStore = create<CartStore>()(
persist(
(set, get) => ({
items: [],
addItem: (item) => set((state) => ({
items: [...state.items, item]
})),
removeItem: (id) => set((state) => ({
items: state.items.filter(item => item.id !== id)
})),
clearCart: () => set({ items: [] }),
get total() {
return get().items.reduce((sum, item) => sum + item.price, 0)
}
}),
{
name: 'cart-storage'
}
)
)
// Usage
function Cart() {
const { items, removeItem, total } = useCartStore()
return (
<div>
<h2>Cart Total: ${total}</h2>
{items.map(item => (
<div key={item.id}>
<span>{item.name}</span>
<button onClick={() => removeItem(item.id)}>Remove</button>
</div>
))}
</div>
)
}
```
### React Query for Server State
```typescript
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
// Fetch markets
export function useMarkets() {
return useQuery({
queryKey: ['markets'],
queRelated in Design
contribute
IncludedLocal-only OSS contribution command center. Auto-refreshes the user's in-flight PR and issue state on invoke so conversations start with full context — no need to brief Claude on what's in flight. Helps the user find issues to contribute to on GitHub, builds per-repo dossiers of what each upstream expects (CLA, DCO, branch convention, AI policy, draft-first, review bots, issue templates), runs deterministic gates before any external action so AI-assisted contributions don't reach maintainers as slop. State is markdown-only: candidate files at ~/.contribute-system/candidates/, repo dossiers at ~/.contribute-system/research/, append-only event log at ~/.contribute-system/log.jsonl. No database, no cloud calls. Use when the user asks about their PRs / issues / contributions, wants to find new work to take on, claim an issue, build/refresh a repo's dossier, or draft a Design Issue or PR. Trigger with "/contribute", "what's my PR status", "find a contribution", "claim issue X", "draft a Design Issue for Y", "refresh dossier for Z".
architectural-analysis
IncludedUser-triggered deep architectural analysis of a codebase or scoped subtree across eight modes — information architecture, data flow, integration points, UI surfaces, interaction patterns, data model, control flow, and failure modes. This skill should be used when the user asks to "diagram this codebase," "map the architecture," "show the data flow," "give me an ERD," "trace control flow," "find the integration points," "verify the layout pattern," "audit the UX architecture," or any similar request whose primary deliverable is mermaid diagrams plus cited reports under docs/architecture/. Dispatches haiku/sonnet sub-agents in parallel for per-mode exploration, then verifies every citation mechanically before any node lands in a diagram. Not for one-off prose explanations of code (use code-explanation) or for high-level system design from scratch (use system-design).
mcp
IncludedModel Context Protocol (MCP) server development and tool management. Languages: Python, TypeScript. Capabilities: build MCP servers, integrate external APIs, discover/execute MCP tools, manage multi-server configs, design agent-centric tools. Actions: create, build, integrate, discover, execute, configure MCP servers/tools. Keywords: MCP, Model Context Protocol, MCP server, MCP tool, stdio transport, SSE transport, tool discovery, resource provider, prompt template, external API integration, Gemini CLI MCP, Claude MCP, agent tools, tool execution, server config. Use when: building MCP servers, integrating external APIs as MCP tools, discovering available MCP tools, executing MCP capabilities, configuring multi-server setups, designing tools for AI agents.
react-native-skia
IncludedDesign, build, debug, and optimise high-polish animated graphics in React Native or Expo using @shopify/react-native-skia, Reanimated, and Gesture Handler. Use when the user wants canvas-driven UI, shaders, paths, rich text, image filters, sprite fields, Skottie, video frames, snapshots, web CanvasKit setup, or performance tuning for custom motion-heavy elements such as loaders, hero art, cards, charts, progress indicators, particle systems, or gesture-driven surfaces. Also use when the user asks for fluid, glow, glass, blob, parallax, 60fps/120fps, or GPU-friendly animated effects in React Native, even if they do not explicitly say "Skia". Do not use for ordinary form/layout work with standard views.
plaid
IncludedProduct Led AI Development — guides founders from idea to launched product. Six capabilities: Idea (discover a product idea), Validate (pressure-test the idea against fatal flaws, problem reality, competition, and 2-week MVP feasibility), Plan (vision intake + document generation), Design (translate image references into a design.md spec), Launch (go-to-market strategy), and Build (roadmap execution). Use when someone says "PLAID", "plaid idea", "help me find an idea", "product idea", "idea from my business", "idea from my expertise", "plaid validate", "validate my idea", "pressure-test", "is this idea good", "find fatal flaws", "validate the problem", "plan a product", "define my vision", "generate a PRD", "product strategy", "plaid design", "design from image", "translate image to design", "create design.md", "extract design tokens", "plaid launch", "go-to-market", "launch plan", "GTM strategy", "launch playbook", "plaid build", "build the app", "start building", or "execute the roadmap".
nextjs-framer-motion-animations
IncludedAdds production-safe Motion for React or Framer Motion animations to Next.js apps, including reveal, hover and tap micro-interactions, whileInView, stagger, AnimatePresence, layout and layoutId transitions, reorder, scroll-linked UI, and lightweight route-content transitions. Use when the user asks to add, refactor, or debug Motion or Framer Motion in App Router or Pages Router codebases, especially around server/client boundaries, reduced motion, LazyMotion, bundle size, hydration, or route transitions. Avoid for GSAP-style timelines, WebGL or 3D scenes, heavy scroll storytelling, or CSS-only effects unless Motion is explicitly requested.