{"id":"f8cad97a-18d0-4bf7-8421-bc7848dfba18","shortId":"sYps3d","kind":"skill","title":"react-dev","tagline":"This skill should be used when building React components with TypeScript, typing hooks, handling events, or when React TypeScript, React 19, Server Components are mentioned. Covers type-safe patterns for React 18-19 including generic components, proper event typing, and routing i","description":"# React TypeScript\n\nType-safe React = compile-time guarantees = confident refactoring.\n\n<when_to_use>\n\n- Building typed React components\n- Implementing generic components\n- Typing event handlers, forms, refs\n- Using React 19 features (Actions, Server Components, use())\n- Router integration (TanStack Router, React Router)\n- Custom hooks with proper typing\n\nNOT for: non-React TypeScript, vanilla JS React\n\n</when_to_use>\n\n<react_19_changes>\n\nReact 19 breaking changes require migration. Key patterns:\n\n**ref as prop** - forwardRef deprecated:\n\n```typescript\n// React 19 - ref as regular prop\ntype ButtonProps = {\n  ref?: React.Ref<HTMLButtonElement>;\n} & React.ComponentPropsWithoutRef<'button'>;\n\nfunction Button({ ref, children, ...props }: ButtonProps) {\n  return <button ref={ref} {...props}>{children}</button>;\n}\n```\n\n**useActionState** - replaces useFormState:\n\n```typescript\nimport { useActionState } from 'react';\n\ntype FormState = { errors?: string[]; success?: boolean };\n\nfunction Form() {\n  const [state, formAction, isPending] = useActionState(submitAction, {});\n  return <form action={formAction}>...</form>;\n}\n```\n\n**use()** - unwraps promises/context:\n\n```typescript\nfunction UserProfile({ userPromise }: { userPromise: Promise<User> }) {\n  const user = use(userPromise); // Suspends until resolved\n  return <div>{user.name}</div>;\n}\n```\n\nSee [react-19-patterns.md](references/react-19-patterns.md) for useOptimistic, useTransition, migration checklist.\n\n</react_19_changes>\n\n<component_patterns>\n\n**Props** - extend native elements:\n\n```typescript\ntype ButtonProps = {\n  variant: 'primary' | 'secondary';\n} & React.ComponentPropsWithoutRef<'button'>;\n\nfunction Button({ variant, children, ...props }: ButtonProps) {\n  return <button className={variant} {...props}>{children}</button>;\n}\n```\n\n**Children typing**:\n\n```typescript\ntype Props = {\n  children: React.ReactNode;          // Anything renderable\n  icon: React.ReactElement;           // Single element\n  render: (data: T) => React.ReactNode;  // Render prop\n};\n```\n\n**Discriminated unions** for variant props:\n\n```typescript\ntype ButtonProps =\n  | { variant: 'link'; href: string }\n  | { variant: 'button'; onClick: () => void };\n\nfunction Button(props: ButtonProps) {\n  if (props.variant === 'link') {\n    return <a href={props.href}>Link</a>;\n  }\n  return <button onClick={props.onClick}>Button</button>;\n}\n```\n\n</component_patterns>\n\n<event_handlers>\n\nUse specific event types for accurate target typing:\n\n```typescript\n// Mouse\nfunction handleClick(e: React.MouseEvent<HTMLButtonElement>) {\n  e.currentTarget.disabled = true;\n}\n\n// Form\nfunction handleSubmit(e: React.FormEvent<HTMLFormElement>) {\n  e.preventDefault();\n  const formData = new FormData(e.currentTarget);\n}\n\n// Input\nfunction handleChange(e: React.ChangeEvent<HTMLInputElement>) {\n  console.log(e.target.value);\n}\n\n// Keyboard\nfunction handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {\n  if (e.key === 'Enter') e.currentTarget.blur();\n}\n```\n\nSee [event-handlers.md](references/event-handlers.md) for focus, drag, clipboard, touch, wheel events.\n\n</event_handlers>\n\n<hooks_typing>\n\n**useState** - explicit for unions/null:\n\n```typescript\nconst [user, setUser] = useState<User | null>(null);\nconst [status, setStatus] = useState<'idle' | 'loading'>('idle');\n```\n\n**useRef** - null for DOM, value for mutable:\n\n```typescript\nconst inputRef = useRef<HTMLInputElement>(null);  // DOM - use ?.\nconst countRef = useRef<number>(0);               // Mutable - direct access\n```\n\n**useReducer** - discriminated unions for actions:\n\n```typescript\ntype Action =\n  | { type: 'increment' }\n  | { type: 'set'; payload: number };\n\nfunction reducer(state: State, action: Action): State {\n  switch (action.type) {\n    case 'set': return { ...state, count: action.payload };\n    default: return state;\n  }\n}\n```\n\n**Custom hooks** - tuple returns with as const:\n\n```typescript\nfunction useToggle(initial = false) {\n  const [value, setValue] = useState(initial);\n  const toggle = () => setValue(v => !v);\n  return [value, toggle] as const;\n}\n```\n\n**useContext** - null guard pattern:\n\n```typescript\nconst UserContext = createContext<User | null>(null);\n\nfunction useUser() {\n  const user = useContext(UserContext);\n  if (!user) throw new Error('useUser outside UserProvider');\n  return user;\n}\n```\n\nSee [hooks.md](references/hooks.md) for useCallback, useMemo, useImperativeHandle, useSyncExternalStore.\n\n</hooks_typing>\n\n<generic_components>\n\nGeneric components infer types from props - no manual annotations at call site.\n\n**Pattern** - keyof T for column keys, render props for custom rendering:\n\n```typescript\ntype Column<T> = {\n  key: keyof T;\n  header: string;\n  render?: (value: T[keyof T], item: T) => React.ReactNode;\n};\n\ntype TableProps<T> = {\n  data: T[];\n  columns: Column<T>[];\n  keyExtractor: (item: T) => string | number;\n};\n\nfunction Table<T>({ data, columns, keyExtractor }: TableProps<T>) {\n  return (\n    <table>\n      <thead>\n        <tr>{columns.map(col => <th key={String(col.key)}>{col.header}</th>)}</tr>\n      </thead>\n      <tbody>\n        {data.map(item => (\n          <tr key={keyExtractor(item)}>\n            {columns.map(col => (\n              <td key={String(col.key)}>\n                {col.render ? col.render(item[col.key], item) : String(item[col.key])}\n              </td>\n            ))}\n          </tr>\n        ))}\n      </tbody>\n    </table>\n  );\n}\n```\n\n**Constrained generics** for required properties:\n\n```typescript\ntype HasId = { id: string | number };\n\nfunction List<T extends HasId>({ items }: { items: T[] }) {\n  return <ul>{items.map(item => <li key={item.id}>...</li>)}</ul>;\n}\n```\n\nSee [generic-components.md](examples/generic-components.md) for Select, List, Modal, FormField patterns.\n\n</generic_components>\n\n<server_components>\n\nReact 19 Server Components run on server, can be async.\n\n**Async data fetching**:\n\n```typescript\nexport default async function UserPage({ params }: { params: { id: string } }) {\n  const user = await fetchUser(params.id);\n  return <div>{user.name}</div>;\n}\n```\n\n**Server Actions** - 'use server' for mutations:\n\n```typescript\n'use server';\n\nexport async function updateUser(userId: string, formData: FormData) {\n  await db.user.update({ where: { id: userId }, data: { ... } });\n  revalidatePath(`/users/${userId}`);\n}\n```\n\n**Client + Server Action**:\n\n```typescript\n'use client';\n\nimport { useActionState } from 'react';\nimport { updateUser } from '@/actions/user';\n\nfunction UserForm({ userId }: { userId: string }) {\n  const [state, formAction, isPending] = useActionState(\n    (prev, formData) => updateUser(userId, formData), {}\n  );\n  return <form action={formAction}>...</form>;\n}\n```\n\n**use() for promise handoff**:\n\n```typescript\n// Server: pass promise without await\nasync function Page() {\n  const userPromise = fetchUser('123');\n  return <UserProfile userPromise={userPromise} />;\n}\n\n// Client: unwrap with use()\n'use client';\nfunction UserProfile({ userPromise }: { userPromise: Promise<User> }) {\n  const user = use(userPromise);\n  return <div>{user.name}</div>;\n}\n```\n\nSee [server-components.md](examples/server-components.md) for parallel fetching, streaming, error boundaries.\n\n</server_components>\n\n<routing>\n\nBoth TanStack Router and React Router v7 provide type-safe routing solutions.\n\n**TanStack Router** - Compile-time type safety with Zod validation:\n\n```typescript\nimport { createRoute } from '@tanstack/react-router';\nimport { z } from 'zod';\n\nconst userRoute = createRoute({\n  path: '/users/$userId',\n  component: UserPage,\n  loader: async ({ params }) => ({ user: await fetchUser(params.userId) }),\n  validateSearch: z.object({\n    tab: z.enum(['profile', 'settings']).optional(),\n    page: z.number().int().positive().default(1),\n  }),\n});\n\nfunction UserPage() {\n  const { user } = useLoaderData({ from: userRoute.id });\n  const { tab, page } = useSearch({ from: userRoute.id });\n  const { userId } = useParams({ from: userRoute.id });\n}\n```\n\n**React Router v7** - Automatic type generation with Framework Mode:\n\n```typescript\nimport type { Route } from \"./+types/user\";\n\nexport async function loader({ params }: Route.LoaderArgs) {\n  return { user: await fetchUser(params.userId) };\n}\n\nexport default function UserPage({ loaderData }: Route.ComponentProps) {\n  const { user } = loaderData; // Typed from loader\n  return <h1>{user.name}</h1>;\n}\n```\n\nSee [tanstack-router.md](references/tanstack-router.md) for TanStack patterns and [react-router.md](references/react-router.md) for React Router patterns.\n\n</routing>\n\n<rules>\n\nALWAYS:\n- Specific event types (MouseEvent, ChangeEvent, etc)\n- Explicit useState for unions/null\n- ComponentPropsWithoutRef for native element extension\n- Discriminated unions for variant props\n- as const for tuple returns\n- ref as prop in React 19 (no forwardRef)\n- useActionState for form actions\n- Type-safe routing patterns (see routing section)\n\nNEVER:\n- any for event handlers\n- JSX.Element for children (use ReactNode)\n- forwardRef in React 19+\n- useFormState (deprecated)\n- Forget null handling for DOM refs\n- Mix Server/Client components in same file\n- Await promises when passing to use()\n\n</rules>\n\n<references>\n\n- [hooks.md](references/hooks.md) - useState, useRef, useReducer, useContext, custom hooks\n- [event-handlers.md](references/event-handlers.md) - all event types, generic handlers\n- [react-19-patterns.md](references/react-19-patterns.md) - useActionState, use(), useOptimistic, migration\n- [generic-components.md](examples/generic-components.md) - Table, Select, List, Modal patterns\n- [server-components.md](examples/server-components.md) - async components, Server Actions, streaming\n- [tanstack-router.md](references/tanstack-router.md) - TanStack Router typed routes, search params, navigation\n- [react-router.md](references/react-router.md) - React Router v7 loaders, actions, type generation, forms\n\n</references>","tags":["react","dev","agent","toolkit","softaworks","agent-skills","automation","claude","claude-code","coding-agent","development"],"capabilities":["skill","source-softaworks","skill-react-dev","topic-agent-skills","topic-automation","topic-claude","topic-claude-code","topic-coding-agent","topic-development"],"categories":["agent-toolkit"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/softaworks/agent-toolkit/react-dev","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add softaworks/agent-toolkit","source_repo":"https://github.com/softaworks/agent-toolkit","install_from":"skills.sh"}},"qualityScore":"0.700","qualityRationale":"deterministic score 0.70 from registry signals: · indexed on github topic:agent-skills · 1847 github stars · SKILL.md body (9,910 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-05-18T18:52:58.841Z","embedding":null,"createdAt":"2026-04-18T20:27:58.504Z","updatedAt":"2026-05-18T18:52:58.841Z","lastSeenAt":"2026-05-18T18:52:58.841Z","tsv":"'-19':37 '/actions/user':636 '/users':621,739 '0':354 '1':762 '123':672 '18':36 '19':24,73,100,114,568,865,893 'access':357 'accur':270 'action':75,161,362,365,376,377,598,625,654,871,947,964 'action.payload':386 'action.type':380 'alway':834 'annot':460 'anyth':220 'async':576,577,583,607,666,744,797,944 'automat':784 'await':592,614,665,747,804,908 'boolean':150 'boundari':702 'break':101 'build':10,59 'button':124,126,132,200,202,208,245,249,261,264 'buttonprop':120,130,195,206,239,251 'call':462 'case':381 'chang':102 'changeev':839 'checklist':188 'children':128,136,204,212,213,218,887 'classnam':209 'client':623,628,677,682 'clipboard':314 'col':510,523 'col.header':515 'col.key':514,527,531,535 'col.render':528,529 'column':468,477,495,496,505 'columns.map':509,522 'compil':54,719 'compile-tim':53,718 'compon':12,26,40,62,65,77,453,570,741,904,945 'componentpropswithoutref':845 'confid':57 'console.log':297 'const':153,172,287,323,330,345,351,396,402,407,416,422,430,590,642,669,688,735,765,770,776,813,856 'constrain':536 'count':385 'countref':352 'cover':29 'createcontext':424 'createrout':728,737 'custom':85,390,473,920 'data':227,493,504,578,619 'data.map':516 'db.user.update':615 'default':387,582,761,808 'deprec':111,895 'dev':3 'direct':356 'discrimin':232,359,850 'dom':340,349,900 'drag':313 'e':277,284,295,302 'e.currenttarget':291 'e.currenttarget.blur':307 'e.currenttarget.disabled':279 'e.key':305 'e.preventdefault':286 'e.target.value':298 'element':192,225,848 'enter':306 'error':147,438,701 'etc':840 'event':18,42,67,267,317,836,883,925 'event-handlers.md':309,922 'examples/generic-components.md':560,936 'examples/server-components.md':696,943 'explicit':319,841 'export':581,606,796,807 'extend':190 'extens':849 'fals':401 'featur':74 'fetch':579,699 'fetchus':593,671,748,805 'file':907 'focus':312 'forget':896 'form':69,152,160,281,653,870,967 'formact':155,162,644,655 'formdata':288,290,612,613,648,651 'formfield':565 'formstat':146 'forwardref':110,867,890 'framework':788 'function':125,151,167,201,248,275,282,293,300,372,398,428,502,547,584,608,637,667,683,763,798,809 'generat':786,966 'generic':39,64,452,537,927 'generic-components.md':559,935 'guarante':56 'guard':419 'handl':17,898 'handlechang':294 'handleclick':276 'handlekeydown':301 'handler':68,884,928 'handlesubmit':283 'handoff':659 'hasid':543 'header':481 'hook':16,86,391,921 'hooks.md':445,914 'href':242,257 'icon':222 'id':544,588,617 'idl':334,336 'implement':63 'import':141,629,633,727,731,791 'includ':38 'increment':367 'infer':454 'initi':400,406 'input':292 'inputref':346 'int':759 'integr':80 'ispend':156,645 'item':488,498,517,521,530,532,534,549,550,554 'item.id':557 'items.map':553 'js':97 'jsx.element':885 'key':105,469,478,512,519,525,556 'keyboard':299 'keyextractor':497,506,520 'keyof':465,479,486 'li':555 'link':241,254,259 'list':548,563,939 'load':335 'loader':743,799,818,963 'loaderdata':811,815 'manual':459 'mention':28 'migrat':104,187,934 'mix':902 'modal':564,940 'mode':789 'mous':274 'mouseev':838 'mutabl':343,355 'mutat':602 'nativ':191,847 'navig':957 'never':880 'new':289,437 'non':93 'non-react':92 'null':328,329,338,348,418,426,427,897 'number':371,501,546 'onclick':246,262 'option':756 'outsid':440 'page':668,757,772 'parallel':698 'param':586,587,745,800,956 'params.id':594 'params.userid':749,806 'pass':662,911 'path':738 'pattern':33,106,420,464,566,826,833,876,941 'payload':370 'posit':760 'prev':647 'primari':197 'profil':754 'promis':171,658,663,687,909 'promises/context':165 'prop':109,118,129,135,189,205,211,217,231,236,250,457,471,854,862 'proper':41,88 'properti':540 'props.href':258 'props.onclick':263 'props.variant':253 'provid':710 'react':2,11,21,23,35,47,52,61,72,83,94,98,99,113,144,567,632,707,781,831,864,892,960 'react-19-patterns.md':182,929 'react-dev':1 'react-router.md':828,958 'react.changeevent':296 'react.componentpropswithoutref':123,199 'react.formevent':285 'react.keyboardevent':303 'react.mouseevent':278 'react.reactelement':223 'react.reactnode':219,229,490 'react.ref':122 'reactnod':889 'reduc':373 'ref':70,107,115,121,127,133,134,860,901 'refactor':58 'references/event-handlers.md':310,923 'references/hooks.md':446,915 'references/react-19-patterns.md':183,930 'references/react-router.md':829,959 'references/tanstack-router.md':823,950 'regular':117 'render':221,226,230,470,474,483 'replac':138 'requir':103,539 'resolv':178 'return':131,159,179,207,255,260,383,388,393,412,442,508,552,595,652,673,692,802,819,859 'revalidatepath':620 'rout':45,714,793,875,878,954 'route.componentprops':812 'route.loaderargs':801 'router':79,82,84,705,708,717,782,832,952,961 'run':571 'safe':32,51,713,874 'safeti':722 'search':955 'secondari':198 'section':879 'see':181,308,444,558,694,821,877 'select':562,938 'server':25,76,569,573,597,600,605,624,661,946 'server-components.md':695,942 'server/client':903 'set':369,382,755 'setstatus':332 'setus':325 'setvalu':404,409 'singl':224 'site':463 'skill':5 'skill-react-dev' 'solut':715 'source-softaworks' 'specif':266,835 'state':154,374,375,378,384,389,643 'status':331 'stream':700,948 'string':148,243,482,500,513,526,533,545,589,611,641 'submitact':158 'success':149 'suspend':176 'switch':379 'tab':752,771 'tabl':503,937 'tableprop':492,507 'tanstack':81,704,716,825,951 'tanstack-router.md':822,949 'tanstack/react-router':730 'target':271 'td':524 'th':511 'throw':436 'time':55,720 'toggl':408,414 'topic-agent-skills' 'topic-automation' 'topic-claude' 'topic-claude-code' 'topic-coding-agent' 'topic-development' 'touch':315 'tr':518 'true':280 'tupl':392,858 'type':15,31,43,50,60,66,89,119,145,194,214,216,238,268,272,364,366,368,455,476,491,542,712,721,785,792,816,837,873,926,953,965 'type-saf':30,49,711,872 'types/user':795 'typescript':14,22,48,95,112,140,166,193,215,237,273,322,344,363,397,421,475,541,580,603,626,660,726,790 'union':233,360,851 'unions/null':321,844 'unwrap':164,678 'updateus':609,634,649 'use':8,71,78,163,174,265,350,599,604,627,656,680,681,690,888,913,932 'useactionst':137,142,157,630,646,868,931 'usecallback':448 'usecontext':417,432,919 'useformst':139,894 'useimperativehandl':450 'useloaderdata':767 'usememo':449 'useoptimist':185,933 'useparam':778 'user':173,324,327,425,431,435,443,591,689,746,766,803,814 'user.name':180,596,693,820 'usercontext':423,433 'usereduc':358,918 'useref':337,347,353,917 'userform':638 'userid':610,618,622,639,640,650,740,777 'userpag':585,742,764,810 'userprofil':168,674,684 'userpromis':169,170,175,670,675,676,685,686,691 'userprovid':441 'userrout':736 'userroute.id':769,775,780 'usesearch':773 'usest':318,326,333,405,842,916 'usesyncexternalstor':451 'usetoggl':399 'usetransit':186 'useus':429,439 'v':410,411 'v7':709,783,962 'valid':725 'validatesearch':750 'valu':341,403,413,484 'vanilla':96 'variant':196,203,210,235,240,244,853 'void':247 'wheel':316 'without':664 'z':732 'z.enum':753 'z.number':758 'z.object':751 'zod':724,734","prices":[{"id":"ff27fed9-162c-42be-8c65-fa7391cd04ed","listingId":"f8cad97a-18d0-4bf7-8421-bc7848dfba18","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"softaworks","category":"agent-toolkit","install_from":"skills.sh"},"createdAt":"2026-04-18T20:27:58.504Z"}],"sources":[{"listingId":"f8cad97a-18d0-4bf7-8421-bc7848dfba18","source":"github","sourceId":"softaworks/agent-toolkit/react-dev","sourceUrl":"https://github.com/softaworks/agent-toolkit/tree/main/skills/react-dev","isPrimary":false,"firstSeenAt":"2026-04-18T21:54:43.691Z","lastSeenAt":"2026-05-18T18:52:58.841Z"},{"listingId":"f8cad97a-18d0-4bf7-8421-bc7848dfba18","source":"skills_sh","sourceId":"softaworks/agent-toolkit/react-dev","sourceUrl":"https://skills.sh/softaworks/agent-toolkit/react-dev","isPrimary":true,"firstSeenAt":"2026-04-18T20:27:58.504Z","lastSeenAt":"2026-05-07T22:40:51.974Z"}],"details":{"listingId":"f8cad97a-18d0-4bf7-8421-bc7848dfba18","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"softaworks","slug":"react-dev","github":{"repo":"softaworks/agent-toolkit","stars":1847,"topics":["agent-skills","ai","automation","claude","claude-code","coding-agent","development"],"license":"mit","html_url":"https://github.com/softaworks/agent-toolkit","pushed_at":"2026-03-05T16:46:24Z","description":"A curated collection of skills for AI coding agents. Skills are packaged instructions and scripts that extend agent capabilities across development, documentation, planning, and professional workflows.","skill_md_sha":"bd288f334a6d818dcb3394dfa53225e4a31ec5ec","skill_md_path":"skills/react-dev/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/softaworks/agent-toolkit/tree/main/skills/react-dev"},"layout":"multi","source":"github","category":"agent-toolkit","frontmatter":{"name":"react-dev","description":"This skill should be used when building React components with TypeScript, typing hooks, handling events, or when React TypeScript, React 19, Server Components are mentioned. Covers type-safe patterns for React 18-19 including generic components, proper event typing, and routing integration (TanStack Router, React Router)."},"skills_sh_url":"https://skills.sh/softaworks/agent-toolkit/react-dev"},"updatedAt":"2026-05-18T18:52:58.841Z"}}