[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M71S0HXRBNRM562RVCCYC9DA":3,"skill-same:01M71S0HXRBNRM562RVCCYC9DA":77},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":42,"linkOnly":43,"markdownContent":44,"markdownBody":45,"frontMatter":46,"refName":47,"commitSha":48,"skillDirectory":49,"forksCount":50,"repositoryHtmlUrl":51,"repositoryHomepageUrl":52,"ownerHtmlUrl":53,"ownerBlogUrl":54,"ownerDescription":55,"updatedAt":56,"install":57,"resources":70},"01M71S0HXRBNRM562RVCCYC9DA","frontend-patterns",null,"Frontend development patterns for React, Next.js, state management, performance optimization, and UI best practices.","kupzed\u002Fcatatz","kupzed","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F162340645?v=4","01M71S01X9HBM7A7AT3E3YS37F","Kupzed","01M71S01Y3YDQ95T5X2PJSATCF",34,"MIT","https:\u002F\u002Fgithub.com\u002Fkupzed\u002Fcatatz\u002Fblob\u002Fmaster\u002F.agents\u002Fskills\u002Ffrontend-patterns\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000009","前端开发","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41],"patterns","frontend patterns","frontend","Next.js","for","next","frontend development","management","performance optimization","js","Optimization","ui","state","best practices","state management","development","react","performance","2026-11-10 13:02:02",false,"---\nname: frontend-patterns\ndescription: Frontend development patterns for React, Next.js, state management, performance optimization, and UI best practices.\n---\n\n# Frontend Development Patterns\n\nModern frontend patterns for React, Next.js, and performant user interfaces.\n\n## When to Activate\n\n- Building React components (composition, props, rendering)\n- Managing state (useState, useReducer, Zustand, Context)\n- Implementing data fetching (SWR, React Query, server components)\n- Optimizing performance (memoization, virtualization, code splitting)\n- Working with forms (validation, controlled inputs, Zod schemas)\n- Handling client-side routing and navigation\n- Building accessible, responsive UI patterns\n\n## Privacy and Data Boundaries\n\nFrontend examples should use synthetic or domain-generic data. Do not collect, log, persist, or display credentials, access tokens, SSNs, health data, payment details, private emails, phone numbers, or other sensitive personal data unless the user explicitly requests a scoped implementation with appropriate validation, redaction, and access controls.\n\nAvoid adding analytics, tracking pixels, third-party scripts, or external data sinks without explicit approval. When handling user data, prefer least-privilege APIs, client-side redaction before logging, and server-side validation for every boundary.\n\n## Component Patterns\n\n### Composition Over Inheritance\n\n```typescript\n\u002F\u002F PASS: GOOD: Component composition\ninterface CardProps {\n  children: React.ReactNode\n  variant?: 'default' | 'outlined'\n}\n\nexport function Card({ children, variant = 'default' }: CardProps) {\n  return \u003Cdiv className={`card card-${variant}`}>{children}\u003C\u002Fdiv>\n}\n\nexport function CardHeader({ children }: { children: React.ReactNode }) {\n  return \u003Cdiv className=\"card-header\">{children}\u003C\u002Fdiv>\n}\n\nexport function CardBody({ children }: { children: React.ReactNode }) {\n  return \u003Cdiv className=\"card-body\">{children}\u003C\u002Fdiv>\n}\n\n\u002F\u002F Usage\n\u003CCard>\n  \u003CCardHeader>Title\u003C\u002FCardHeader>\n  \u003CCardBody>Content\u003C\u002FCardBody>\n\u003C\u002FCard>\n```\n\n### Compound Components\n\n```typescript\ninterface TabsContextValue {\n  activeTab: string\n  setActiveTab: (tab: string) => void\n}\n\nconst TabsContext = createContext\u003CTabsContextValue | undefined>(undefined)\n\nexport function Tabs({ children, defaultTab }: {\n  children: React.ReactNode\n  defaultTab: string\n}) {\n  const [activeTab, setActiveTab] = useState(defaultTab)\n\n  return (\n    \u003CTabsContext.Provider value={{ activeTab, setActiveTab }}>\n      {children}\n    \u003C\u002FTabsContext.Provider>\n  )\n}\n\nexport function TabList({ children }: { children: React.ReactNode }) {\n  return \u003Cdiv className=\"tab-list\">{children}\u003C\u002Fdiv>\n}\n\nexport function Tab({ id, children }: { id: string, children: React.ReactNode }) {\n  const context = useContext(TabsContext)\n  if (!context) throw new Error('Tab must be used within Tabs')\n\n  return (\n    \u003Cbutton\n      className={context.activeTab === id ? 'active' : ''}\n      onClick={() => context.setActiveTab(id)}\n    >\n      {children}\n    \u003C\u002Fbutton>\n  )\n}\n\n\u002F\u002F Usage\n\u003CTabs defaultTab=\"overview\">\n  \u003CTabList>\n    \u003CTab id=\"overview\">Overview\u003C\u002FTab>\n    \u003CTab id=\"details\">Details\u003C\u002FTab>\n  \u003C\u002FTabList>\n\u003C\u002FTabs>\n```\n\n### Render Props Pattern\n\n```typescript\ninterface DataLoaderProps\u003CT> {\n  url: string\n  children: (data: T | null, loading: boolean, error: Error | null) => React.ReactNode\n}\n\nexport function DataLoader\u003CT>({ url, children }: DataLoaderProps\u003CT>) {\n  const [data, setData] = useState\u003CT | null>(null)\n  const [loading, setLoading] = useState(true)\n  const [error, setError] = useState\u003CError | null>(null)\n\n  useEffect(() => {\n    fetch(url)\n      .then(res => res.json())\n      .then(setData)\n      .catch(setError)\n      .finally(() => setLoading(false))\n  }, [url])\n\n  return \u003C>{children(data, loading, error)}\u003C\u002F>\n}\n\n\u002F\u002F Usage\n\u003CDataLoader\u003CMarket[]> url=\"\u002Fapi\u002Fmarkets\">\n  {(markets, loading, error) => {\n    if (loading) return \u003CSpinner \u002F>\n    if (error) return \u003CError error={error} \u002F>\n    return \u003CMarketList markets={markets!} \u002F>\n  }}\n\u003C\u002FDataLoader>\n```\n\n## Custom Hooks Patterns\n\n### State Management Hook\n\n```typescript\nexport function useToggle(initialValue = false): [boolean, () => void] {\n  const [value, setValue] = useState(initialValue)\n\n  const toggle = useCallback(() => {\n    setValue(v => !v)\n  }, [])\n\n  return [value, toggle]\n}\n\n\u002F\u002F Usage\nconst [isOpen, toggleOpen] = useToggle()\n```\n\n### Async Data Fetching Hook\n\n```typescript\ninterface UseQueryOptions\u003CT> {\n  onSuccess?: (data: T) => void\n  onError?: (error: Error) => void\n  enabled?: boolean\n}\n\nexport function useQuery\u003CT>(\n  key: string,\n  fetcher: () => Promise\u003CT>,\n  options?: UseQueryOptions\u003CT>\n) {\n  const [data, setData] = useState\u003CT | null>(null)\n  const [error, setError] = useState\u003CError | null>(null)\n  const [loading, setLoading] = useState(false)\n\n  \u002F\u002F Keep the latest fetcher\u002Foptions in refs so refetch stays referentially\n  \u002F\u002F stable even when callers pass inline functions and object literals.\n  \u002F\u002F Without this, every render creates a new refetch, and the effect below\n  \u002F\u002F re-runs after each state update - an infinite fetch loop.\n  const fetcherRef = useRef(fetcher)\n  const optionsRef = useRef(options)\n  useEffect(() => {\n    fetcherRef.current = fetcher\n    optionsRef.current = options\n  })\n\n  const refetch = useCallback(async () => {\n    setLoading(true)\n    setError(null)\n\n    try {\n      const result = await fetcherRef.current()\n      setData(result)\n      optionsRef.current?.onSuccess?.(result)\n    } catch (err) {\n      const error = err as Error\n      setError(error)\n      optionsRef.current?.onError?.(error)\n    } finally {\n      setLoading(false)\n    }\n  }, [])\n\n  const enabled = options?.enabled !== false\n\n  useEffect(() => {\n    if (enabled) {\n      refetch()\n    }\n  }, [key, enabled, refetch])\n\n  return { data, error, loading, refetch }\n}\n\n\u002F\u002F Usage\nconst { data: markets, loading, error, refetch } = useQuery(\n  'markets',\n  () => fetch('\u002Fapi\u002Fmarkets').then(r => r.json()),\n  {\n    onSuccess: data => console.log('Fetched', data.length, 'markets'),\n    onError: err => console.error('Failed:', err)\n  }\n)\n```\n\n### Debounce Hook\n\n```typescript\nexport function useDebounce\u003CT>(value: T, delay: number): T {\n  const [debouncedValue, setDebouncedValue] = useState\u003CT>(value)\n\n  useEffect(() => {\n    const handler = setTimeout(() => {\n      setDebouncedValue(value)\n    }, delay)\n\n    return () => clearTimeout(handler)\n  }, [value, delay])\n\n  return debouncedValue\n}\n\n\u002F\u002F Usage\nconst [searchQuery, setSearchQuery] = useState('')\nconst debouncedQuery = useDebounce(searchQuery, 500)\n\nuseEffect(() => {\n  if (debouncedQuery) {\n    performSearch(debouncedQuery)\n  }\n}, [debouncedQuery])\n```\n\n## State Management Patterns\n\n### Context + Reducer Pattern\n\n```typescript\ninterface State {\n  markets: Market[]\n  selectedMarket: Market | null\n  loading: boolean\n}\n\ntype Action =\n  | { type: 'SET_MARKETS'; payload: Market[] }\n  | { type: 'SELECT_MARKET'; payload: Market }\n  | { type: 'SET_LOADING'; payload: boolean }\n\nfunction reducer(state: State, action: Action): State {\n  switch (action.type) {\n    case 'SET_MARKETS':\n      return { ...state, markets: action.payload }\n    case 'SELECT_MARKET':\n      return { ...state, selectedMarket: action.payload }\n    case 'SET_LOADING':\n      return { ...state, loading: action.payload }\n    default:\n      return state\n  }\n}\n\nconst MarketContext = createContext\u003C{\n  state: State\n  dispatch: Dispatch\u003CAction>\n} | undefined>(undefined)\n\nexport function MarketProvider({ children }: { children: React.ReactNode }) {\n  const [state, dispatch] = useReducer(reducer, {\n    markets: [],\n    selectedMarket: null,\n    loading: false\n  })\n\n  return (\n    \u003CMarketContext.Provider value={{ state, dispatch }}>\n      {children}\n    \u003C\u002FMarketContext.Provider>\n  )\n}\n\nexport function useMarkets() {\n  const context = useContext(MarketContext)\n  if (!context) throw new Error('useMarkets must be used within MarketProvider')\n  return context\n}\n```\n\n## Performance Optimization\n\n### Memoization\n\n```typescript\n\u002F\u002F PASS: useMemo for expensive computations\n\u002F\u002F Copy before sorting - Array.prototype.sort mutates in place\nconst sortedMarkets = useMemo(() => {\n  return [...markets].sort((a, b) => b.volume - a.volume)\n}, [markets])\n\n\u002F\u002F PASS: useCallback for functions passed to children\nconst handleSearch = useCallback((query: string) => {\n  setSearchQuery(query)\n}, [])\n\n\u002F\u002F PASS: React.memo for pure components\nexport const MarketCard = React.memo\u003CMarketCardProps>(({ market }) => {\n  return (\n    \u003Cdiv className=\"market-card\">\n      \u003Ch3>{market.name}\u003C\u002Fh3>\n      \u003Cp>{market.description}\u003C\u002Fp>\n    \u003C\u002Fdiv>\n  )\n})\n```\n\n### Code Splitting & Lazy Loading\n\n```typescript\nimport { lazy, Suspense } from 'react'\n\n\u002F\u002F PASS: Lazy load heavy components\nconst HeavyChart = lazy(() => import('.\u002FHeavyChart'))\nconst ThreeJsBackground = lazy(() => import('.\u002FThreeJsBackground'))\n\nexport function Dashboard() {\n  return (\n    \u003Cdiv>\n      \u003CSuspense fallback={\u003CChartSkeleton \u002F>}>\n        \u003CHeavyChart data={data} \u002F>\n      \u003C\u002FSuspense>\n\n      \u003CSuspense fallback={null}>\n        \u003CThreeJsBackground \u002F>\n      \u003C\u002FSuspense>\n    \u003C\u002Fdiv>\n  )\n}\n```\n\n### Virtualization for Long Lists\n\n```typescript\nimport { useVirtualizer } from '@tanstack\u002Freact-virtual'\n\nexport function VirtualMarketList({ markets }: { markets: Market[] }) {\n  const parentRef = useRef\u003CHTMLDivElement>(null)\n\n  const virtualizer = useVirtualizer({\n    count: markets.length,\n    getScrollElement: () => parentRef.current,\n    estimateSize: () => 100,  \u002F\u002F Estimated row height\n    overscan: 5  \u002F\u002F Extra items to render\n  })\n\n  return (\n    \u003Cdiv ref={parentRef} style={{ height: '600px', overflow: 'auto' }}>\n      \u003Cdiv\n        style={{\n          height: `${virtualizer.getTotalSize()}px`,\n          position: 'relative'\n        }}\n      >\n        {virtualizer.getVirtualItems().map(virtualRow => (\n          \u003Cdiv\n            key={virtualRow.index}\n            style={{\n              position: 'absolute',\n              top: 0,\n              left: 0,\n              width: '100%',\n              height: `${virtualRow.size}px`,\n              transform: `translateY(${virtualRow.start}px)`\n            }}\n          >\n            \u003CMarketCard market={markets[virtualRow.index]} \u002F>\n          \u003C\u002Fdiv>\n        ))}\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n  )\n}\n```\n\n## Form Handling Patterns\n\n### Controlled Form with Validation\n\n```typescript\ninterface FormData {\n  name: string\n  description: string\n  endDate: string\n}\n\ninterface FormErrors {\n  name?: string\n  description?: string\n  endDate?: string\n}\n\nexport function CreateMarketForm() {\n  const [formData, setFormData] = useState\u003CFormData>({\n    name: '',\n    description: '',\n    endDate: ''\n  })\n\n  const [errors, setErrors] = useState\u003CFormErrors>({})\n\n  const validate = (): boolean => {\n    const newErrors: FormErrors = {}\n\n    if (!formData.name.trim()) {\n      newErrors.name = 'Name is required'\n    } else if (formData.name.length > 200) {\n      newErrors.name = 'Name must be under 200 characters'\n    }\n\n    if (!formData.description.trim()) {\n      newErrors.description = 'Description is required'\n    }\n\n    if (!formData.endDate) {\n      newErrors.endDate = 'End date is required'\n    }\n\n    setErrors(newErrors)\n    return Object.keys(newErrors).length === 0\n  }\n\n  const handleSubmit = async (e: React.FormEvent) => {\n    e.preventDefault()\n\n    if (!validate()) return\n\n    try {\n      await createMarket(formData)\n      \u002F\u002F Success handling\n    } catch (error) {\n      \u002F\u002F Error handling\n    }\n  }\n\n  return (\n    \u003Cform onSubmit={handleSubmit}>\n      \u003Cinput\n        value={formData.name}\n        onChange={e => setFormData(prev => ({ ...prev, name: e.target.value }))}\n        placeholder=\"Market name\"\n      \u002F>\n      {errors.name && \u003Cspan className=\"error\">{errors.name}\u003C\u002Fspan>}\n\n      {\u002F* Other fields *\u002F}\n\n      \u003Cbutton type=\"submit\">Create Market\u003C\u002Fbutton>\n    \u003C\u002Fform>\n  )\n}\n```\n\n## Error Boundary Pattern\n\n```typescript\ninterface ErrorBoundaryState {\n  hasError: boolean\n  error: Error | null\n}\n\nexport class ErrorBoundary extends React.Component\u003C\n  { children: React.ReactNode },\n  ErrorBoundaryState\n> {\n  state: ErrorBoundaryState = {\n    hasError: false,\n    error: null\n  }\n\n  static getDerivedStateFromError(error: Error): ErrorBoundaryState {\n    return { hasError: true, error }\n  }\n\n  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {\n    console.error('Error boundary caught:', error, errorInfo)\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return (\n        \u003Cdiv className=\"error-fallback\">\n          \u003Ch2>Something went wrong\u003C\u002Fh2>\n          \u003Cp>{this.state.error?.message}\u003C\u002Fp>\n          \u003Cbutton onClick={() => this.setState({ hasError: false })}>\n            Try again\n          \u003C\u002Fbutton>\n        \u003C\u002Fdiv>\n      )\n    }\n\n    return this.props.children\n  }\n}\n\n\u002F\u002F Usage\n\u003CErrorBoundary>\n  \u003CApp \u002F>\n\u003C\u002FErrorBoundary>\n```\n\n## Animation Patterns\n\n### Framer Motion Animations\n\n```typescript\nimport { motion, AnimatePresence } from 'framer-motion'\n\n\u002F\u002F PASS: List animations\nexport function AnimatedMarketList({ markets }: { markets: Market[] }) {\n  return (\n    \u003CAnimatePresence>\n      {markets.map(market => (\n        \u003Cmotion.div\n          key={market.id}\n          initial={{ opacity: 0, y: 20 }}\n          animate={{ opacity: 1, y: 0 }}\n          exit={{ opacity: 0, y: -20 }}\n          transition={{ duration: 0.3 }}\n        >\n          \u003CMarketCard market={market} \u002F>\n        \u003C\u002Fmotion.div>\n      ))}\n    \u003C\u002FAnimatePresence>\n  )\n}\n\n\u002F\u002F PASS: Modal animations\nexport function Modal({ isOpen, onClose, children }: ModalProps) {\n  return (\n    \u003CAnimatePresence>\n      {isOpen && (\n        \u003C>\n          \u003Cmotion.div\n            className=\"modal-overlay\"\n            initial={{ opacity: 0 }}\n            animate={{ opacity: 1 }}\n            exit={{ opacity: 0 }}\n            onClick={onClose}\n          \u002F>\n          \u003Cmotion.div\n            className=\"modal-content\"\n            initial={{ opacity: 0, scale: 0.9, y: 20 }}\n            animate={{ opacity: 1, scale: 1, y: 0 }}\n            exit={{ opacity: 0, scale: 0.9, y: 20 }}\n          >\n            {children}\n          \u003C\u002Fmotion.div>\n        \u003C\u002F>\n      )}\n    \u003C\u002FAnimatePresence>\n  )\n}\n```\n\n## Accessibility Patterns\n\n### Keyboard Navigation\n\n```typescript\nexport function Dropdown({ options, onSelect }: DropdownProps) {\n  const [isOpen, setIsOpen] = useState(false)\n  const [activeIndex, setActiveIndex] = useState(0)\n\n  const handleKeyDown = (e: React.KeyboardEvent) => {\n    switch (e.key) {\n      case 'ArrowDown':\n        e.preventDefault()\n        setActiveIndex(i => Math.min(i + 1, options.length - 1))\n        break\n      case 'ArrowUp':\n        e.preventDefault()\n        setActiveIndex(i => Math.max(i - 1, 0))\n        break\n      case 'Enter':\n        e.preventDefault()\n        onSelect(options[activeIndex])\n        setIsOpen(false)\n        break\n      case 'Escape':\n        setIsOpen(false)\n        break\n    }\n  }\n\n  return (\n    \u003Cdiv\n      role=\"combobox\"\n      aria-expanded={isOpen}\n      aria-haspopup=\"listbox\"\n      onKeyDown={handleKeyDown}\n    >\n      {\u002F* Dropdown implementation *\u002F}\n    \u003C\u002Fdiv>\n  )\n}\n```\n\n### Focus Management\n\n```typescript\nexport function Modal({ isOpen, onClose, children }: ModalProps) {\n  const modalRef = useRef\u003CHTMLDivElement>(null)\n  const previousFocusRef = useRef\u003CHTMLElement | null>(null)\n\n  useEffect(() => {\n    if (isOpen) {\n      \u002F\u002F Save currently focused element\n      previousFocusRef.current = document.activeElement as HTMLElement\n\n      \u002F\u002F Focus modal\n      modalRef.current?.focus()\n    } else {\n      \u002F\u002F Restore focus when closing\n      previousFocusRef.current?.focus()\n    }\n  }, [isOpen])\n\n  return isOpen ? (\n    \u003Cdiv\n      ref={modalRef}\n      role=\"dialog\"\n      aria-modal=\"true\"\n      tabIndex={-1}\n      onKeyDown={e => e.key === 'Escape' && onClose()}\n    >\n      {children}\n    \u003C\u002Fdiv>\n  ) : null\n}\n```\n\n**Remember**: Modern frontend patterns enable maintainable, performant user interfaces. Choose patterns that fit your project complexity.\n","# Frontend Development Patterns\n\nModern frontend patterns for React, Next.js, and performant user interfaces.\n\n## When to Activate\n\n- Building React components (composition, props, rendering)\n- Managing state (useState, useReducer, Zustand, Context)\n- Implementing data fetching (SWR, React Query, server components)\n- Optimizing performance (memoization, virtualization, code splitting)\n- Working with forms (validation, controlled inputs, Zod schemas)\n- Handling client-side routing and navigation\n- Building accessible, responsive UI patterns\n\n## Privacy and Data Boundaries\n\nFrontend examples should use synthetic or domain-generic data. Do not collect, log, persist, or display credentials, access tokens, SSNs, health data, payment details, private emails, phone numbers, or other sensitive personal data unless the user explicitly requests a scoped implementation with appropriate validation, redaction, and access controls.\n\nAvoid adding analytics, tracking pixels, third-party scripts, or external data sinks without explicit approval. When handling user data, prefer least-privilege APIs, client-side redaction before logging, and server-side validation for every boundary.\n\n## Component Patterns\n\n### Composition Over Inheritance\n\n```typescript\n\u002F\u002F PASS: GOOD: Component composition\ninterface CardProps {\n  children: React.ReactNode\n  variant?: 'default' | 'outlined'\n}\n\nexport function Card({ children, variant = 'default' }: CardProps) {\n  return \u003Cdiv className={`card card-${variant}`}>{children}\u003C\u002Fdiv>\n}\n\nexport function CardHeader({ children }: { children: React.ReactNode }) {\n  return \u003Cdiv className=\"card-header\">{children}\u003C\u002Fdiv>\n}\n\nexport function CardBody({ children }: { children: React.ReactNode }) {\n  return \u003Cdiv className=\"card-body\">{children}\u003C\u002Fdiv>\n}\n\n\u002F\u002F Usage\n\u003CCard>\n  \u003CCardHeader>Title\u003C\u002FCardHeader>\n  \u003CCardBody>Content\u003C\u002FCardBody>\n\u003C\u002FCard>\n```\n\n### Compound Components\n\n```typescript\ninterface TabsContextValue {\n  activeTab: string\n  setActiveTab: (tab: string) => void\n}\n\nconst TabsContext = createContext\u003CTabsContextValue | undefined>(undefined)\n\nexport function Tabs({ children, defaultTab }: {\n  children: React.ReactNode\n  defaultTab: string\n}) {\n  const [activeTab, setActiveTab] = useState(defaultTab)\n\n  return (\n    \u003CTabsContext.Provider value={{ activeTab, setActiveTab }}>\n      {children}\n    \u003C\u002FTabsContext.Provider>\n  )\n}\n\nexport function TabList({ children }: { children: React.ReactNode }) {\n  return \u003Cdiv className=\"tab-list\">{children}\u003C\u002Fdiv>\n}\n\nexport function Tab({ id, children }: { id: string, children: React.ReactNode }) {\n  const context = useContext(TabsContext)\n  if (!context) throw new Error('Tab must be used within Tabs')\n\n  return (\n    \u003Cbutton\n      className={context.activeTab === id ? 'active' : ''}\n      onClick={() => context.setActiveTab(id)}\n    >\n      {children}\n    \u003C\u002Fbutton>\n  )\n}\n\n\u002F\u002F Usage\n\u003CTabs defaultTab=\"overview\">\n  \u003CTabList>\n    \u003CTab id=\"overview\">Overview\u003C\u002FTab>\n    \u003CTab id=\"details\">Details\u003C\u002FTab>\n  \u003C\u002FTabList>\n\u003C\u002FTabs>\n```\n\n### Render Props Pattern\n\n```typescript\ninterface DataLoaderProps\u003CT> {\n  url: string\n  children: (data: T | null, loading: boolean, error: Error | null) => React.ReactNode\n}\n\nexport function DataLoader\u003CT>({ url, children }: DataLoaderProps\u003CT>) {\n  const [data, setData] = useState\u003CT | null>(null)\n  const [loading, setLoading] = useState(true)\n  const [error, setError] = useState\u003CError | null>(null)\n\n  useEffect(() => {\n    fetch(url)\n      .then(res => res.json())\n      .then(setData)\n      .catch(setError)\n      .finally(() => setLoading(false))\n  }, [url])\n\n  return \u003C>{children(data, loading, error)}\u003C\u002F>\n}\n\n\u002F\u002F Usage\n\u003CDataLoader\u003CMarket[]> url=\"\u002Fapi\u002Fmarkets\">\n  {(markets, loading, error) => {\n    if (loading) return \u003CSpinner \u002F>\n    if (error) return \u003CError error={error} \u002F>\n    return \u003CMarketList markets={markets!} \u002F>\n  }}\n\u003C\u002FDataLoader>\n```\n\n## Custom Hooks Patterns\n\n### State Management Hook\n\n```typescript\nexport function useToggle(initialValue = false): [boolean, () => void] {\n  const [value, setValue] = useState(initialValue)\n\n  const toggle = useCallback(() => {\n    setValue(v => !v)\n  }, [])\n\n  return [value, toggle]\n}\n\n\u002F\u002F Usage\nconst [isOpen, toggleOpen] = useToggle()\n```\n\n### Async Data Fetching Hook\n\n```typescript\ninterface UseQueryOptions\u003CT> {\n  onSuccess?: (data: T) => void\n  onError?: (error: Error) => void\n  enabled?: boolean\n}\n\nexport function useQuery\u003CT>(\n  key: string,\n  fetcher: () => Promise\u003CT>,\n  options?: UseQueryOptions\u003CT>\n) {\n  const [data, setData] = useState\u003CT | null>(null)\n  const [error, setError] = useState\u003CError | null>(null)\n  const [loading, setLoading] = useState(false)\n\n  \u002F\u002F Keep the latest fetcher\u002Foptions in refs so refetch stays referentially\n  \u002F\u002F stable even when callers pass inline functions and object literals.\n  \u002F\u002F Without this, every render creates a new refetch, and the effect below\n  \u002F\u002F re-runs after each state update - an infinite fetch loop.\n  const fetcherRef = useRef(fetcher)\n  const optionsRef = useRef(options)\n  useEffect(() => {\n    fetcherRef.current = fetcher\n    optionsRef.current = options\n  })\n\n  const refetch = useCallback(async () => {\n    setLoading(true)\n    setError(null)\n\n    try {\n      const result = await fetcherRef.current()\n      setData(result)\n      optionsRef.current?.onSuccess?.(result)\n    } catch (err) {\n      const error = err as Error\n      setError(error)\n      optionsRef.current?.onError?.(error)\n    } finally {\n      setLoading(false)\n    }\n  }, [])\n\n  const enabled = options?.enabled !== false\n\n  useEffect(() => {\n    if (enabled) {\n      refetch()\n    }\n  }, [key, enabled, refetch])\n\n  return { data, error, loading, refetch }\n}\n\n\u002F\u002F Usage\nconst { data: markets, loading, error, refetch } = useQuery(\n  'markets',\n  () => fetch('\u002Fapi\u002Fmarkets').then(r => r.json()),\n  {\n    onSuccess: data => console.log('Fetched', data.length, 'markets'),\n    onError: err => console.error('Failed:', err)\n  }\n)\n```\n\n### Debounce Hook\n\n```typescript\nexport function useDebounce\u003CT>(value: T, delay: number): T {\n  const [debouncedValue, setDebouncedValue] = useState\u003CT>(value)\n\n  useEffect(() => {\n    const handler = setTimeout(() => {\n      setDebouncedValue(value)\n    }, delay)\n\n    return () => clearTimeout(handler)\n  }, [value, delay])\n\n  return debouncedValue\n}\n\n\u002F\u002F Usage\nconst [searchQuery, setSearchQuery] = useState('')\nconst debouncedQuery = useDebounce(searchQuery, 500)\n\nuseEffect(() => {\n  if (debouncedQuery) {\n    performSearch(debouncedQuery)\n  }\n}, [debouncedQuery])\n```\n\n## State Management Patterns\n\n### Context + Reducer Pattern\n\n```typescript\ninterface State {\n  markets: Market[]\n  selectedMarket: Market | null\n  loading: boolean\n}\n\ntype Action =\n  | { type: 'SET_MARKETS'; payload: Market[] }\n  | { type: 'SELECT_MARKET'; payload: Market }\n  | { type: 'SET_LOADING'; payload: boolean }\n\nfunction reducer(state: State, action: Action): State {\n  switch (action.type) {\n    case 'SET_MARKETS':\n      return { ...state, markets: action.payload }\n    case 'SELECT_MARKET':\n      return { ...state, selectedMarket: action.payload }\n    case 'SET_LOADING':\n      return { ...state, loading: action.payload }\n    default:\n      return state\n  }\n}\n\nconst MarketContext = createContext\u003C{\n  state: State\n  dispatch: Dispatch\u003CAction>\n} | undefined>(undefined)\n\nexport function MarketProvider({ children }: { children: React.ReactNode }) {\n  const [state, dispatch] = useReducer(reducer, {\n    markets: [],\n    selectedMarket: null,\n    loading: false\n  })\n\n  return (\n    \u003CMarketContext.Provider value={{ state, dispatch }}>\n      {children}\n    \u003C\u002FMarketContext.Provider>\n  )\n}\n\nexport function useMarkets() {\n  const context = useContext(MarketContext)\n  if (!context) throw new Error('useMarkets must be used within MarketProvider')\n  return context\n}\n```\n\n## Performance Optimization\n\n### Memoization\n\n```typescript\n\u002F\u002F PASS: useMemo for expensive computations\n\u002F\u002F Copy before sorting - Array.prototype.sort mutates in place\nconst sortedMarkets = useMemo(() => {\n  return [...markets].sort((a, b) => b.volume - a.volume)\n}, [markets])\n\n\u002F\u002F PASS: useCallback for functions passed to children\nconst handleSearch = useCallback((query: string) => {\n  setSearchQuery(query)\n}, [])\n\n\u002F\u002F PASS: React.memo for pure components\nexport const MarketCard = React.memo\u003CMarketCardProps>(({ market }) => {\n  return (\n    \u003Cdiv className=\"market-card\">\n      \u003Ch3>{market.name}\u003C\u002Fh3>\n      \u003Cp>{market.description}\u003C\u002Fp>\n    \u003C\u002Fdiv>\n  )\n})\n```\n\n### Code Splitting & Lazy Loading\n\n```typescript\nimport { lazy, Suspense } from 'react'\n\n\u002F\u002F PASS: Lazy load heavy components\nconst HeavyChart = lazy(() => import('.\u002FHeavyChart'))\nconst ThreeJsBackground = lazy(() => import('.\u002FThreeJsBackground'))\n\nexport function Dashboard() {\n  return (\n    \u003Cdiv>\n      \u003CSuspense fallback={\u003CChartSkeleton \u002F>}>\n        \u003CHeavyChart data={data} \u002F>\n      \u003C\u002FSuspense>\n\n      \u003CSuspense fallback={null}>\n        \u003CThreeJsBackground \u002F>\n      \u003C\u002FSuspense>\n    \u003C\u002Fdiv>\n  )\n}\n```\n\n### Virtualization for Long Lists\n\n```typescript\nimport { useVirtualizer } from '@tanstack\u002Freact-virtual'\n\nexport function VirtualMarketList({ markets }: { markets: Market[] }) {\n  const parentRef = useRef\u003CHTMLDivElement>(null)\n\n  const virtualizer = useVirtualizer({\n    count: markets.length,\n    getScrollElement: () => parentRef.current,\n    estimateSize: () => 100,  \u002F\u002F Estimated row height\n    overscan: 5  \u002F\u002F Extra items to render\n  })\n\n  return (\n    \u003Cdiv ref={parentRef} style={{ height: '600px', overflow: 'auto' }}>\n      \u003Cdiv\n        style={{\n          height: `${virtualizer.getTotalSize()}px`,\n          position: 'relative'\n        }}\n      >\n        {virtualizer.getVirtualItems().map(virtualRow => (\n          \u003Cdiv\n            key={virtualRow.index}\n            style={{\n              position: 'absolute',\n              top: 0,\n              left: 0,\n              width: '100%',\n              height: `${virtualRow.size}px`,\n              transform: `translateY(${virtualRow.start}px)`\n            }}\n          >\n            \u003CMarketCard market={markets[virtualRow.index]} \u002F>\n          \u003C\u002Fdiv>\n        ))}\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n  )\n}\n```\n\n## Form Handling Patterns\n\n### Controlled Form with Validation\n\n```typescript\ninterface FormData {\n  name: string\n  description: string\n  endDate: string\n}\n\ninterface FormErrors {\n  name?: string\n  description?: string\n  endDate?: string\n}\n\nexport function CreateMarketForm() {\n  const [formData, setFormData] = useState\u003CFormData>({\n    name: '',\n    description: '',\n    endDate: ''\n  })\n\n  const [errors, setErrors] = useState\u003CFormErrors>({})\n\n  const validate = (): boolean => {\n    const newErrors: FormErrors = {}\n\n    if (!formData.name.trim()) {\n      newErrors.name = 'Name is required'\n    } else if (formData.name.length > 200) {\n      newErrors.name = 'Name must be under 200 characters'\n    }\n\n    if (!formData.description.trim()) {\n      newErrors.description = 'Description is required'\n    }\n\n    if (!formData.endDate) {\n      newErrors.endDate = 'End date is required'\n    }\n\n    setErrors(newErrors)\n    return Object.keys(newErrors).length === 0\n  }\n\n  const handleSubmit = async (e: React.FormEvent) => {\n    e.preventDefault()\n\n    if (!validate()) return\n\n    try {\n      await createMarket(formData)\n      \u002F\u002F Success handling\n    } catch (error) {\n      \u002F\u002F Error handling\n    }\n  }\n\n  return (\n    \u003Cform onSubmit={handleSubmit}>\n      \u003Cinput\n        value={formData.name}\n        onChange={e => setFormData(prev => ({ ...prev, name: e.target.value }))}\n        placeholder=\"Market name\"\n      \u002F>\n      {errors.name && \u003Cspan className=\"error\">{errors.name}\u003C\u002Fspan>}\n\n      {\u002F* Other fields *\u002F}\n\n      \u003Cbutton type=\"submit\">Create Market\u003C\u002Fbutton>\n    \u003C\u002Fform>\n  )\n}\n```\n\n## Error Boundary Pattern\n\n```typescript\ninterface ErrorBoundaryState {\n  hasError: boolean\n  error: Error | null\n}\n\nexport class ErrorBoundary extends React.Component\u003C\n  { children: React.ReactNode },\n  ErrorBoundaryState\n> {\n  state: ErrorBoundaryState = {\n    hasError: false,\n    error: null\n  }\n\n  static getDerivedStateFromError(error: Error): ErrorBoundaryState {\n    return { hasError: true, error }\n  }\n\n  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {\n    console.error('Error boundary caught:', error, errorInfo)\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return (\n        \u003Cdiv className=\"error-fallback\">\n          \u003Ch2>Something went wrong\u003C\u002Fh2>\n          \u003Cp>{this.state.error?.message}\u003C\u002Fp>\n          \u003Cbutton onClick={() => this.setState({ hasError: false })}>\n            Try again\n          \u003C\u002Fbutton>\n        \u003C\u002Fdiv>\n      )\n    }\n\n    return this.props.children\n  }\n}\n\n\u002F\u002F Usage\n\u003CErrorBoundary>\n  \u003CApp \u002F>\n\u003C\u002FErrorBoundary>\n```\n\n## Animation Patterns\n\n### Framer Motion Animations\n\n```typescript\nimport { motion, AnimatePresence } from 'framer-motion'\n\n\u002F\u002F PASS: List animations\nexport function AnimatedMarketList({ markets }: { markets: Market[] }) {\n  return (\n    \u003CAnimatePresence>\n      {markets.map(market => (\n        \u003Cmotion.div\n          key={market.id}\n          initial={{ opacity: 0, y: 20 }}\n          animate={{ opacity: 1, y: 0 }}\n          exit={{ opacity: 0, y: -20 }}\n          transition={{ duration: 0.3 }}\n        >\n          \u003CMarketCard market={market} \u002F>\n        \u003C\u002Fmotion.div>\n      ))}\n    \u003C\u002FAnimatePresence>\n  )\n}\n\n\u002F\u002F PASS: Modal animations\nexport function Modal({ isOpen, onClose, children }: ModalProps) {\n  return (\n    \u003CAnimatePresence>\n      {isOpen && (\n        \u003C>\n          \u003Cmotion.div\n            className=\"modal-overlay\"\n            initial={{ opacity: 0 }}\n            animate={{ opacity: 1 }}\n            exit={{ opacity: 0 }}\n            onClick={onClose}\n          \u002F>\n          \u003Cmotion.div\n            className=\"modal-content\"\n            initial={{ opacity: 0, scale: 0.9, y: 20 }}\n            animate={{ opacity: 1, scale: 1, y: 0 }}\n            exit={{ opacity: 0, scale: 0.9, y: 20 }}\n          >\n            {children}\n          \u003C\u002Fmotion.div>\n        \u003C\u002F>\n      )}\n    \u003C\u002FAnimatePresence>\n  )\n}\n```\n\n## Accessibility Patterns\n\n### Keyboard Navigation\n\n```typescript\nexport function Dropdown({ options, onSelect }: DropdownProps) {\n  const [isOpen, setIsOpen] = useState(false)\n  const [activeIndex, setActiveIndex] = useState(0)\n\n  const handleKeyDown = (e: React.KeyboardEvent) => {\n    switch (e.key) {\n      case 'ArrowDown':\n        e.preventDefault()\n        setActiveIndex(i => Math.min(i + 1, options.length - 1))\n        break\n      case 'ArrowUp':\n        e.preventDefault()\n        setActiveIndex(i => Math.max(i - 1, 0))\n        break\n      case 'Enter':\n        e.preventDefault()\n        onSelect(options[activeIndex])\n        setIsOpen(false)\n        break\n      case 'Escape':\n        setIsOpen(false)\n        break\n    }\n  }\n\n  return (\n    \u003Cdiv\n      role=\"combobox\"\n      aria-expanded={isOpen}\n      aria-haspopup=\"listbox\"\n      onKeyDown={handleKeyDown}\n    >\n      {\u002F* Dropdown implementation *\u002F}\n    \u003C\u002Fdiv>\n  )\n}\n```\n\n### Focus Management\n\n```typescript\nexport function Modal({ isOpen, onClose, children }: ModalProps) {\n  const modalRef = useRef\u003CHTMLDivElement>(null)\n  const previousFocusRef = useRef\u003CHTMLElement | null>(null)\n\n  useEffect(() => {\n    if (isOpen) {\n      \u002F\u002F Save currently focused element\n      previousFocusRef.current = document.activeElement as HTMLElement\n\n      \u002F\u002F Focus modal\n      modalRef.current?.focus()\n    } else {\n      \u002F\u002F Restore focus when closing\n      previousFocusRef.current?.focus()\n    }\n  }, [isOpen])\n\n  return isOpen ? (\n    \u003Cdiv\n      ref={modalRef}\n      role=\"dialog\"\n      aria-modal=\"true\"\n      tabIndex={-1}\n      onKeyDown={e => e.key === 'Escape' && onClose()}\n    >\n      {children}\n    \u003C\u002Fdiv>\n  ) : null\n}\n```\n\n**Remember**: Modern frontend patterns enable maintainable, performant user interfaces. Choose patterns that fit your project complexity.\n",{"name":5,"description":7},"master","2c685f738254579a4ff65716e9057ad3ca3297a1",".agents\u002Fskills\u002Ffrontend-patterns",6,"https:\u002F\u002Fgithub.com\u002Fkupzed\u002Fcatatz","https:\u002F\u002Fcatatz.kupzed.tech","https:\u002F\u002Fgithub.com\u002Fkupzed","https:\u002F\u002Fkupzed.tech","🌆","2026-10-31 15:31:25",{"source":8,"skillName":5,"commands":58,"prompt":68,"zipAvailable":69},[59,62,65],{"tool":60,"command":61},"npx","npx skills add kupzed\u002Fcatatz --skill frontend-patterns",{"tool":63,"command":64},"bunx","bunx skills add kupzed\u002Fcatatz --skill frontend-patterns",{"tool":66,"command":67},"pnpm","pnpm dlx skills add kupzed\u002Fcatatz --skill frontend-patterns","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fkupzed\u002Fcatatz\u002Fblob\u002Fmaster\u002F.agents\u002Fskills\u002Ffrontend-patterns\u002FSKILL.md",true,[71],{"path":72,"resourceType":73,"resourceTypeDesc":74,"sizeBytes":75,"sourceHtmlUrl":76},"agents\u002Fopenai.yaml","REFERENCE","参考文档",262,"https:\u002F\u002Fgithub.com\u002Fkupzed\u002Fcatatz\u002Fblob\u002Fmaster\u002F.agents\u002Fskills\u002Ffrontend-patterns\u002Fagents\u002Fopenai.yaml",[78,96,113,130,146,163],{"skillNo":79,"name":80,"nameZh":6,"description":81,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":82,"categories":83,"keywords":84,"publishedAt":42},"01M71S242VD0GHH7KEK4V9WKAE","verification-loop","Run CatatZ build, type, lint, test, security, and diff verification before completion.","https:\u002F\u002Fgithub.com\u002Fkupzed\u002Fcatatz\u002Fblob\u002Fmaster\u002F.claude\u002Fskills\u002Fverification-loop\u002FSKILL.md",[],[85,86,87,88,89,90,91,92,93,94,95],"verification loop","verification","loop","lint","type","test","diff","build","run","verification before completion","security",{"skillNo":97,"name":98,"nameZh":6,"description":99,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":100,"categories":101,"keywords":102,"publishedAt":42},"01M71S21V0QQZR8W5HCKFQBNQT","tdd-workflow","Use test-first development for new features, bug fixes, and refactors.","https:\u002F\u002Fgithub.com\u002Fkupzed\u002Fcatatz\u002Fblob\u002Fmaster\u002F.claude\u002Fskills\u002Ftdd-workflow\u002FSKILL.md",[],[103,104,105,106,90,107,39,108,28,109,110,111,112],"workflow","tdd workflow","tdd","test-first","first","fixes","features","bug","new","use",{"skillNo":114,"name":115,"nameZh":6,"description":116,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":117,"categories":118,"keywords":119,"publishedAt":42},"01M71S20D9FY212BP9ZG6F83MZ","supabase","Use for CatatZ tasks involving Supabase database, auth, SSR, RLS, storage, migrations, CLI, or client libraries.","https:\u002F\u002Fgithub.com\u002Fkupzed\u002Fcatatz\u002Fblob\u002Fmaster\u002F.claude\u002Fskills\u002Fsupabase\u002FSKILL.md",[],[115,120,112,121,122,123,124,125,28,126,127,128,129],"rls","tasks","client","ssr","storage","database","auth","libraries","cli","migrations",{"skillNo":131,"name":132,"nameZh":6,"description":133,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":134,"categories":135,"keywords":136,"publishedAt":42},"01M71S1Y9QNVQSK11YC7DVH90Q","supabase-postgres-best-practices","Use for PostgreSQL performance, query, schema, RLS, and database configuration reviews.","https:\u002F\u002Fgithub.com\u002Fkupzed\u002Fcatatz\u002Fblob\u002Fmaster\u002F.claude\u002Fskills\u002Fsupabase-postgres-best-practices\u002FSKILL.md",[],[37,137,138,139,115,140,141,142,28,41,143,112,125,144,145,120],"supabase postgres best practices","postgres","supabase postgres","postgres best practices","postgresql","reviews","schema","configuration","query",{"skillNo":147,"name":148,"nameZh":6,"description":149,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":150,"categories":151,"keywords":152,"publishedAt":42},"01M71S1WY8GM1Y8Y86M2D8CJBY","strategic-compact","Use manual context compaction at logical boundaries in long multi-phase tasks.","https:\u002F\u002Fgithub.com\u002Fkupzed\u002Fcatatz\u002Fblob\u002Fmaster\u002F.claude\u002Fskills\u002Fstrategic-compact\u002FSKILL.md",[],[153,154,155,156,157,158,159,160,112,121,161,162],"compact","strategic","strategic compact","context","multi","at","manual","long","phase","compaction",{"skillNo":164,"name":165,"nameZh":6,"description":166,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":167,"categories":168,"keywords":169,"publishedAt":42},"01M71S1VMJVRE4W5QG5EVDCXWK","security-review","Use for authentication, input, secrets, endpoints, uploads, and sensitive financial-data changes.","https:\u002F\u002Fgithub.com\u002Fkupzed\u002Fcatatz\u002Fblob\u002Fmaster\u002F.claude\u002Fskills\u002Fsecurity-review\u002FSKILL.md",[],[95,170,171,172,173,28,174,175,176,177,112,178],"review","security review","changes","endpoints","authentication","secrets","data","input","uploads"]