Install
$ agentstack add skill-shiven0504-claude-skills-collection-react-patterns ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
React Component Patterns
Help users write well-structured React components with clean composition, proper state management, and maintainable patterns. Assumes Next.js + Tailwind CSS environment.
Component Composition
Prefer composition over props overload
Instead of a component with many conditional props, compose smaller focused components together.
Avoid — prop-heavy monolith:
Export}
footer={View details}
/>
Prefer — composable parts:
Revenue
Monthly
Export
View details
Compound Components
Use compound components when a group of components share implicit state and always work together (tabs, accordions, selects, menus).
"use client"
import { createContext, useContext, useState, type ReactNode } from 'react'
type TabsContextType = { activeTab: string; setActiveTab: (id: string) => void }
const TabsContext = createContext(null)
function useTabs() {
const ctx = useContext(TabsContext)
if (!ctx) throw new Error('Tab components must be used within ')
return ctx
}
function Tabs({ defaultTab, children }: { defaultTab: string; children: ReactNode }) {
const [activeTab, setActiveTab] = useState(defaultTab)
return (
{children}
)
}
function TabList({ children }: { children: ReactNode }) {
return {children}
}
function Tab({ id, children }: { id: string; children: ReactNode }) {
const { activeTab, setActiveTab } = useTabs()
return (
setActiveTab(id)}
className={`px-4 py-2 text-sm font-medium border-b-2 -mb-px transition-colors ${
activeTab === id
? 'border-blue-600 text-blue-600'
: 'border-transparent text-gray-500 hover:text-gray-700'
}`}
>
{children}
)
}
function TabPanel({ id, children }: { id: string; children: ReactNode }) {
const { activeTab } = useTabs()
if (activeTab !== id) return null
return {children}
}
Tabs.List = TabList
Tabs.Tab = Tab
Tabs.Panel = TabPanel
export { Tabs }
Usage:
Overview
Analytics
Overview content
Analytics content
Props Design
Keep props interfaces focused
Each component should accept only the props it needs. Use TypeScript to be explicit:
interface UserCardProps {
name: string
email: string
avatarUrl?: string
role: 'admin' | 'member' | 'viewer'
}
export function UserCard({ name, email, avatarUrl, role }: UserCardProps) {
// ...
}
Spread HTML attributes for wrapper components
For components that wrap native elements, extend the native element's props:
import { type ButtonHTMLAttributes, forwardRef } from 'react'
interface ButtonProps extends ButtonHTMLAttributes {
variant?: 'primary' | 'secondary' | 'ghost'
size?: 'sm' | 'md' | 'lg'
}
const Button = forwardRef(
({ variant = 'primary', size = 'md', className, children, ...props }, ref) => {
const base = 'inline-flex items-center justify-center font-medium rounded-lg transition-colors'
const variants = {
primary: 'bg-blue-600 text-white hover:bg-blue-700',
secondary: 'bg-gray-100 text-gray-900 hover:bg-gray-200',
ghost: 'text-gray-600 hover:bg-gray-100',
}
const sizes = {
sm: 'px-3 py-1.5 text-sm',
md: 'px-4 py-2 text-sm',
lg: 'px-6 py-3 text-base',
}
return (
{children}
)
}
)
Button.displayName = 'Button'
export { Button }
children vs render props
- Use
childrenfor most cases — it's simpler and more readable. - Use render props (or render function children) when the child needs data from the parent:
{({ data, isLoading }) =>
isLoading ? :
}
Custom Hooks
Extract logic, not JSX
Custom hooks should encapsulate stateful logic and side effects, returning values and callbacks — not components.
function useDebounce(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 useLocalStorage(key: string, initialValue: T) {
const [storedValue, setStoredValue] = useState(() => {
if (typeof window === 'undefined') return initialValue
try {
const item = window.localStorage.getItem(key)
return item ? JSON.parse(item) : initialValue
} catch {
return initialValue
}
})
const setValue = (value: T | ((val: T) => T)) => {
const valueToStore = value instanceof Function ? value(storedValue) : value
setStoredValue(valueToStore)
window.localStorage.setItem(key, JSON.stringify(valueToStore))
}
return [storedValue, setValue] as const
}
Hook naming conventions
- Always prefix with
use - Name should describe what data/behavior it provides:
useAuth,useMediaQuery,useClickOutside - Return consistent shapes:
[value, setter]for simple state,{ data, error, isLoading }for async
State Management
Start simple, scale as needed
- Local state (useState) — for state that belongs to one component
- Lifted state — when a sibling needs access, lift to the nearest common parent
- Context — for state shared across a subtree (theme, auth, locale)
- URL state (searchParams) — for state that should be shareable/bookmarkable (filters, pagination, tabs)
- External store (Zustand, Jotai) — for complex client-side state shared across many components
Context — avoid the "god provider" anti-pattern
Split contexts by domain rather than creating one massive AppContext:
// Separate concerns into focused providers
{children}
Each provider manages its own slice of state. Components only subscribe to the contexts they need, which avoids unnecessary re-renders.
URL state for user-facing state
Filters, sort order, pagination, and active tabs should live in the URL so users can share and bookmark views:
"use client"
import { useSearchParams, useRouter, usePathname } from 'next/navigation'
function useQueryState(key: string, defaultValue: string) {
const searchParams = useSearchParams()
const router = useRouter()
const pathname = usePathname()
const value = searchParams.get(key) ?? defaultValue
const setValue = (newValue: string) => {
const params = new URLSearchParams(searchParams.toString())
if (newValue === defaultValue) {
params.delete(key)
} else {
params.set(key, newValue)
}
router.replace(`${pathname}?${params.toString()}`)
}
return [value, setValue] as const
}
Controlled vs Uncontrolled Components
Support both patterns
Build form components that work controlled (parent owns the state) or uncontrolled (component owns its own state via defaultValue):
interface InputProps extends Omit, 'size'> {
label: string
error?: string
}
export function Input({ label, error, id, ...props }: InputProps) {
const inputId = id ?? label.toLowerCase().replace(/\s+/g, '-')
return (
{label}
{error && {error}}
)
}
Works both ways:
// Controlled
setEmail(e.target.value)} />
// Uncontrolled
Error Boundaries
Wrap sections, not the entire app
Place error boundaries around independent sections so a failure in one area doesn't crash everything:
"use client"
import { Component, type ReactNode } from 'react'
interface Props {
children: ReactNode
fallback?: ReactNode
}
interface State {
hasError: boolean
error: Error | null
}
export class ErrorBoundary extends Component {
state: State = { hasError: false, error: null }
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error }
}
render() {
if (this.state.hasError) {
return this.props.fallback ?? (
Something went wrong
{this.state.error?.message}
)
}
return this.props.children
}
}
Usage:
}>
}>
Lists and Keys
Use stable, unique identifiers as keys
// Good — stable ID from data
{users.map(user => )}
// Avoid — index as key (breaks with reordering, insertion, deletion)
{users.map((user, i) => )}
Index keys are acceptable only for static lists that never change order.
Conditional Rendering
Keep it readable
// Simple boolean — use &&
{isAdmin && }
// Binary choice — use ternary
{isLoading ? : }
// Multiple conditions — use early returns or a mapping
function StatusBadge({ status }: { status: 'active' | 'pending' | 'inactive' }) {
const styles = {
active: 'bg-green-100 text-green-800',
pending: 'bg-yellow-100 text-yellow-800',
inactive: 'bg-gray-100 text-gray-800',
}
return (
{status}
)
}
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Shiven0504
- Source: Shiven0504/claude-skills-collection
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.