{"id":"e9ecbcaa-3178-46ed-af4a-c98b526ca1ed","shortId":"rZ2qHA","kind":"skill","title":"React Ui Patterns","tagline":"Antigravity Awesome Skills skill by Sickn33","description":"# React UI Patterns\n\n## Core Principles\n\n1. **Never show stale UI** - Loading spinners only when actually loading\n2. **Always surface errors** - Users must know when something fails\n3. **Optimistic updates** - Make the UI feel instant\n4. **Progressive disclosure** - Show content as it becomes available\n5. **Graceful degradation** - Partial data is better than no data\n\n## Loading State Patterns\n\n### The Golden Rule\n\n**Show loading indicator ONLY when there's no data to display.**\n\n```typescript\n// CORRECT - Only show loading when no data exists\nconst { data, loading, error } = useGetItemsQuery();\n\nif (error) return <ErrorState error={error} onRetry={refetch} />;\nif (loading && !data) return <LoadingState />;\nif (!data?.items.length) return <EmptyState />;\n\nreturn <ItemList items={data.items} />;\n```\n\n```typescript\n// WRONG - Shows spinner even when we have cached data\nif (loading) return <LoadingState />; // Flashes on refetch!\n```\n\n### Loading State Decision Tree\n\n```\nIs there an error?\n  → Yes: Show error state with retry option\n  → No: Continue\n\nIs it loading AND we have no data?\n  → Yes: Show loading indicator (spinner/skeleton)\n  → No: Continue\n\nDo we have data?\n  → Yes, with items: Show the data\n  → Yes, but empty: Show empty state\n  → No: Show loading (fallback)\n```\n\n### Skeleton vs Spinner\n\n| Use Skeleton When | Use Spinner When |\n|-------------------|------------------|\n| Known content shape | Unknown content shape |\n| List/card layouts | Modal actions |\n| Initial page load | Button submissions |\n| Content placeholders | Inline operations |\n\n## Error Handling Patterns\n\n### The Error Handling Hierarchy\n\n```\n1. Inline error (field-level) → Form validation errors\n2. Toast notification → Recoverable errors, user can retry\n3. Error banner → Page-level errors, data still partially usable\n4. Full error screen → Unrecoverable, needs user action\n```\n\n### Always Show Errors\n\n**CRITICAL: Never swallow errors silently.**\n\n```typescript\n// CORRECT - Error always surfaced to user\nconst [createItem, { loading }] = useCreateItemMutation({\n  onCompleted: () => {\n    toast.success({ title: 'Item created' });\n  },\n  onError: (error) => {\n    console.error('createItem failed:', error);\n    toast.error({ title: 'Failed to create item' });\n  },\n});\n\n// WRONG - Error silently caught, user has no idea\nconst [createItem] = useCreateItemMutation({\n  onError: (error) => {\n    console.error(error); // User sees nothing!\n  },\n});\n```\n\n### Error State Component Pattern\n\n```typescript\ninterface ErrorStateProps {\n  error: Error;\n  onRetry?: () => void;\n  title?: string;\n}\n\nconst ErrorState = ({ error, onRetry, title }: ErrorStateProps) => (\n  <div className=\"error-state\">\n    <Icon name=\"exclamation-circle\" />\n    <h3>{title ?? 'Something went wrong'}</h3>\n    <p>{error.message}</p>\n    {onRetry && (\n      <Button onClick={onRetry}>Try Again</Button>\n    )}\n  </div>\n);\n```\n\n## Button State Patterns\n\n### Button Loading State\n\n```tsx\n<Button\n  onClick={handleSubmit}\n  isLoading={isSubmitting}\n  disabled={!isValid || isSubmitting}\n>\n  Submit\n</Button>\n```\n\n### Disable During Operations\n\n**CRITICAL: Always disable triggers during async operations.**\n\n```tsx\n// CORRECT - Button disabled while loading\n<Button\n  disabled={isSubmitting}\n  isLoading={isSubmitting}\n  onClick={handleSubmit}\n>\n  Submit\n</Button>\n\n// WRONG - User can tap multiple times\n<Button onClick={handleSubmit}>\n  {isSubmitting ? 'Submitting...' : 'Submit'}\n</Button>\n```\n\n## Empty States\n\n### Empty State Requirements\n\nEvery list/collection MUST have an empty state:\n\n```tsx\n// WRONG - No empty state\nreturn <FlatList data={items} />;\n\n// CORRECT - Explicit empty state\nreturn (\n  <FlatList\n    data={items}\n    ListEmptyComponent={<EmptyState />}\n  />\n);\n```\n\n### Contextual Empty States\n\n```tsx\n// Search with no results\n<EmptyState\n  icon=\"search\"\n  title=\"No results found\"\n  description=\"Try different search terms\"\n/>\n\n// List with no items yet\n<EmptyState\n  icon=\"plus-circle\"\n  title=\"No items yet\"\n  description=\"Create your first item\"\n  action={{ label: 'Create Item', onClick: handleCreate }}\n/>\n```\n\n## Form Submission Pattern\n\n```tsx\nconst MyForm = () => {\n  const [submit, { loading }] = useSubmitMutation({\n    onCompleted: handleSuccess,\n    onError: handleError,\n  });\n\n  const handleSubmit = async () => {\n    if (!isValid) {\n      toast.error({ title: 'Please fix errors' });\n      return;\n    }\n    await submit({ variables: { input: values } });\n  };\n\n  return (\n    <form>\n      <Input\n        value={values.name}\n        onChange={handleChange('name')}\n        error={touched.name ? errors.name : undefined}\n      />\n      <Button\n        type=\"submit\"\n        onClick={handleSubmit}\n        disabled={!isValid || loading}\n        isLoading={loading}\n      >\n        Submit\n      </Button>\n    </form>\n  );\n};\n```\n\n## Anti-Patterns\n\n### Loading States\n\n```typescript\n// WRONG - Spinner when data exists (causes flash)\nif (loading) return <Spinner />;\n\n// CORRECT - Only show loading without data\nif (loading && !data) return <Spinner />;\n```\n\n### Error Handling\n\n```typescript\n// WRONG - Error swallowed\ntry {\n  await mutation();\n} catch (e) {\n  console.log(e); // User has no idea!\n}\n\n// CORRECT - Error surfaced\nonError: (error) => {\n  console.error('operation failed:', error);\n  toast.error({ title: 'Operation failed' });\n}\n```\n\n### Button States\n\n```typescript\n// WRONG - Button not disabled during submission\n<Button onClick={submit}>Submit</Button>\n\n// CORRECT - Disabled and shows loading\n<Button onClick={submit} disabled={loading} isLoading={loading}>\n  Submit\n</Button>\n```\n\n## Checklist\n\nBefore completing any UI component:\n\n**UI States:**\n- [ ] Error state handled and shown to user\n- [ ] Loading state shown only when no data exists\n- [ ] Empty state provided for collections\n- [ ] Buttons disabled during async operations\n- [ ] Buttons show loading indicator when appropriate\n\n**Data & Mutations:**\n- [ ] Mutations have onError handler\n- [ ] All user actions have feedback (toast/visual)\n\n## Integration with Other Skills\n\n- **graphql-schema**: Use mutation patterns with proper error handling\n- **testing-patterns**: Test all UI states (loading, error, empty, success)\n- **formik-patterns**: Apply form submission patterns\n\n## When to Use\nThis skill is applicable to execute the workflow or actions described in the overview.\n\n## Limitations\n- Use this skill only when the task clearly matches the scope described above.\n- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.\n- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.","tags":["react","patterns","antigravity","awesome","skills","sickn33"],"capabilities":["skill","source-sickn33","category-antigravity-awesome-skills"],"categories":["antigravity-awesome-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/sickn33/antigravity-awesome-skills/react-ui-patterns","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"install_from":"skills.sh"}},"qualityScore":"0.300","qualityRationale":"deterministic score 0.30 from registry signals: · indexed on skills.sh · published under sickn33/antigravity-awesome-skills","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:v1","enrichmentVersion":1,"enrichedAt":"2026-04-25T11:40:48.412Z","embedding":null,"createdAt":"2026-04-18T20:34:53.271Z","updatedAt":"2026-04-25T11:40:48.412Z","lastSeenAt":"2026-04-25T11:40:48.412Z","tsv":"'1':15,217 '2':26,226 '3':36,234 '4':44,245 '5':53 'action':200,252,446,633,681 'actual':24 'alway':27,253,264,357 'anti':505 'anti-pattern':504 'antigrav':4 'appli':665 'applic':675 'appropri':624 'ask':719 'async':361,468,617 'avail':52 'await':477,537 'awesom':5 'banner':236 'becom':51 'better':59 'boundari':727 'button':204,332,337,340,344,365,369,383,493,560,564,569,578,614,619 'cach':122 'catch':539 'category-antigravity-awesome-skills' 'caught':292 'caus':515 'checklist':586 'circl':436 'clarif':721 'clear':694 'collect':613 'complet':588 'compon':309,591 'console.error':279,302,552 'console.log':541 'const':89,268,297,320,456,458,466 'content':48,192,195,206 'contextu':419 'continu':146,161 'core':13 'correct':81,262,364,410,520,547,573 'creat':276,287,442,448 'createitem':269,280,298 'criteria':730 'critic':256,356 'data':57,62,77,87,90,104,107,123,154,165,171,241,408,416,513,525,528,607,625 'data.items':113 'decis':132 'degrad':55 'describ':682,698 'descript':441 'disabl':349,353,358,366,370,498,566,574,581,615 'disclosur':46 'display':79 'e':540,542 'empti':174,176,389,391,399,404,412,420,609,660 'emptyst':432 'environ':710 'environment-specif':709 'error':29,92,95,98,99,137,140,210,214,219,225,230,235,240,247,255,259,263,278,282,290,301,303,307,314,315,322,475,489,530,534,548,551,555,594,649,659 'error.message':330 'errors.name':491 'errorst':97,321 'errorstateprop':313,325 'even':118 'everi':394 'execut':677 'exist':88,514,608 'expert':715 'explicit':411 'fail':35,281,285,554,559 'fallback':181 'feedback':635 'feel':42 'field':221 'field-level':220 'first':444 'fix':474 'flash':127,516 'flatlist':407,415 'form':223,452,666 'formik':663 'formik-pattern':662 'full':246 'golden':67 'grace':54 'graphql':642 'graphql-schema':641 'handl':211,215,531,596,650 'handlechang':487 'handlecr':451 'handleerror':465 'handler':630 'handlesubmit':346,375,385,467,497 'handlesuccess':463 'hierarchi':216 'icon':433 'idea':296,546 'indic':71,158,622 'initi':201 'inlin':208,218 'input':480,483,724 'instant':43 'integr':637 'interfac':312 'isload':347,372,501,583 'issubmit':348,351,371,373,386 'isvalid':350,470,499 'item':112,168,275,288,409,417,430,439,445,449 'itemlist':111 'items.length':108 'know':32 'known':191 'label':447 'layout':198 'level':222,239 'limit':686 'list':427 'list/card':197 'list/collection':395 'listemptycompon':418 'load':20,25,63,70,84,91,103,125,130,149,157,180,203,270,341,368,460,500,502,507,518,523,527,577,582,584,601,621,658 'make':39 'match':695 'miss':732 'modal':199 'multipl':381 'must':31,396 'mutat':538,626,627,645 'myform':457 'name':488 'need':250 'never':16,257 'noth':306 'notif':228 'onchang':486 'onclick':333,345,374,384,450,496,570,579 'oncomplet':272,462 'onerror':277,300,464,550,629 'onretri':100,316,323,331,334 'oper':209,355,362,553,558,618 'optimist':37 'option':144 'output':704 'overview':685 'page':202,238 'page-level':237 'partial':56,243 'pattern':3,12,65,212,310,339,454,506,646,653,664,668 'permiss':725 'placehold':207 'pleas':473 'plus':435 'plus-circl':434 'principl':14 'progress':45 'proper':648 'provid':611 'react':1,10 'recover':229 'refetch':101,129 'requir':393,723 'result':426 'retri':143,233 'return':96,105,109,110,126,406,414,476,482,519,529 'review':716 'rule':68 'safeti':726 'schema':643 'scope':697 'screen':248 'search':423 'see':305 'shape':193,196 'show':17,47,69,83,116,139,156,169,175,179,254,522,576,620 'shown':598,603 'sickn33':9 'silent':260,291 'skeleton':182,186 'skill':6,7,640,673,689 'someth':34,327 'source-sickn33' 'specif':711 'spinner':21,117,184,189,511 'spinner/skeleton':159 'stale':18 'state':64,131,141,177,308,338,342,390,392,400,405,413,421,508,561,593,595,602,610,657 'still':242 'stop':717 'string':319 'submiss':205,453,568,667 'submit':352,376,387,388,459,478,495,503,571,572,580,585 'substitut':707 'success':661,729 'surfac':28,265,549 'swallow':258,535 'tap':380 'task':693 'test':652,654,713 'testing-pattern':651 'time':382 'titl':274,284,318,324,326,437,472,557 'toast':227 'toast.error':283,471,556 'toast.success':273 'toast/visual':636 'touched.name':490 'treat':702 'tree':133 'tri':335,536 'trigger':359 'tsx':343,363,401,422,455 'type':494 'typescript':80,114,261,311,509,532,562 'ui':2,11,19,41,590,592,656 'undefin':492 'unknown':194 'unrecover':249 'updat':38 'usabl':244 'use':185,188,644,671,687 'usecreateitemmut':271,299 'usegetitemsqueri':93 'user':30,231,251,267,293,304,378,543,600,632 'usesubmitmut':461 'valid':224,712 'valu':481,484 'values.name':485 'variabl':479 'void':317 'vs':183 'went':328 'without':524 'workflow':679 'wrong':115,289,329,377,402,510,533,563 'yes':138,155,166,172 'yet':431,440","prices":[{"id":"9ac38514-4e83-46bb-987e-3735e0269110","listingId":"e9ecbcaa-3178-46ed-af4a-c98b526ca1ed","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"sickn33","category":"antigravity-awesome-skills","install_from":"skills.sh"},"createdAt":"2026-04-18T20:34:53.271Z"}],"sources":[{"listingId":"e9ecbcaa-3178-46ed-af4a-c98b526ca1ed","source":"github","sourceId":"sickn33/antigravity-awesome-skills/react-ui-patterns","sourceUrl":"https://github.com/sickn33/antigravity-awesome-skills/tree/main/skills/react-ui-patterns","isPrimary":false,"firstSeenAt":"2026-04-18T21:43:23.391Z","lastSeenAt":"2026-04-25T06:51:49.974Z"},{"listingId":"e9ecbcaa-3178-46ed-af4a-c98b526ca1ed","source":"skills_sh","sourceId":"sickn33/antigravity-awesome-skills/react-ui-patterns","sourceUrl":"https://skills.sh/sickn33/antigravity-awesome-skills/react-ui-patterns","isPrimary":true,"firstSeenAt":"2026-04-18T20:34:53.271Z","lastSeenAt":"2026-04-25T11:40:48.412Z"}],"details":{"listingId":"e9ecbcaa-3178-46ed-af4a-c98b526ca1ed","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"sickn33","slug":"react-ui-patterns","source":"skills_sh","category":"antigravity-awesome-skills","skills_sh_url":"https://skills.sh/sickn33/antigravity-awesome-skills/react-ui-patterns"},"updatedAt":"2026-04-25T11:40:48.412Z"}}