{"id":"f7c46514-d2eb-49e9-8d2f-54fa4329ff19","shortId":"2ZJQNz","kind":"skill","title":"react-frontend","tagline":">-","description":"# React Frontend\n\n**Verify before implementing**: For App Router patterns, React 19 APIs, or version-specific behavior, search current docs via `search_docs` before writing code. Training data may lag current releases.\n\n## Component TypeScript\n\n- Extend native elements with `ComponentPropsWithoutRef<'button'>`, add custom props via intersection\n- Use `React.ReactNode` for children, `React.ReactElement` for single element, render prop `(data: T) => ReactNode`\n- Discriminated unions for variant props -- TypeScript narrows automatically in branches\n- Generic components: `<T>` with `keyof T` for column keys, `T extends { id: string }` for constraints\n- Event types: `React.MouseEvent<HTMLButtonElement>`, `FormEvent<HTMLFormElement>`, `ChangeEvent<HTMLInputElement>`\n- `as const` for custom hook tuple returns\n- `useRef<HTMLInputElement>(null)` for DOM (use `?.`), `useRef<number>(0)` for mutable values\n- Explicit `useState<User | null>(null)` for unions/null\n- useReducer actions as discriminated unions: `{ type: 'set'; payload: number } | { type: 'reset' }`\n- useContext null guard: throw in custom `useX()` hook if context is null\n\n## Effects Decision Tree\n\nEffects are escape hatches -- most logic should NOT use effects.\n\n| Need | Solution |\n|------|----------|\n| Derived value from props/state | Calculate during render (useMemo if expensive) |\n| Reset state on prop change | `key` prop on component |\n| Respond to user event | Event handler |\n| Notify parent of state change | Call onChange in event handler, or fully controlled component |\n| Chain of state updates | Calculate all next state in one event handler |\n| Sync with external system | Effect with cleanup |\n\n**Effect rules:**\n- Never suppress the linter -- fix the code instead\n- Use updater functions (`setItems(prev => [...prev, item])`) to remove state dependencies\n- Move objects/functions inside effects to stabilize dependencies\n- `useEffectEvent` for non-reactive values (e.g., theme in a connection effect)\n- Always return cleanup for subscriptions, connections, listeners\n- Data fetching cancellation (pick by situation): `AbortController` for fetch; `ignore` flag for non-cancellable promises; React Query handles both automatically\n\n## Concurrency & Race Classes\n\nFrontend bugs that survive type-checking and unit tests usually land in one of five race classes. Hunt each one explicitly during review:\n\n1. **Lifecycle cleanup gaps** -- in-production signal: \"Can't perform state update on an unmounted component\" warnings, slow-burn memory leaks under rapid navigation, duplicate event handlers firing. Root cause: `useEffect` registered a listener/timer/observer without returning cleanup (see Effect rules above for the rule).\n2. **Remount-timing mistakes** -- async callbacks mutate DOM or state after swap / disconnect / route change. Classic cases: a `fetch().then(setData)` resolves after navigation to a different route; a `requestAnimationFrame` fires after the parent unmounts. See \"Data fetching\" in Effect rules above for the cancellation hierarchy.\n3. **Boolean-as-state for UI that isn't binary** -- `isLoading: boolean` can't represent `idle | loading | success | error | retry` without creating inconsistent combinations (`isLoading: true, error: Error` is contradictory). Prefer an explicit state constant (`'idle' | 'loading' | 'success' | 'error'`) with a transition function so invalid states are unreachable.\n4. **Stale promises and timers with no cancel path** -- a promise chain or `setTimeout` holds a reference to `setState` after the component's moved on. Bind every async operation to a cancel mechanism per the cancellation hierarchy above, and verify the cleanup path is exercised by a test.\n5. **Per-element handlers where delegation would be safer** -- attaching `onClick` to every row in a list creates N closures and N subscriptions; delegated listeners (single handler on the parent reading `event.target.closest(...)`) are safer under rapid re-renders, avoid stale-closure bugs, and scale to large lists. Use delegation when the list exceeds ~50 items or updates frequently.\n\nThese classes produce bugs that are intermittent, environment-dependent, and invisible to type-checking -- exactly the ones that reach production. Review for them deliberately, not just as \"subscriptions need cleanup.\"\n\n## State Management\n\n```\nLocal UI state       → useState, useReducer\nShared client state  → Zustand (simple) | Redux Toolkit (complex)\nAtomic/granular      → Jotai\nServer/remote data   → React Query (TanStack Query)\nURL state            → nuqs, router search params\nForm state           → React Hook Form\n```\n\n**Key patterns:**\n- Zustand: `create<State>()(devtools(persist((set) => ({...}))))` -- use slices for scale, selective subscriptions to prevent re-renders\n- React Query: query keys factory (`['users', 'detail', id] as const`), `staleTime`/`gcTime`, optimistic updates with `onMutate`/`onError` rollback\n- Separate client state (Zustand) from server state (React Query) -- never duplicate server data in client store\n- Colocate state close to where it's used; don't over-globalize\n\n## Performance\n\n**Critical -- eliminate waterfalls:**\n- `Promise.all()` for independent async operations\n- Move `await` into branches where actually needed\n- Suspense boundaries to stream slow content\n\n**Critical -- bundle size:**\n- Import directly from modules, avoid barrel files (`index.ts` re-exports)\n- `next/dynamic` or `React.lazy()` for heavy components\n- Defer third-party scripts (analytics, logging) until after hydration\n- Preload on hover/focus for perceived speed\n- `content-visibility: auto` + `contain-intrinsic-size` on long lists -- skips off-screen layout/paint\n\n**Re-render optimization:**\n- Derive state during render, not in effects\n- Subscribe to derived booleans, not raw objects (`state.items.length > 0` not `state.items`)\n- Functional setState for stable callbacks: `setCount(c => c + 1)`\n- Lazy state init: `useState(() => expensiveComputation())`\n- `useTransition` for non-urgent updates (search filtering)\n- `useDeferredValue` for expensive derived UI\n- Don't subscribe to searchParams/state if only read in callbacks -- read on demand instead\n- Use ternary (`condition ? <A /> : <B />`), not `&&` for conditionals\n- `React.memo` only for expensive subtrees with stable props\n- Hoist static JSX outside components\n\n**React Compiler** (React 19): auto-memoizes -- write idiomatic React, remove manual `useMemo`/`useCallback`/`memo`. Enable via framework config (Next.js: `reactCompiler: true` in next.config). Non-framework: install `babel-plugin-react-compiler`. Keep components pure.\n\n## React 19\n\n- **ref as prop** -- `forwardRef` deprecated. Accept `ref?: React.Ref<HTMLElement>` as regular prop\n- **useActionState** -- replaces `useFormState`: `const [state, formAction, isPending] = useActionState(action, initialState)`\n- **use()** -- unwrap Promise or Context during render (not in callbacks/effects). Enables conditional context reads\n- **useOptimistic** -- `const [optimistic, addOptimistic] = useOptimistic(state, mergeFn)` for instant UI feedback\n- **useFormStatus** -- `const { pending } = useFormStatus()` in child of `<form action={...}>`\n- **Server Components** -- default in App Router. Async, access DB/secrets directly. No hooks, no event handlers\n- **Server Actions** -- `'use server'` directive. Validate inputs (Zod), `revalidateTag`/`revalidatePath` after mutations. **Server Actions are public endpoints** -- always verify auth/authz inside each action, not just in middleware or layout guards\n- **`<Activity mode='visible'|'hidden'>`** -- preserves state/DOM for toggled components (experimental)\n\n## Next.js App Router\n\n**File conventions:** `page.tsx` (route UI), `layout.tsx` (shared wrapper), `template.tsx` (re-mounted on navigation, unlike layout), `loading.tsx` (Suspense), `error.tsx` (error boundary), `not-found.tsx` (404), `default.tsx` (parallel route fallback), `route.ts` (API endpoint)\n\n**Rendering modes:** Server Components (default) | Client (`'use client'`) | Static (build) | Dynamic (request) | Streaming (progressive)\n\n**Decision:** Server Component unless it needs hooks, event handlers, or browser APIs. Split: server parent + client child. Isolate interactive components as `'use client'` leaf components -- keep server components static with no global state or event handlers.\n\n**Routing patterns:**\n- Route groups `(name)` -- organize without affecting URL\n- Parallel routes `@slot` -- independent loading states in same layout\n- Intercepting routes `(.)` -- modal overlays with full-page fallback\n\n**Caching:**\n- `fetch(url, { cache: 'force-cache' })` -- static\n- `fetch(url, { next: { revalidate: 60 } })` -- ISR\n- `fetch(url, { cache: 'no-store' })` -- dynamic\n- Tag-based: `fetch(url, { next: { tags: ['products'] } })` then `revalidateTag('products')`\n\n**Data fetching:** Fetch in Server Components where data is used. Use Suspense boundaries for slow queries. `React.cache()` for per-request dedup. `generateStaticParams` for static generation. `generateMetadata` for dynamic SEO. Static metadata with `title: { default: 'App', template: '%s | App' }` for cascading page titles. `after()` for non-blocking side effects (logging, analytics) -- runs after response is sent. Hoist static I/O (fonts, config) to module level -- runs once, not per request.\n\n## Testing (Vitest + React Testing Library)\n\n- **Component tests**: Vitest + RTL, co-located `*.test.tsx`. Default for React components.\n- **Hook tests**: `renderHook` + `act`, co-located `*.test.ts`\n- **Unit tests**: Vitest for pure functions, utilities, services\n- **E2E**: Playwright for user flows and critical paths\n- **Query priority**: `getByRole` > `getByLabelText` > `getByPlaceholderText` > `getByText` > `getByTestId`\n- Mock API services and external providers; render child components real for integration confidence\n- One behavior per test with AAA structure. Name: `should <behavior> when <condition>`\n- Use `userEvent` over `fireEvent` for realistic interactions\n- `findBy*` for async elements, `waitFor` after state-triggering actions\n- `vi.clearAllMocks()` in `beforeEach`. Recreate state per test.\nGeneral testing discipline (anti-patterns, rationalization resistance): see [ia-writing-tests](../writing-tests/SKILL.md) skill.\nSee [testing patterns and examples](./references/testing.md) for component, hook, and mocking examples.\nSee [e2e testing](./references/e2e-testing.md) for Playwright patterns.\n\n## Tailwind Integration\n\nFor Tailwind v4 configuration, utility patterns, dark mode, and component variants, see [ia-tailwind-css](../tailwind-css/SKILL.md) skill.\n\n**Class sorting in JSX**: when using `clsx`, `cva`, `cn`, `tv`, or `tw` utility functions, keep Tailwind classes in canonical order. Configure `eslint-plugin-better-tailwindcss` with `useSortedClasses` and `functions: [\"clsx\", \"cva\", \"cn\", \"tv\", \"tw\"]` to enforce this automatically across JSX attributes and helper calls.\n\n## Discipline\n\n- Simplicity first -- every change as simple as possible, impact minimal code\n- Only touch what's necessary -- avoid introducing unrelated changes\n- No hacky workarounds -- if a fix feels wrong, step back and implement the clean solution\n- Before adding a new abstraction, verify it appears in 3+ places\n\n## References\n\n- [testing.md](./references/testing.md) -- Component, hook, and mocking test examples\n- [e2e-testing.md](./references/e2e-testing.md) -- Playwright E2E patterns\n\n## Verify\n\n- TypeScript compiles with zero errors\n- No suppressed lint rules (`eslint-disable`, `@ts-ignore`) in new code\n- `useEffect` dependency arrays not manually overridden\n- No `forwardRef` usage in React 19+ projects (use `ref` prop directly)","tags":["react","frontend","skills","iliaal","agent-skills","ai-coding-assistant","ai-tools","claude-code"],"capabilities":["skill","source-iliaal","skill-react-frontend","topic-agent-skills","topic-ai-coding-assistant","topic-ai-tools","topic-claude-code","topic-skills"],"categories":["ai-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/iliaal/ai-skills/react-frontend","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add iliaal/ai-skills","source_repo":"https://github.com/iliaal/ai-skills","install_from":"skills.sh"}},"qualityScore":"0.456","qualityRationale":"deterministic score 0.46 from registry signals: · indexed on github topic:agent-skills · 13 github stars · SKILL.md body (11,703 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-18T19:07:03.425Z","embedding":null,"createdAt":"2026-05-07T20:45:53.271Z","updatedAt":"2026-05-18T19:07:03.425Z","lastSeenAt":"2026-05-18T19:07:03.425Z","tsv":"'/references/e2e-testing.md':1329,1455 '/references/testing.md':1319,1447 '/tailwind-css/skill.md':1351 '/writing-tests/skill.md':1312 '0':104,781 '1':306,792 '19':14,847,881,1489 '2':352 '3':399,1443 '4':448 '404':1017 '5':496 '50':552 '60':1114 'aaa':1270 'abortcontrol':264 'abstract':1438 'accept':887 'access':944 'across':1392 'act':1224 'action':116,901,936,953,965,974,1291 'activ':982 'actual':702 'ad':1435 'add':44 'addoptimist':920 'affect':1082 'alway':251,969 'analyt':735,1185 'anti':1303 'anti-pattern':1302 'api':15,1023,1050,1253 'app':10,941,993,1169,1172 'appear':1441 'array':1480 'async':357,475,695,943,1284 'atomic/granular':604 'attach':506 'attribut':1394 'auth/authz':971 'auto':749,849 'auto-memo':848 'automat':69,278,1391 'avoid':536,717,1415 'await':698 'babel':873 'babel-plugin-react-compil':872 'back':1428 'barrel':718 'base':1125 'beforeeach':1294 'behavior':20,1266 'better':1377 'binari':409 'bind':473 'block':1181 'boolean':401,411,776 'boolean-as-st':400 'boundari':705,1015,1146 'branch':71,700 'browser':1049 'bug':283,540,560 'build':1034 'bundl':711 'burn':326 'button':43 'c':790,791 'cach':1102,1105,1108,1118 'calcul':157,196 'call':183,1397 'callback':358,788,820 'callbacks/effects':912 'cancel':260,272,397,455,479,483 'canon':1371 'cascad':1174 'case':369 'caus':337 'chain':192,459 'chang':167,182,367,1402,1418 'changeev':90 'check':288,572 'child':933,1055,1259 'children':52 'class':281,299,558,1353,1369 'classic':368 'clean':1432 'cleanup':210,253,308,344,489,588 'client':597,660,673,1030,1032,1054,1061 'close':677 'closur':516,539 'clsx':1359,1383 'cn':1361,1385 'co':1214,1226 'co-loc':1213,1225 'code':29,219,1409,1477 'coloc':675 'column':78 'combin':423 'compil':845,876,1461 'complex':603 'compon':36,73,171,191,322,469,729,843,878,938,990,1028,1041,1058,1063,1066,1139,1209,1220,1260,1321,1344,1448 'componentpropswithoutref':42 'concurr':279 'condit':827,830,914 'confid':1264 'config':862,1195 'configur':1338,1373 'connect':249,256 'const':92,650,896,918,929 'constant':434 'constraint':85 'contain':751 'contain-intrinsic-s':750 'content':709,747 'content-vis':746 'context':135,907,915 'contradictori':429 'control':190 'convent':996 'creat':421,514,626 'critic':689,710,1243 'css':1350 'current':22,34 'custom':45,94,131 'cva':1360,1384 'dark':1341 'data':31,59,258,389,607,671,1134,1141 'db/secrets':945 'decis':139,1039 'dedup':1155 'default':939,1029,1168,1217 'default.tsx':1018 'defer':730 'deleg':502,520,547 'deliber':582 'demand':823 'depend':231,238,566,1479 'deprec':886 'deriv':153,766,775,809 'detail':647 'devtool':627 'differ':379 'direct':714,946,956,1494 'disabl':1471 'disciplin':1301,1398 'disconnect':365 'discrimin':62,118 'doc':23,26 'dom':101,360 'duplic':332,669 'dynam':1035,1122,1162 'e.g':245 'e2e':1237,1327,1457 'e2e-testing.md':1454 'effect':138,141,150,208,211,235,250,346,392,772,1183 'element':40,56,499,1285 'elimin':690 'enabl':859,913 'endpoint':968,1024 'enforc':1389 'environ':565 'environment-depend':564 'error':418,426,427,438,1014,1464 'error.tsx':1013 'escap':143 'eslint':1375,1470 'eslint-dis':1469 'eslint-plugin-better-tailwindcss':1374 'event':86,175,176,186,202,333,950,1046,1073 'event.target.closest':528 'everi':474,509,1401 'exact':573 'exampl':1318,1325,1453 'exceed':551 'exercis':492 'expens':162,808,834 'expensivecomput':797 'experiment':991 'explicit':108,303,432 'export':723 'extend':38,81 'extern':206,1256 'factori':645 'fallback':1021,1101 'feedback':927 'feel':1425 'fetch':259,266,371,390,1103,1110,1116,1126,1135,1136 'file':719,995 'filter':805 'findbi':1282 'fire':335,383 'fireev':1278 'first':1400 'five':297 'fix':217,1424 'flag':268 'flow':1241 'font':1194 'forc':1107 'force-cach':1106 'form':618,622,935 'formact':898 'formev':89 'forwardref':885,1485 'framework':861,870 'frequent':556 'frontend':3,5,282 'full':1099 'full-pag':1098 'fulli':189 'function':223,442,784,1234,1366,1382 'gap':309 'gctime':652 'general':1299 'generat':1159 'generatemetadata':1160 'generatestaticparam':1156 'generic':72 'getbylabeltext':1248 'getbyplaceholdertext':1249 'getbyrol':1247 'getbytestid':1251 'getbytext':1250 'global':687,1070 'group':1078 'guard':128,981 'hacki':1420 'handl':276 'handler':177,187,203,334,500,523,951,1047,1074 'hatch':144 'heavi':728 'helper':1396 'hidden':985 'hierarchi':398,484 'hoist':839,1191 'hold':462 'hook':95,133,621,948,1045,1221,1322,1449 'hover/focus':742 'hunt':300 'hydrat':739 'i/o':1193 'ia':1309,1348 'ia-tailwind-css':1347 'ia-writing-test':1308 'id':82,648 'idiomat':852 'idl':415,435 'ignor':267,1474 'impact':1407 'implement':8,1430 'import':713 'in-product':310 'inconsist':422 'independ':694,1087 'index.ts':720 'init':795 'initialst':902 'input':958 'insid':234,972 'instal':871 'instant':925 'instead':220,824 'integr':1263,1334 'interact':1057,1281 'intercept':1093 'intermitt':563 'intersect':48 'intrins':752 'introduc':1416 'invalid':444 'invis':568 'isload':410,424 'isn':407 'isol':1056 'ispend':899 'isr':1115 'item':227,553 'jotai':605 'jsx':841,1356,1393 'keep':877,1064,1367 'key':79,168,623,644 'keyof':75 'lag':33 'land':293 'larg':544 'layout':980,1010,1092 'layout.tsx':1000 'layout/paint':761 'lazi':793 'leaf':1062 'leak':328 'level':1198 'librari':1208 'lifecycl':307 'lint':1467 'linter':216 'list':513,545,550,756 'listen':257,521 'listener/timer/observer':341 'load':416,436,1088 'loading.tsx':1011 'local':591 'locat':1215,1227 'log':736,1184 'logic':146 'long':755 'manag':590 'manual':855,1482 'may':32 'mechan':480 'memo':858 'memoiz':850 'memori':327 'mergefn':923 'metadata':1165 'middlewar':978 'minim':1408 'mistak':356 'mock':1252,1324,1451 'modal':1095 'mode':983,1026,1342 'modul':716,1197 'mount':1006 'move':232,471,697 'mutabl':106 'mutat':359,963 'n':515,518 'name':1079,1272 'narrow':68 'nativ':39 'navig':331,376,1008 'necessari':1414 'need':151,587,703,1044 'never':213,668 'new':1437,1476 'next':198,1112,1128 'next.config':867 'next.js':863,992 'next/dynamic':724 'no-stor':1119 'non':242,271,801,869,1180 'non-block':1179 'non-cancel':270 'non-framework':868 'non-react':241 'non-urg':800 'not-found.tsx':1016 'notifi':178 'null':99,111,112,127,137 'number':123 'nuq':614 'object':779 'objects/functions':233 'off-screen':758 'onchang':184 'onclick':507 'one':201,295,302,575,1265 'onerror':657 'onmut':656 'oper':476,696 'optim':765 'optimist':653,919 'order':1372 'organ':1080 'outsid':842 'over-glob':685 'overlay':1096 'overridden':1483 'page':1100,1175 'page.tsx':997 'parallel':1019,1084 'param':617 'parent':179,386,526,1053 'parti':733 'path':456,490,1244 'pattern':12,624,1076,1304,1316,1332,1340,1458 'payload':122 'pend':930 'per':481,498,1153,1202,1267,1297 'per-el':497 'per-request':1152 'perceiv':744 'perform':316,688 'persist':628 'pick':261 'place':1444 'playwright':1238,1331,1456 'plugin':874,1376 'possibl':1406 'prefer':430 'preload':740 'preserv':986 'prev':225,226 'prevent':637 'prioriti':1246 'produc':559 'product':312,578,1130,1133 'progress':1038 'project':1490 'promis':273,450,458,905 'promise.all':692 'prop':46,58,66,166,169,838,884,892,1493 'props/state':156 'provid':1257 'public':967 'pure':879,1233 'queri':275,609,611,642,643,667,1149,1245 'race':280,298 'rapid':330,532 'ration':1305 'raw':778 're':534,639,722,763,1005 're-export':721 're-mount':1004 're-rend':533,638,762 'reach':577 'react':2,4,13,274,608,620,641,666,844,846,853,875,880,1206,1219,1488 'react-frontend':1 'react.cache':1150 'react.lazy':726 'react.memo':831 'react.mouseevent':88 'react.reactelement':53 'react.reactnode':50 'react.ref':889 'reactcompil':864 'reactiv':243 'reactnod':61 'read':527,818,821,916 'real':1261 'realist':1280 'recreat':1295 'redux':601 'ref':882,888,1492 'refer':464,1445 'regist':339 'regular':891 'releas':35 'remount':354 'remount-tim':353 'remov':229,854 'render':57,159,535,640,764,769,909,1025,1258 'renderhook':1223 'replac':894 'repres':414 'request':1036,1154,1203 'requestanimationfram':382 'reset':125,163 'resist':1306 'resolv':374 'respond':172 'respons':1188 'retri':419 'return':97,252,343 'revalid':1113 'revalidatepath':961 'revalidatetag':960,1132 'review':305,579 'rollback':658 'root':336 'rout':366,380,998,1020,1075,1077,1085,1094 'route.ts':1022 'router':11,615,942,994 'row':510 'rtl':1212 'rule':212,347,351,393,1468 'run':1186,1199 'safer':505,530 'scale':542,633 'screen':760 'script':734 'search':21,25,616,804 'searchparams/state':815 'see':345,388,1307,1314,1326,1346 'select':634 'sent':1190 'seo':1163 'separ':659 'server':664,670,937,952,955,964,1027,1040,1052,1065,1138 'server/remote':606 'servic':1236,1254 'set':121,629 'setcount':789 'setdata':373 'setitem':224 'setstat':466,785 'settimeout':461 'share':596,1001 'side':1182 'signal':313 'simpl':600,1404 'simplic':1399 'singl':55,522 'situat':263 'size':712,753 'skill':1313,1352 'skill-react-frontend' 'skip':757 'slice':631 'slot':1086 'slow':325,708,1148 'slow-burn':324 'solut':152,1433 'sort':1354 'source-iliaal' 'specif':19 'speed':745 'split':1051 'stabil':237 'stabl':787,837 'stale':449,538 'stale-closur':537 'staletim':651 'state':164,181,194,199,230,317,362,403,433,445,589,593,598,613,619,661,665,676,767,794,897,922,1071,1089,1289,1296 'state-trigg':1288 'state.items':783 'state.items.length':780 'state/dom':987 'static':840,1033,1067,1109,1158,1164,1192 'step':1427 'store':674,1121 'stream':707,1037 'string':83 'structur':1271 'subscrib':773,813 'subscript':255,519,586,635 'subtre':835 'success':417,437 'suppress':214,1466 'surviv':285 'suspens':704,1012,1145 'swap':364 'sync':204 'system':207 'tag':1124,1129 'tag-bas':1123 'tailwind':1333,1336,1349,1368 'tailwindcss':1378 'tanstack':610 'templat':1170 'template.tsx':1003 'ternari':826 'test':291,495,1204,1207,1210,1222,1230,1268,1298,1300,1311,1315,1328,1452 'test.ts':1228 'test.tsx':1216 'testing.md':1446 'theme':246 'third':732 'third-parti':731 'throw':129 'time':355 'timer':452 'titl':1167,1176 'toggl':989 'toolkit':602 'topic-agent-skills' 'topic-ai-coding-assistant' 'topic-ai-tools' 'topic-claude-code' 'topic-skills' 'touch':1411 'train':30 'transit':441 'tree':140 'trigger':1290 'true':425,865 'ts':1473 'ts-ignor':1472 'tupl':96 'tv':1362,1386 'tw':1364,1387 'type':87,120,124,287,571 'type-check':286,570 'typescript':37,67,1460 'ui':405,592,810,926,999 'union':63,119 'unions/null':114 'unit':290,1229 'unless':1042 'unlik':1009 'unmount':321,387 'unreach':447 'unrel':1417 'unwrap':904 'updat':195,222,318,555,654,803 'urgent':802 'url':612,1083,1104,1111,1117,1127 'usag':1486 'use':49,102,149,221,546,630,682,825,903,954,1031,1060,1143,1144,1275,1358,1491 'useactionst':893,900 'usecallback':857 'usecontext':126 'usedeferredvalu':806 'useeffect':338,1478 'useeffectev':239 'useformst':895 'useformstatus':928,931 'usememo':160,856 'useoptimist':917,921 'user':110,174,646,1240 'usereduc':115,595 'useref':98,103 'userev':1276 'usesortedclass':1380 'usest':109,594,796 'usetransit':798 'usex':132 'usual':292 'util':1235,1339,1365 'v4':1337 'valid':957 'valu':107,154,244 'variant':65,1345 'verifi':6,487,970,1439,1459 'version':18 'version-specif':17 'vi.clearallmocks':1292 'via':24,47,860 'visibl':748,984 'vitest':1205,1211,1231 'waitfor':1286 'warn':323 'waterfal':691 'without':342,420,1081 'workaround':1421 'would':503 'wrapper':1002 'write':28,851,1310 'wrong':1426 'zero':1463 'zod':959 'zustand':599,625,662","prices":[{"id":"8581106e-d266-4147-afcc-416a73893876","listingId":"f7c46514-d2eb-49e9-8d2f-54fa4329ff19","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"iliaal","category":"ai-skills","install_from":"skills.sh"},"createdAt":"2026-05-07T20:45:53.271Z"}],"sources":[{"listingId":"f7c46514-d2eb-49e9-8d2f-54fa4329ff19","source":"github","sourceId":"iliaal/ai-skills/react-frontend","sourceUrl":"https://github.com/iliaal/ai-skills/tree/master/skills/react-frontend","isPrimary":false,"firstSeenAt":"2026-05-09T01:05:36.467Z","lastSeenAt":"2026-05-18T19:07:03.425Z"},{"listingId":"f7c46514-d2eb-49e9-8d2f-54fa4329ff19","source":"skills_sh","sourceId":"iliaal/ai-skills/react-frontend","sourceUrl":"https://skills.sh/iliaal/ai-skills/react-frontend","isPrimary":true,"firstSeenAt":"2026-05-07T20:45:53.271Z","lastSeenAt":"2026-05-07T22:43:39.302Z"}],"details":{"listingId":"f7c46514-d2eb-49e9-8d2f-54fa4329ff19","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"iliaal","slug":"react-frontend","github":{"repo":"iliaal/ai-skills","stars":13,"topics":["agent-skills","ai-coding-assistant","ai-tools","claude-code","skills"],"license":"mit","html_url":"https://github.com/iliaal/ai-skills","pushed_at":"2026-05-16T13:15:17Z","description":"Curated collection of agent skills for AI coding assistants.","skill_md_sha":"f6e41ac2de33f55de45a2288cf77bc6452d40d18","skill_md_path":"skills/react-frontend/SKILL.md","default_branch":"master","skill_tree_url":"https://github.com/iliaal/ai-skills/tree/master/skills/react-frontend"},"layout":"multi","source":"github","category":"ai-skills","frontmatter":{"name":"react-frontend","description":">-"},"skills_sh_url":"https://skills.sh/iliaal/ai-skills/react-frontend"},"updatedAt":"2026-05-18T19:07:03.425Z"}}