{"id":"85167bf6-15a1-42c7-b628-6d7d81ec92f7","shortId":"tatC8m","kind":"skill","title":"react-typescript","tagline":"Build React 19 applications with TypeScript. Covers Actions, Activity, use() hook, React Compiler, ref-as-prop, useEffectEvent, and strict TypeScript patterns. Use when creating components, managing state, typing props, handling events, using hooks, or working with React 19 featu","description":"# React TypeScript\n\nPatterns for building type-safe React 19.2 applications with TypeScript 5.9. React Compiler handles memoization automatically - write plain components, let the tooling optimize.\n\n## Critical Rules\n\n### No forwardRef - ref Is a Prop Now\n\n```tsx\n// WRONG - deprecated pattern\nconst Input = forwardRef<HTMLInputElement, InputProps>((props, ref) => (\n  <input ref={ref} {...props} />\n))\n\n// CORRECT - React 19: ref is a regular prop\nfunction Input({ ref, ...props }: React.ComponentProps<\"input\">) {\n  return <input ref={ref} {...props} />\n}\n```\n\n### No Manual Memoization with React Compiler\n\n```tsx\n// WRONG - unnecessary with React Compiler\nconst MemoizedList = memo(function List({ items }: { items: Item[] }) {\n  const sorted = useMemo(() => items.toSorted(compare), [items])\n  const handleClick = useCallback((id: string) => onSelect(id), [onSelect])\n  return sorted.map(item => <Row key={item.id} onClick={() => handleClick(item.id)} />)\n})\n\n// CORRECT - React Compiler auto-memoizes all of this\nfunction List({ items, onSelect }: { items: Item[]; onSelect: (id: string) => void }) {\n  const sorted = items.toSorted(compare)\n  return sorted.map(item => <Row key={item.id} onClick={() => onSelect(item.id)} />)\n}\n```\n\n### Use `React.ComponentProps<>` for Element Props\n\n```tsx\n// WRONG - manual HTML attribute typing\ninterface ButtonProps {\n  onClick?: (e: MouseEvent<HTMLButtonElement>) => void\n  disabled?: boolean\n  children: React.ReactNode\n  className?: string\n}\n\n// CORRECT - extend native element props\ntype ButtonProps = React.ComponentProps<\"button\"> & {\n  variant?: \"primary\" | \"ghost\"\n}\n```\n\n### Type State Discriminated Unions, Not Booleans\n\n```tsx\n// WRONG - impossible states possible\ninterface RequestState { isLoading: boolean; error: string | null; data: User | null }\n\n// CORRECT - discriminated union prevents impossible states\ntype RequestState =\n  | { status: \"idle\" }\n  | { status: \"loading\" }\n  | { status: \"error\"; error: string }\n  | { status: \"success\"; data: User }\n```\n\n### Use `satisfies` for Type-Safe Literals\n\n```tsx\n// WRONG - widens to Record<string, Route>\nconst routes: Record<string, Route> = { home: { path: \"/\" }, about: { path: \"/about\" } }\n\n// CORRECT - preserves literal keys while checking shape\nconst routes = {\n  home: { path: \"/\" },\n  about: { path: \"/about\" },\n} satisfies Record<string, Route>\n\nroutes.home // typed, autocomplete works\n```\n\n### Context Must Have Strict Defaults or Throw\n\n```tsx\n// WRONG - null default with no guard\nconst AuthContext = createContext<AuthState | null>(null)\n// consumers must null-check every time\n\n// CORRECT - factory hook that throws on missing provider\nconst AuthContext = createContext<AuthState | null>(null)\n\nfunction useAuth(): AuthState {\n  const ctx = use(AuthContext)\n  if (ctx === null) throw new Error(\"useAuth must be used within AuthProvider\")\n  return ctx\n}\n```\n\n### Prefer `use()` over `useContext()`\n\n```tsx\n// OLD pattern\nfunction Header() {\n  const theme = useContext(ThemeContext) // cannot use after early return\n  if (!isVisible) return null\n  return <h1 style={{ color: theme.color }}>Title</h1>\n}\n\n// CORRECT - React 19: use() works after early returns\nfunction Header({ isVisible }: { isVisible: boolean }) {\n  if (!isVisible) return null\n  const theme = use(ThemeContext) // works here - use() is not bound by hook rules\n  return <h1 style={{ color: theme.color }}>Title</h1>\n}\n```\n\n## React 19 Patterns\n\n### Component Authoring\n\nPlain functions with `data-slot` for styling hooks. No `forwardRef`, no `FC`:\n\n```tsx\ntype CardProps = React.ComponentProps<\"div\"> & {\n  variant?: \"elevated\" | \"outlined\"\n}\n\nfunction Card({ variant = \"outlined\", className, ...props }: CardProps) {\n  return (\n    <div\n      data-slot=\"card\"\n      data-variant={variant}\n      className={cn(\"rounded-xl border bg-card\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction CardTitle({ className, ...props }: React.ComponentProps<\"h3\">) {\n  return <h3 data-slot=\"card-title\" className={cn(\"font-semibold\", className)} {...props} />\n}\n```\n\n### Actions and useTransition\n\nAsync functions in transitions handle pending state, errors, and form resets automatically:\n\n```tsx\nfunction UpdateProfile({ userId }: { userId: string }) {\n  const [error, submitAction, isPending] = useActionState(\n    async (_prev: string | null, formData: FormData) => {\n      const result = await updateProfile(userId, formData)\n      if (result.error) return result.error\n      redirect(\"/profile\")\n      return null\n    },\n    null\n  )\n\n  return (\n    <form action={submitAction}>\n      <input type=\"text\" name=\"displayName\" required />\n      <button type=\"submit\" disabled={isPending}>\n        {isPending ? \"Saving...\" : \"Save\"}\n      </button>\n      {error && <p className=\"text-destructive\">{error}</p>}\n    </form>\n  )\n}\n```\n\n**useTransition for non-form Actions:**\n\n```tsx\nfunction DeleteButton({ onDelete }: { onDelete: () => Promise<void> }) {\n  const [isPending, startTransition] = useTransition()\n\n  return (\n    <button\n      disabled={isPending}\n      onClick={() => startTransition(async () => { await onDelete() })}\n    >\n      {isPending ? \"Deleting...\" : \"Delete\"}\n    </button>\n  )\n}\n```\n\n**useOptimistic for instant feedback:**\n\n```tsx\nfunction LikeButton({ likes, onLike }: { likes: number; onLike: () => Promise<void> }) {\n  const [optimisticLikes, addOptimisticLike] = useOptimistic(likes, (prev) => prev + 1)\n\n  const handleLike = async () => {\n    addOptimisticLike(null)\n    await onLike()\n  }\n\n  return (\n    <form action={handleLike}>\n      <button type=\"submit\">{optimisticLikes} Likes</button>\n    </form>\n  )\n}\n```\n\n### use() Hook\n\nRead promises and context in render. Works conditionally, after early returns:\n\n```tsx\n// Reading a promise - suspends until resolved\nfunction Comments({ commentsPromise }: { commentsPromise: Promise<Comment[]> }) {\n  const comments = use(commentsPromise)\n  return (\n    <ul>\n      {comments.map(c => <li key={c.id}>{c.text}</li>)}\n    </ul>\n  )\n}\n\n// Parent gets promise from loader/cache, NOT created during render\nfunction PostPage({ commentsPromise }: { commentsPromise: Promise<Comment[]> }) {\n  return (\n    <Suspense fallback={<Skeleton />}>\n      <Comments commentsPromise={commentsPromise} />\n    </Suspense>\n  )\n}\n\n// Reading context conditionally\nfunction AdminPanel({ user }: { user: User | null }) {\n  if (!user) return <LoginPrompt />\n  const permissions = use(PermissionsContext) // legal - use() works after early return\n  if (!permissions.isAdmin) return <Forbidden />\n  return <Dashboard user={user} permissions={permissions} />\n}\n```\n\n**Important:** `use()` does not support promises created during render. Pass promises from loaders, server functions, or cached sources.\n\n### Activity Component (React 19.2)\n\nPreserve state of hidden UI. Hidden children keep their state and DOM but unmount effects:\n\n```tsx\nimport { Activity, useState } from \"react\"\n\nfunction TabLayout({ tabs }: { tabs: TabConfig[] }) {\n  const [activeTab, setActiveTab] = useState(tabs[0].id)\n\n  return (\n    <div>\n      <nav>\n        {tabs.map(tab => (\n          <button key={tab.id} onClick={() => setActiveTab(tab.id)}>\n            {tab.label}\n          </button>\n        ))}\n      </nav>\n\n      {tabs.map(tab => (\n        <Activity key={tab.id} mode={activeTab === tab.id ? \"visible\" : \"hidden\"}>\n          <tab.component />\n        </Activity>\n      ))}\n    </div>\n  )\n}\n```\n\n**Key behaviors:**\n- `visible` - renders normally, effects mounted\n- `hidden` - hides via `display: none`, effects cleaned up, state preserved, updates deferred\n- Pre-rendering: `<Activity mode=\"hidden\">` renders children at low priority for faster future reveals\n- DOM side effects (video, audio) persist when hidden - add `useLayoutEffect` cleanup\n\n### useEffectEvent (React 19.2)\n\nExtract non-reactive logic from effects. The event function always sees latest props/state without triggering effect re-runs:\n\n```tsx\nfunction ChatRoom({ roomId, theme }: { roomId: string; theme: string }) {\n  const onConnected = useEffectEvent(() => {\n    showNotification(\"Connected!\", theme) // always reads latest theme\n  })\n\n  useEffect(() => {\n    const connection = createConnection(roomId)\n    connection.on(\"connected\", () => onConnected())\n    connection.connect()\n    return () => connection.disconnect()\n  }, [roomId]) // theme NOT in deps - onConnected is an Effect Event\n}\n```\n\n**Rules:**\n- Only call from inside effects or other effect events\n- Never pass to child components or include in dependency arrays\n- Never call during render\n- Use for logic that is conceptually an \"event\" fired from an effect\n\n**Custom hook pattern:**\n\n```tsx\nfunction useInterval(callback: () => void, delay: number | null) {\n  const onTick = useEffectEvent(callback)\n\n  useEffect(() => {\n    if (delay === null) return\n    const id = setInterval(() => onTick(), delay)\n    return () => clearInterval(id)\n  }, [delay])\n}\n```\n\n### Document Metadata\n\nRender `<title>`, `<meta>`, and `<link>` directly in components - React hoists them to `<head>`:\n\n```tsx\nfunction BlogPost({ post }: { post: Post }) {\n  return (\n    <article>\n      <title>{post.title}</title>\n      <meta name=\"description\" content={post.excerpt} />\n      <meta name=\"author\" content={post.author} />\n      <link rel=\"canonical\" href={`https://example.com/posts/${post.slug}`} />\n      <h1>{post.title}</h1>\n      <div>{post.content}</div>\n    </article>\n  )\n}\n```\n\n### Context as Provider\n\n```tsx\nconst ThemeContext = createContext<Theme>(\"light\")\n\n// React 19 - use Context directly as provider (no .Provider)\nfunction App({ children }: { children: React.ReactNode }) {\n  return (\n    <ThemeContext value=\"dark\">\n      {children}\n    </ThemeContext>\n  )\n}\n```\n\n### Ref Cleanup Functions\n\n```tsx\nfunction MeasuredBox() {\n  return (\n    <div\n      ref={(node) => {\n        if (node) {\n          const observer = new ResizeObserver(handleResize)\n          observer.observe(node)\n          return () => observer.disconnect() // cleanup on unmount\n        }\n      }}\n    />\n  )\n}\n```\n\n## React Compiler\n\n### What It Does\n\nReact Compiler (`babel-plugin-react-compiler`) analyzes your code at build time and automatically inserts memoization. It replaces manual `useMemo`, `useCallback`, and `React.memo` in most cases.\n\n**Auto-memoizes:**\n- Component return values (skip re-render if props unchanged)\n- Expensive computations inside components\n- Callback functions passed as props\n- JSX element creation\n\n### Setup (Vite)\n\n```bash\npnpm add -D babel-plugin-react-compiler\n```\n\n```ts\n// vite.config.ts\nimport { defineConfig } from \"vite\"\nimport react from \"@vitejs/plugin-react\"\n\nexport default defineConfig({\n  plugins: [\n    react({\n      babel: {\n        plugins: [\"babel-plugin-react-compiler\"], // must be first\n      },\n    }),\n  ],\n})\n```\n\n### What NOT to Do\n\n```tsx\n// DON'T - compiler handles this\nconst MemoComponent = memo(MyComponent)\nconst memoized = useMemo(() => expensive(data), [data])\nconst stableCallback = useCallback(() => handler(id), [id])\n\n// DO - write plain code, compiler optimizes\nfunction MyComponent({ data, onSelect }: Props) {\n  const processed = expensive(data)\n  return <Child onClick={() => onSelect(data.id)} />\n}\n```\n\n### When Manual Memoization Still Applies\n\n- `useMemo`/`useCallback` as effect dependencies when you need precise control over when effects fire\n- Values shared across many components (compiler memoizes per-component, not globally)\n- Opting out: `\"use no memo\"` directive skips compilation for a specific component\n\n### Verification\n\nComponents optimized by the compiler show a \"Memo\" badge in React DevTools. Check build output for `react/compiler-runtime` imports.\n\n## TypeScript Patterns\n\n### Strict tsconfig for React\n\n```jsonc\n{\n  \"compilerOptions\": {\n    \"strict\": true,\n    \"target\": \"esnext\",\n    \"module\": \"nodenext\",\n    \"moduleDetection\": \"force\",\n    \"jsx\": \"react-jsx\",\n    \"verbatimModuleSyntax\": true,\n    \"isolatedModules\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"exactOptionalPropertyTypes\": true,\n    \"noUncheckedSideEffectImports\": true,\n    \"skipLibCheck\": true,\n    \"declaration\": true,\n    \"declarationMap\": true,\n    \"sourceMap\": true,\n    \"types\": []\n  }\n}\n```\n\n### Component Props Patterns\n\n```tsx\n// Extending native element props\ntype ButtonProps = React.ComponentProps<\"button\"> & {\n  variant?: \"primary\" | \"secondary\"\n  isLoading?: boolean\n}\n\nfunction Button({ variant = \"primary\", isLoading, children, ...props }: ButtonProps) {\n  return (\n    <button data-slot=\"button\" disabled={isLoading || props.disabled} {...props}>\n      {isLoading ? <Spinner /> : children}\n    </button>\n  )\n}\n\n// Polymorphic \"as\" prop\ntype PolymorphicProps<E extends React.ElementType> = {\n  as?: E\n} & Omit<React.ComponentProps<E>, \"as\">\n\nfunction Text<E extends React.ElementType = \"span\">({\n  as,\n  ...props\n}: PolymorphicProps<E>) {\n  const Component = as || \"span\"\n  return <Component {...props} />\n}\n\n// Usage: <Text as=\"h1\">Hello</Text>\n```\n\n### Event Handler Types\n\n```tsx\nfunction Form() {\n  // Inferred from handler context - no explicit typing needed\n  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {\n    e.preventDefault()\n    const formData = new FormData(e.currentTarget)\n    // process formData\n  }\n\n  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n    console.log(e.target.value)\n  }\n\n  const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n    if (e.key === \"Enter\") submit()\n  }\n\n  return (\n    <form onSubmit={handleSubmit}>\n      <input onChange={handleChange} onKeyDown={handleKeyDown} />\n    </form>\n  )\n}\n```\n\n### Hook Types\n\n```tsx\n// useState - inferred when initial value provided\nconst [count, setCount] = useState(0) // number\nconst [user, setUser] = useState<User | null>(null) // explicit for null init\n\n// useReducer - discriminated union actions\ntype CounterAction =\n  | { type: \"increment\"; amount: number }\n  | { type: \"decrement\"; amount: number }\n  | { type: \"reset\" }\n\nfunction counterReducer(state: number, action: CounterAction): number {\n  switch (action.type) {\n    case \"increment\": return state + action.amount\n    case \"decrement\": return state - action.amount\n    case \"reset\": return 0\n  }\n}\n\nconst [count, dispatch] = useReducer(counterReducer, 0)\ndispatch({ type: \"increment\", amount: 5 })\n\n// useRef - element refs (React 19: returns RefObject<T | null>, always nullable)\nconst inputRef = useRef<HTMLInputElement>(null)\n\n// useRef - mutable value (no null)\nconst intervalRef = useRef<number | undefined>(undefined)\n```\n\n### Generic Components\n\n```tsx\ntype SelectProps<T> = {\n  items: T[]\n  value: T\n  onChange: (item: T) => void\n  getLabel: (item: T) => string\n  getKey: (item: T) => string\n}\n\nfunction Select<T>({ items, value, onChange, getLabel, getKey }: SelectProps<T>) {\n  return (\n    <select\n      value={getKey(value)}\n      onChange={(e) => {\n        const item = items.find(i => getKey(i) === e.target.value)\n        if (item) onChange(item)\n      }}\n    >\n      {items.map(item => (\n        <option key={getKey(item)} value={getKey(item)}>\n          {getLabel(item)}\n        </option>\n      ))}\n    </select>\n  )\n}\n\n// Usage - T inferred as User\n<Select\n  items={users}\n  value={selectedUser}\n  onChange={setSelectedUser}\n  getLabel={u => u.name}\n  getKey={u => u.id}\n/>\n```\n\n### Discriminated Unions for Component State\n\n```tsx\ntype AsyncState<T> =\n  | { status: \"idle\" }\n  | { status: \"loading\" }\n  | { status: \"error\"; error: Error }\n  | { status: \"success\"; data: T }\n\nfunction AsyncContent<T>({\n  state,\n  render,\n}: {\n  state: AsyncState<T>\n  render: (data: T) => React.ReactNode\n}) {\n  switch (state.status) {\n    case \"idle\": return null\n    case \"loading\": return <Spinner />\n    case \"error\": return <ErrorDisplay error={state.error} />\n    case \"success\": return <>{render(state.data)}</>\n  }\n}\n```\n\n### Zod v4 Integration\n\n```tsx\nimport { z } from \"zod\"\n\nconst UserSchema = z.object({\n  name: z.string().min(1),\n  email: z.string().email(),\n  role: z.enum([\"admin\", \"user\", \"viewer\"]),\n})\n\ntype User = z.infer<typeof UserSchema>\n\n// Form validation with useActionState\ntype FieldErrors = { name?: string[]; email?: string[]; role?: string[] }\n\nfunction CreateUser() {\n  const [errors, submitAction, isPending] = useActionState(\n    async (_prev: FieldErrors | null, formData: FormData) => {\n      const result = UserSchema.safeParse(Object.fromEntries(formData))\n      if (!result.success) {\n        // Zod v4: use z.flattenError() to get field-level errors\n        const flat = z.flattenError(result.error)\n        return flat.fieldErrors as FieldErrors\n      }\n      await saveUser(result.data)\n      return null\n    },\n    null\n  )\n\n  return (\n    <form action={submitAction}>\n      <input name=\"name\" />\n      {errors?.name && (\n        <span className=\"text-destructive\">{errors.name[0]}</span>\n      )}\n      <input name=\"email\" type=\"email\" />\n      <select name=\"role\">\n        <option value=\"user\">User</option>\n        <option value=\"admin\">Admin</option>\n        <option value=\"viewer\">Viewer</option>\n      </select>\n      <button type=\"submit\" disabled={isPending}>Create</button>\n    </form>\n  )\n}\n```\n\n## Best Practices\n\n1. **Plain functions for components** - no `FC`, no `forwardRef`, no `memo`. `FC` implicitly typed `children` in older types and adds no value over plain function signatures. Let React Compiler optimize.\n2. **`React.ComponentProps<\"element\">`** for extending native elements - catches all HTML attributes.\n3. **Discriminated unions over booleans** for state - prevents impossible states at the type level.\n4. **`use()` over `useContext()`** - works conditionally, cleaner for context with guards.\n5. **`satisfies` for config objects** - preserves literal types while validating shape.\n6. **`Activity` over conditional rendering** when state preservation matters (tabs, sidebars, wizards).\n7. **`useEffectEvent` over suppressing deps** - extracts non-reactive logic cleanly from effects.\n8. **Strict tsconfig** - enable `noUncheckedIndexedAccess`, `exactOptionalPropertyTypes`, `verbatimModuleSyntax`.\n9. **`as const satisfies`** for route configs, theme tokens, and lookup objects.\n10. **Type narrowing in switch** - exhaustive checks via `never` in default cases.\n11. **`import defer`** (TS 5.9) - defer module evaluation until first property access for lazy-loaded heavy modules. See [typescript-patterns.md](references/typescript-patterns.md).\n\n## Deep Dives\n\n- [react-19-features.md](references/react-19-features.md) - Complete React 19/19.2 feature reference with detailed examples\n- [typescript-patterns.md](references/typescript-patterns.md) - Advanced TypeScript patterns for React: generics, utility types, strict config, type-level programming\n\n## Resources\n\n- **React Docs**: https://react.dev\n- **React 19.2 Blog Post**: https://react.dev/blog/2025/10/01/react-19-2\n- **React Compiler**: https://react.dev/learn/react-compiler\n- **TypeScript 5.9**: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-9.html\n- **TypeScript 5.8**: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-8.html\n- **React TypeScript Cheatsheet**: https://react-typescript-cheatsheet.netlify.app/","tags":["react","typescript","skills","tenequm","agent-skills","ai-agents","claude-code","claude-skills","clawhub","erc-8004","mpp","openclaw"],"capabilities":["skill","source-tenequm","skill-react-typescript","topic-agent-skills","topic-ai-agents","topic-claude-code","topic-claude-skills","topic-clawhub","topic-erc-8004","topic-mpp","topic-openclaw","topic-skills","topic-solana","topic-x402"],"categories":["skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/tenequm/skills/react-typescript","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add tenequm/skills","source_repo":"https://github.com/tenequm/skills","install_from":"skills.sh"}},"qualityScore":"0.461","qualityRationale":"deterministic score 0.46 from registry signals: · indexed on github topic:agent-skills · 23 github stars · SKILL.md body (19,208 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-04-22T01:01:40.384Z","embedding":null,"createdAt":"2026-04-18T23:05:22.734Z","updatedAt":"2026-04-22T01:01:40.384Z","lastSeenAt":"2026-04-22T01:01:40.384Z","tsv":"'/about':287,301 '/blog/2025/10/01/react-19-2':1926 '/docs/handbook/release-notes/typescript-5-8.html':1941 '/docs/handbook/release-notes/typescript-5-9.html':1936 '/learn/react-compiler':1931 '/posts/$':1003 '/profile':554 '0':776,1418,1469,1475,1722 '1':620,1647,1734 '10':1855 '11':1867 '19':6,42,96,402,437,1016,1485 '19.2':53,744,842,1921 '19/19.2':1894 '2':1764 '3':1775 '4':1789 '5':1480,1800 '5.8':1938 '5.9':57,1871,1933 '6':1811 '7':1823 '8':1836 '9':1843 'access':1878 'across':1215 'action':11,511,560,577,630,1434,1451,1717 'action.amount':1460,1465 'action.type':1455 'activ':12,741,762,790,1812 'activetab':772,794 'add':837,1116,1753 'addoptimisticlik':615,624 'admin':1653,1724 'adminpanel':696 'advanc':1902 'alway':853,878,1490 'amount':1439,1443,1479 'analyz':1067 'app':1025 'appli':1198 'applic':7,54 'array':922 'async':514,537,594,623,1678 'asynccont':1604 'asyncst':1590,1608 'attribut':197,1774 'audio':833 'authcontext':325,346,357 'author':440,994 'authprovid':369 'authstat':327,348,353 'auto':160,1088 'auto-memo':159,1087 'autocomplet':308 'automat':62,525,1074 'await':545,595,626,1709 'babel':1063,1119,1138,1141 'babel-plugin-react-compil':1062,1118,1140 'badg':1246 'bash':1114 'behavior':799 'best':1732 'bg':486 'bg-card':485 'blog':1922 'blogpost':981 'boolean':206,228,237,412,1311,1779 'border':484 'bound':426 'build':4,48,1071,1251 'button':219,562,589,781,1306,1313,1321,1325,1726 'buttonprop':200,217,1304,1319 'c':666 'c.id':669 'c.text':670 'cach':739 'call':905,924 'callback':945,953,1104 'cannot':385 'canon':999 'card':463,474,487,502 'card-titl':501 'cardprop':456,468 'cardtitl':491 'case':1086,1456,1461,1466,1615,1619,1622,1628,1866 'catch':1771 'chatroom':865 'cheatsheet':1944 'check':293,334,1250,1861 'child':916,1190 'children':207,751,821,1026,1027,1030,1317,1331,1748 'classnam':209,466,479,488,492,504,509 'clean':811,1833 'cleaner':1795 'cleanup':839,1032,1052 'clearinterv':965 'cn':480,505 'code':1069,1177 'color':397,433 'comment':655,659,661,685,689 'comments.map':665 'commentspromis':656,657,663,682,683,690,691 'compar':137,178 'compil':16,59,118,124,158,1056,1061,1066,1122,1144,1155,1178,1218,1232,1242,1762,1928 'compileropt':1263 'complet':1892 'compon':29,65,439,742,917,974,1090,1103,1217,1222,1236,1238,1295,1348,1352,1508,1586,1738 'comput':1101 'conceptu':932 'condit':643,694,1794,1814 'config':1803,1849,1911 'connect':876,884,888 'connection.connect':890 'connection.disconnect':892 'connection.on':887 'console.log':1386 'const':83,125,133,139,175,278,295,324,345,354,381,417,532,543,584,613,621,660,704,771,872,883,950,959,1011,1043,1158,1162,1168,1185,1347,1370,1375,1382,1388,1414,1420,1470,1492,1501,1543,1641,1673,1684,1701,1845 'consum':330 'content':990,995 'context':310,639,693,1007,1018,1365,1797 'control':1208 'correct':94,156,211,244,288,337,400 'count':1415,1471 'counteract':1436,1452 'counterreduc':1448,1474 'cover':10 'creat':28,677,729,1731 'createconnect':885 'createcontext':326,347,1013 'createus':1672 'creation':1111 'critic':70 'ctx':355,359,371 'custom':939 'd':1117 'dashboard':718 'data':241,262,445,472,476,499,1166,1167,1182,1188,1323,1601,1610 'data-slot':444,471,498,1322 'data-vari':475 'data.id':1193 'declar':1288 'declarationmap':1290 'decrement':1442,1462 'deep':1888 'default':314,320,1134,1865 'defer':816,1869,1872 'defineconfig':1126,1135 'delay':947,956,963,967 'delet':598,599 'deletebutton':580 'dep':897,1827 'depend':921,1203 'deprec':81 'descript':989 'detail':1898 'devtool':1249 'direct':972,1019,1230 'disabl':205,565,590,1326,1729 'discrimin':225,245,1432,1583,1776 'dispatch':1472,1476 'display':808 'div':458,470,1038 'dive':1889 'doc':1918 'document':968 'dom':756,829 'e':202,1338,1372,1384,1390,1542 'e.currenttarget':1379 'e.key':1393 'e.preventdefault':1374 'e.target.value':1387,1549 'earli':388,406,645,712 'effect':759,803,810,831,849,859,901,908,911,938,1202,1211,1835 'element':191,214,1110,1301,1482,1766,1770 'elev':460 'email':1648,1650,1667 'enabl':1839 'enter':1394 'error':238,257,258,363,521,533,570,571,1596,1597,1598,1623,1626,1674,1700,1719 'errordisplay':1625 'errors.name':1721 'esnext':1267 'evalu':1874 'event':35,851,902,912,934,1356 'everi':335 'exactoptionalpropertytyp':1282,1841 'exampl':1899 'example.com':1002 'example.com/posts/$':1001 'exhaust':1860 'expens':1100,1165,1187 'explicit':1367,1427 'export':1133 'extend':212,1299,1768 'extract':843,1828 'factori':338 'fallback':688 'faster':826 'fc':453,1740,1745 'featu':43 'featur':1895 'feedback':603 'field':1698 'field-level':1697 'fielderror':1664,1680,1708 'fire':935,1212 'first':1147,1876 'flat':1702 'flat.fielderrors':1706 'font':507 'font-semibold':506 'forc':1271 'form':523,559,576,629,1361,1397,1659,1716 'formdata':541,542,548,1376,1378,1381,1682,1683,1688 'forwardref':73,85,451,1742 'function':102,128,165,351,379,408,442,462,490,515,527,579,605,654,680,695,737,766,852,864,943,980,1024,1033,1035,1105,1180,1312,1342,1360,1447,1528,1603,1671,1736,1758 'futur':827 'generic':1507,1907 'get':672,1696 'getkey':1524,1534,1539,1547,1558,1561,1580 'getlabel':1520,1533,1563,1577 'ghost':222 'global':1224 'guard':323,1799 'h1':395,431 'h3':495,497 'handl':34,60,518,1156 'handlechang':1383,1402 'handleclick':140,154 'handlekeydown':1389,1404 'handlelik':622,631 'handler':1171,1357,1364 'handleres':1047 'handlesubmit':1371,1399 'header':380,409 'heavi':1883 'hello':1355 'hidden':748,750,797,805,836 'hide':806 'hoist':976 'home':283,297 'hook':14,37,339,428,449,635,940,1405 'href':1000 'html':196,1773 'htmlinputel':86 'id':142,145,172,777,960,966,1172,1173 'idl':253,1592,1616 'implicit':1746 'import':723,761,1125,1129,1255,1637,1868 'imposs':231,248,1783 'includ':919 'increment':1438,1457,1478 'infer':1362,1409,1567 'init':1430 'initi':1411 'input':84,90,103,107,109,1400 'inputprop':87 'inputref':1493 'insert':1075 'insid':907,1102 'instant':602 'integr':1635 'interfac':199,234 'intervalref':1502 'isload':236,1310,1316,1327,1330 'isolatedmodul':1278 'ispend':535,566,567,585,591,597,1676,1730 'isvis':391,410,411,414 'item':130,131,132,138,149,167,169,170,181,1512,1517,1521,1525,1530,1544,1551,1553,1555,1559,1562,1564,1571 'item.id':152,155,184,187 'items.find':1545 'items.map':1554 'items.tosorted':136,177 'jsonc':1262 'jsx':1109,1272,1275 'keep':752 'key':151,183,291,668,782,791,798,1557 'latest':855,880 'lazi':1881 'lazy-load':1880 'legal':708 'let':66,1760 'level':1699,1788,1914 'li':667 'light':1014 'like':607,609,617,633 'likebutton':606 'link':997 'list':129,166 'liter':270,290,1806 'load':255,1594,1620,1882 'loader':735 'loader/cache':675 'logic':847,929,1832 'lookup':1853 'low':823 'manag':30 'mani':1216 'manual':114,195,1079,1195 'matter':1819 'measuredbox':1036 'memo':127,1160,1229,1245,1744 'memocompon':1159 'memoiz':61,115,161,1076,1089,1163,1196,1219 'memoizedlist':126 'meta':987,992 'metadata':969 'min':1646 'miss':343 'mode':793 'modul':1268,1873,1884 'moduledetect':1270 'mount':804 'mouseev':203 'must':311,331,365,1145 'mutabl':1497 'mycompon':1161,1181 'name':988,993,1644,1665,1720 'narrow':1857 'nativ':213,1300,1769 'need':1206,1369 'never':913,923,1863 'new':362,1045,1377 'node':1040,1042,1049 'nodenext':1269 'non':575,845,1830 'non-form':574 'non-react':844,1829 'none':809 'normal':802 'nouncheckedindexedaccess':1280,1840 'nouncheckedsideeffectimport':1284 'null':240,243,319,328,329,333,349,350,360,393,416,540,556,557,625,700,949,957,1425,1426,1429,1489,1495,1500,1618,1681,1713,1714 'null-check':332 'nullabl':1491 'number':610,948,1419,1440,1444,1450,1453,1504 'object':1804,1854 'object.fromentries':1687 'observ':1044 'observer.disconnect':1051 'observer.observe':1048 'old':377 'older':1750 'omit':1339 'onchang':1401,1516,1532,1541,1552,1575 'onclick':153,185,201,592,784,1191 'onconnect':873,889,898 'ondelet':581,582,596 'onkeydown':1403 'onlik':608,611,627 'onselect':144,146,168,171,186,1183,1192 'onsubmit':1398 'ontick':951,962 'opt':1225 'optim':69,1179,1239,1763 'optimisticlik':614,632 'option':1556 'outlin':461,465 'output':1252 'parent':671 'pass':732,914,1106 'path':284,286,298,300 'pattern':25,46,82,378,438,941,1257,1297,1904 'pend':519 'per':1221 'per-compon':1220 'permiss':705,721,722 'permissions.isadmin':715 'permissionscontext':707 'persist':834 'plain':64,441,1176,1735,1757 'plugin':1064,1120,1136,1139,1142 'pnpm':1115 'polymorph':1332 'polymorphicprop':1336,1346 'possibl':233 'post':982,983,984,1923 'post.author':996 'post.content':1006 'post.excerpt':991 'post.slug':1004 'post.title':986,1005 'postpag':681 'practic':1733 'pre':818 'pre-rend':817 'precis':1207 'prefer':372 'preserv':289,745,814,1805,1818 'prev':538,618,619,1679 'prevent':247,1782 'primari':221,1308,1315 'prioriti':824 'process':1186,1380 'program':1915 'promis':583,612,637,650,658,673,684,728,733 'prop':20,33,77,88,93,101,105,112,192,215,467,489,493,510,1098,1108,1184,1296,1302,1318,1329,1334,1345,1353 'properti':1877 'props.disabled':1328 'props/state':856 'provid':344,1009,1021,1023,1413 're':861,1095 're-rend':1094 're-run':860 'react':2,5,15,41,44,52,58,95,117,123,157,401,436,743,765,841,975,1015,1055,1060,1065,1121,1130,1137,1143,1248,1261,1274,1484,1761,1893,1906,1917,1920,1927,1942 'react-19-features.md':1890 'react-jsx':1273 'react-typescript':1 'react-typescript-cheatsheet.netlify.app':1945 'react.changeevent':1385 'react.componentprops':106,189,218,457,494,1305,1340,1765 'react.dev':1919,1925,1930 'react.dev/blog/2025/10/01/react-19-2':1924 'react.dev/learn/react-compiler':1929 'react.formevent':1373 'react.keyboardevent':1391 'react.memo':1083 'react.reactnode':208,1028,1612 'react/compiler-runtime':1254 'reactiv':846,1831 'read':636,648,692,879 'record':275,280,303 'redirect':553 'ref':18,74,89,91,92,97,104,110,111,1031,1039,1483 'ref-as-prop':17 'refer':1896 'references/react-19-features.md':1891 'references/typescript-patterns.md':1887,1901 'refobject':1487 'regular':100 'rel':998 'render':641,679,731,801,819,820,926,970,1096,1606,1609,1631,1815 'replac':1078 'requestst':235,251 'reset':524,1446,1467 'resizeobserv':1046 'resolv':653 'resourc':1916 'result':544,1685 'result.data':1711 'result.error':550,552,1704 'result.success':1690 'return':108,147,179,370,389,392,394,407,415,430,469,496,551,555,558,588,628,646,664,686,703,713,716,717,778,891,958,964,985,1029,1037,1050,1091,1189,1320,1351,1396,1458,1463,1468,1486,1536,1617,1621,1624,1630,1705,1712,1715 'reveal':828 'role':1651,1669 'roomid':866,868,886,893 'round':482 'rounded-xl':481 'rout':277,279,282,296,305,1848 'routes.home':306 'row':150,182 'rule':71,429,903 'run':862 'safe':51,269 'satisfi':265,302,1801,1846 'save':568,569 'saveus':1710 'secondari':1309 'see':854,1885 'select':1529,1537,1570 'selectedus':1574 'selectprop':1511,1535 'semibold':508 'server':736 'setactivetab':773,785 'setcount':1416 'setinterv':961 'setselectedus':1576 'setup':1112 'setus':1422 'shape':294,1810 'share':1214 'show':1243 'shownotif':875 'side':830 'sidebar':1821 'signatur':1759 'skill' 'skill-react-typescript' 'skip':1093,1231 'skiplibcheck':1286 'slot':446,473,500,1324 'sort':134,176 'sorted.map':148,180 'sourc':740 'source-tenequm' 'sourcemap':1292 'span':1350 'specif':1235 'stablecallback':1169 'starttransit':586,593 'state':31,224,232,249,520,746,754,813,1449,1459,1464,1587,1605,1607,1781,1784,1817 'state.data':1632 'state.error':1627 'state.status':1614 'status':252,254,256,260,1591,1593,1595,1599 'still':1197 'strict':23,313,1258,1264,1837,1910 'string':143,173,210,239,259,276,281,304,531,539,869,871,1523,1527,1666,1668,1670 'style':396,432,448 'submit':564,1395,1728 'submitact':534,561,1675,1718 'success':261,1600,1629 'support':727 'suppress':1826 'suspend':651 'suspens':687 'switch':1454,1613,1859 'tab':768,769,775,780,789,1820 'tab.id':783,786,792,795 'tab.label':787 'tabconfig':770 'tablayout':767 'tabs.map':779,788 'target':1266 'text':1343 'theme':382,418,867,870,877,881,894,1850 'theme.color':398,434 'themecontext':384,420,1012 'throw':316,341,361 'time':336,1072 'titl':399,435,503 'token':1851 'tool':68 'topic-agent-skills' 'topic-ai-agents' 'topic-claude-code' 'topic-claude-skills' 'topic-clawhub' 'topic-erc-8004' 'topic-mpp' 'topic-openclaw' 'topic-skills' 'topic-solana' 'topic-x402' 'transit':517 'trigger':858 'true':1265,1277,1279,1281,1283,1285,1287,1289,1291,1293 'ts':1123,1870 'tsconfig':1259,1838 'tsx':79,119,193,229,271,317,376,454,526,578,604,647,760,863,942,979,1010,1034,1152,1298,1359,1407,1509,1588,1636 'type':32,50,198,216,223,250,268,307,455,563,1294,1303,1335,1358,1368,1406,1435,1437,1441,1445,1477,1510,1589,1656,1663,1727,1747,1751,1787,1807,1856,1909,1913 'type-level':1912 'type-saf':49,267 'typescript':3,9,24,45,56,1256,1903,1932,1937,1943 'typescript-patterns.md':1886,1900 'u':1578,1581 'u.id':1582 'u.name':1579 'ui':749 'unchang':1099 'undefin':1505,1506 'union':226,246,1433,1584,1777 'unmount':758,1054 'unnecessari':121 'updat':815 'updateprofil':528,546 'usag':1354,1565 'use':13,26,36,188,264,356,367,373,386,403,419,423,634,662,706,709,724,927,1017,1227,1693,1790 'useactionst':536,1662,1677 'useauth':352,364 'usecallback':141,1081,1170,1200 'usecontext':375,383,1792 'useeffect':882,954 'useeffectev':21,840,874,952,1824 'useinterv':944 'uselayouteffect':838 'usememo':135,1080,1164,1199 'useoptimist':600,616 'user':242,263,697,698,699,702,719,720,1421,1424,1569,1572,1654,1657,1723 'usereduc':1431,1473 'useref':1481,1494,1496,1503 'userid':529,530,547 'userschema':1642 'userschema.safeparse':1686 'usest':763,774,1408,1417,1423 'usetransit':513,572,587 'util':1908 'v4':1634,1692 'valid':1660,1809 'valu':1092,1213,1412,1498,1514,1531,1538,1540,1560,1573,1755 'variant':220,459,464,477,478,1307,1314 'verbatimmodulesyntax':1276,1842 'verif':1237 'via':807,1862 'video':832 'viewer':1655,1725 'visibl':796,800 'vite':1113,1128 'vite.config.ts':1124 'vitejs/plugin-react':1132 'void':174,204,946,1519 'widen':273 'within':368 'without':857 'wizard':1822 'work':39,309,404,421,642,710,1793 'write':63,1175 'wrong':80,120,194,230,272,318 'www.typescriptlang.org':1935,1940 'www.typescriptlang.org/docs/handbook/release-notes/typescript-5-8.html':1939 'www.typescriptlang.org/docs/handbook/release-notes/typescript-5-9.html':1934 'xl':483 'z':1638 'z.enum':1652 'z.flattenerror':1694,1703 'z.infer':1658 'z.object':1643 'z.string':1645,1649 'zod':1633,1640,1691","prices":[{"id":"01287cb1-a85d-4841-b57c-a867e22b9e82","listingId":"85167bf6-15a1-42c7-b628-6d7d81ec92f7","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"tenequm","category":"skills","install_from":"skills.sh"},"createdAt":"2026-04-18T23:05:22.734Z"}],"sources":[{"listingId":"85167bf6-15a1-42c7-b628-6d7d81ec92f7","source":"github","sourceId":"tenequm/skills/react-typescript","sourceUrl":"https://github.com/tenequm/skills/tree/main/skills/react-typescript","isPrimary":false,"firstSeenAt":"2026-04-18T23:05:22.734Z","lastSeenAt":"2026-04-22T01:01:40.384Z"}],"details":{"listingId":"85167bf6-15a1-42c7-b628-6d7d81ec92f7","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"tenequm","slug":"react-typescript","github":{"repo":"tenequm/skills","stars":23,"topics":["agent-skills","ai-agents","claude-code","claude-skills","clawhub","erc-8004","mpp","openclaw","skills","solana","x402"],"license":"mit","html_url":"https://github.com/tenequm/skills","pushed_at":"2026-04-14T16:24:57Z","description":"Agent skills for building, shipping, and growing software products","skill_md_sha":"a407424fad4fdc94f3e9434af3df42b3d402538f","skill_md_path":"skills/react-typescript/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/tenequm/skills/tree/main/skills/react-typescript"},"layout":"multi","source":"github","category":"skills","frontmatter":{"name":"react-typescript","description":"Build React 19 applications with TypeScript. Covers Actions, Activity, use() hook, React Compiler, ref-as-prop, useEffectEvent, and strict TypeScript patterns. Use when creating components, managing state, typing props, handling events, using hooks, or working with React 19 features. Triggers on react, typescript, tsx, component types, hook types, react 19, react compiler, actions, use hook, useEffectEvent, activity, import defer."},"skills_sh_url":"https://skills.sh/tenequm/skills/react-typescript"},"updatedAt":"2026-04-22T01:01:40.384Z"}}