<< All versions
Skill v1.0.1
currentLLM-judged scan100/100sickn33/antigravity-awesome-skills/cc-skill-frontend-patterns
2 files
──Details
PublishedMay 14, 2026 at 07:36 PM
Content Hashsha256:78bc0a9718063bdb...
Git SHA82058b6f14a7
Bump Typepatch
──Files
Files (1 file, 14.5 KB)
SKILL.md14.5 KBactive
SKILL.md · 644 lines · 14.5 KB
version: "1.0.1" name: cc-skill-frontend-patterns description: "Frontend development patterns for React, Next.js, state management, performance optimization, and UI best practices." risk: unknown source: community date_added: "2026-02-27"
Frontend Development Patterns
Modern frontend patterns for React, Next.js, and performant user interfaces.
Component Patterns
Composition Over Inheritance
typescript
// ✅ GOOD: Component compositioninterface CardProps {children: React.ReactNodevariant?: 'default' | 'outlined'}export function Card({ children, variant = 'default' }: CardProps) {return <div className={`card card-${variant}`}>{children}</div>}export function CardHeader({ children }: { children: React.ReactNode }) {return <div className="card-header">{children}</div>}export function CardBody({ children }: { children: React.ReactNode }) {return <div className="card-body">{children}</div>}// Usage<Card><CardHeader>Title</CardHeader><CardBody>Content</CardBody></Card>
Compound Components
typescript
interface TabsContextValue {activeTab: stringsetActiveTab: (tab: string) => void}const TabsContext = createContext<TabsContextValue | undefined>(undefined)export function Tabs({ children, defaultTab }: {children: React.ReactNodedefaultTab: string}) {const [activeTab, setActiveTab] = useState(defaultTab)return (<TabsContext.Provider value={{ activeTab, setActiveTab }}>{children}</TabsContext.Provider>)}export function TabList({ children }: { children: React.ReactNode }) {return <div className="tab-list">{children}</div>}export 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 (<buttonclassName={context.activeTab === id ? 'active' : ''}onClick={() => context.setActiveTab(id)}>{children}</button>)}// Usage<Tabs defaultTab="overview"><TabList><Tab id="overview">Overview</Tab><Tab id="details">Details</Tab></TabList></Tabs>
Render Props Pattern
typescript
interface DataLoaderProps<T> {url: stringchildren: (data: T | null, loading: boolean, error: Error | null) => React.ReactNode}export function DataLoader<T>({ url, children }: DataLoaderProps<T>) {const [data, setData] = useState<T | null>(null)const [loading, setLoading] = useState(true)const [error, setError] = useState<Error | null>(null)useEffect(() => {fetch(url).then(res => res.json()).then(setData).catch(setError).finally(() => setLoading(false))}, [url])return <>{children(data, loading, error)}</>}// Usage<DataLoader<Market[]> url="/api/markets">{(markets, loading, error) => {if (loading) return <Spinner />if (error) return <Error error={error} />return <MarketList markets={markets!} />}}</DataLoader>
Custom Hooks Patterns
State Management Hook
typescript
export function useToggle(initialValue = false): [boolean, () => void] {const [value, setValue] = useState(initialValue)const toggle = useCallback(() => {setValue(v => !v)}, [])return [value, toggle]}// Usageconst [isOpen, toggleOpen] = useToggle()
Async Data Fetching Hook
typescript
interface UseQueryOptions<T> {onSuccess?: (data: T) => voidonError?: (error: Error) => voidenabled?: boolean}export function useQuery<T>(key: string,fetcher: () => Promise<T>,options?: UseQueryOptions<T>) {const [data, setData] = useState<T | null>(null)const [error, setError] = useState<Error | null>(null)const [loading, setLoading] = useState(false)const refetch = useCallback(async () => {setLoading(true)setError(null)try {const result = await fetcher()setData(result)options?.onSuccess?.(result)} catch (err) {const error = err as ErrorsetError(error)options?.onError?.(error)} finally {setLoading(false)}}, [fetcher, options])useEffect(() => {if (options?.enabled !== false) {refetch()}}, [key, refetch, options?.enabled])return { data, error, loading, refetch }}// Usageconst { data: markets, loading, error, refetch } = useQuery('markets',() => fetch('/api/markets').then(r => r.json()),{onSuccess: data => console.log('Fetched', data.length, 'markets'),onError: err => console.error('Failed:', err)})
Debounce Hook
typescript
export function useDebounce<T>(value: T, delay: number): T {const [debouncedValue, setDebouncedValue] = useState<T>(value)useEffect(() => {const handler = setTimeout(() => {setDebouncedValue(value)}, delay)return () => clearTimeout(handler)}, [value, delay])return debouncedValue}// Usageconst [searchQuery, setSearchQuery] = useState('')const debouncedQuery = useDebounce(searchQuery, 500)useEffect(() => {if (debouncedQuery) {performSearch(debouncedQuery)}}, [debouncedQuery])
State Management Patterns
Context + Reducer Pattern
typescript
interface State {markets: Market[]selectedMarket: Market | nullloading: boolean}type Action =| { type: 'SET_MARKETS'; payload: Market[] }| { type: 'SELECT_MARKET'; payload: Market }| { type: 'SET_LOADING'; payload: boolean }function reducer(state: State, action: Action): State {switch (action.type) {case 'SET_MARKETS':return { ...state, markets: action.payload }case 'SELECT_MARKET':return { ...state, selectedMarket: action.payload }case 'SET_LOADING':return { ...state, loading: action.payload }default:return state}}const MarketContext = createContext<{state: Statedispatch: Dispatch<Action>} | undefined>(undefined)export function MarketProvider({ children }: { children: React.ReactNode }) {const [state, dispatch] = useReducer(reducer, {markets: [],selectedMarket: null,loading: false})return (<MarketContext.Provider value={{ state, dispatch }}>{children}</MarketContext.Provider>)}export function useMarkets() {const context = useContext(MarketContext)if (!context) throw new Error('useMarkets must be used within MarketProvider')return context}
Performance Optimization
Memoization
typescript
// ✅ useMemo for expensive computationsconst sortedMarkets = useMemo(() => {return markets.sort((a, b) => b.volume - a.volume)}, [markets])// ✅ useCallback for functions passed to childrenconst handleSearch = useCallback((query: string) => {setSearchQuery(query)}, [])// ✅ React.memo for pure componentsexport const MarketCard = React.memo<MarketCardProps>(({ market }) => {return (<div className="market-card"><h3>{market.name}</h3><p>{market.description}</p></div>)})
Code Splitting & Lazy Loading
typescript
import { lazy, Suspense } from 'react'// ✅ Lazy load heavy componentsconst HeavyChart = lazy(() => import('./HeavyChart'))const ThreeJsBackground = lazy(() => import('./ThreeJsBackground'))export function Dashboard() {return (<div><Suspense fallback={<ChartSkeleton />}><HeavyChart data={data} /></Suspense><Suspense fallback={null}><ThreeJsBackground /></Suspense></div>)}
Virtualization for Long Lists
typescript
import { useVirtualizer } from '@tanstack/react-virtual'export function VirtualMarketList({ markets }: { markets: Market[] }) {const parentRef = useRef<HTMLDivElement>(null)const virtualizer = useVirtualizer({count: markets.length,getScrollElement: () => parentRef.current,estimateSize: () => 100, // Estimated row heightoverscan: 5 // Extra items to render})return (<div ref={parentRef} style={{ height: '600px', overflow: 'auto' }}><divstyle={{height: `${virtualizer.getTotalSize()}px`,position: 'relative'}}>{virtualizer.getVirtualItems().map(virtualRow => (<divkey={virtualRow.index}style={{position: 'absolute',top: 0,left: 0,width: '100%',height: `${virtualRow.size}px`,transform: `translateY(${virtualRow.start}px)`}}><MarketCard market={markets[virtualRow.index]} /></div>))}</div></div>)}
Form Handling Patterns
Controlled Form with Validation
typescript
interface FormData {name: stringdescription: stringendDate: string}interface FormErrors {name?: stringdescription?: stringendDate?: string}export function CreateMarketForm() {const [formData, setFormData] = useState<FormData>({name: '',description: '',endDate: ''})const [errors, setErrors] = useState<FormErrors>({})const validate = (): boolean => {const newErrors: FormErrors = {}if (!formData.name.trim()) {newErrors.name = 'Name is required'} else if (formData.name.length > 200) {newErrors.name = 'Name must be under 200 characters'}if (!formData.description.trim()) {newErrors.description = 'Description is required'}if (!formData.endDate) {newErrors.endDate = 'End date is required'}setErrors(newErrors)return Object.keys(newErrors).length === 0}const handleSubmit = async (e: React.FormEvent) => {e.preventDefault()if (!validate()) returntry {await createMarket(formData)// Success handling} catch (error) {// Error handling}}return (<form onSubmit={handleSubmit}><inputvalue={formData.name}onChange={e => setFormData(prev => ({ ...prev, name: e.target.value }))}placeholder="Market name"/>{errors.name && <span className="error">{errors.name}</span>}{/* Other fields */}<button type="submit">Create Market</button></form>)}
Error Boundary Pattern
typescript
interface ErrorBoundaryState {hasError: booleanerror: Error | null}export class ErrorBoundary extends React.Component<{ children: React.ReactNode },ErrorBoundaryState> {state: ErrorBoundaryState = {hasError: false,error: null}static getDerivedStateFromError(error: Error): ErrorBoundaryState {return { hasError: true, error }}componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {console.error('Error boundary caught:', error, errorInfo)}render() {if (this.state.hasError) {return (<div className="error-fallback"><h2>Something went wrong</h2><p>{this.state.error?.message}</p><button onClick={() => this.setState({ hasError: false })}>Try again</button></div>)}return this.props.children}}// Usage<ErrorBoundary><App /></ErrorBoundary>
Animation Patterns
Framer Motion Animations
typescript
import { motion, AnimatePresence } from 'framer-motion'// ✅ List animationsexport function AnimatedMarketList({ markets }: { markets: Market[] }) {return (<AnimatePresence>{markets.map(market => (<motion.divkey={market.id}initial={{ opacity: 0, y: 20 }}animate={{ opacity: 1, y: 0 }}exit={{ opacity: 0, y: -20 }}transition={{ duration: 0.3 }}><MarketCard market={market} /></motion.div>))}</AnimatePresence>)}// ✅ Modal animationsexport function Modal({ isOpen, onClose, children }: ModalProps) {return (<AnimatePresence>{isOpen && (<><motion.divclassName="modal-overlay"initial={{ opacity: 0 }}animate={{ opacity: 1 }}exit={{ opacity: 0 }}onClick={onClose}/><motion.divclassName="modal-content"initial={{ opacity: 0, scale: 0.9, y: 20 }}animate={{ opacity: 1, scale: 1, y: 0 }}exit={{ opacity: 0, scale: 0.9, y: 20 }}>{children}</motion.div></>)}</AnimatePresence>)}
Accessibility Patterns
Keyboard Navigation
typescript
export function Dropdown({ options, onSelect }: DropdownProps) {const [isOpen, setIsOpen] = useState(false)const [activeIndex, setActiveIndex] = useState(0)const handleKeyDown = (e: React.KeyboardEvent) => {switch (e.key) {case 'ArrowDown':e.preventDefault()setActiveIndex(i => Math.min(i + 1, options.length - 1))breakcase 'ArrowUp':e.preventDefault()setActiveIndex(i => Math.max(i - 1, 0))breakcase 'Enter':e.preventDefault()onSelect(options[activeIndex])setIsOpen(false)breakcase 'Escape':setIsOpen(false)break}}return (<divrole="combobox"aria-expanded={isOpen}aria-haspopup="listbox"onKeyDown={handleKeyDown}>{/* Dropdown implementation */}</div>)}
Focus Management
typescript
export function Modal({ isOpen, onClose, children }: ModalProps) {const modalRef = useRef<HTMLDivElement>(null)const previousFocusRef = useRef<HTMLElement | null>(null)useEffect(() => {if (isOpen) {// Save currently focused elementpreviousFocusRef.current = document.activeElement as HTMLElement// Focus modalmodalRef.current?.focus()} else {// Restore focus when closingpreviousFocusRef.current?.focus()}}, [isOpen])return isOpen ? (<divref={modalRef}role="dialog"aria-modal="true"tabIndex={-1}onKeyDown={e => e.key === 'Escape' && onClose()}>{children}</div>) : null}
Remember: Modern frontend patterns enable maintainable, performant user interfaces. Choose patterns that fit your project complexity.
When to Use
This skill is applicable to execute the workflow or actions described in the overview.
Limitations
- Use this skill only when the task clearly matches the scope described above.
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.