[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-meta-relay-best-practices":3,"mdc-gln4qb-key":40,"related-org-meta-relay-best-practices":3372,"related-repo-meta-relay-best-practices":3548},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":12,"stars":29,"repoUrl":30,"updatedAt":31,"license":32,"forks":33,"topics":34,"repo":35,"sourceUrl":38,"mdContent":39},"relay-best-practices","write idiomatic Relay code","Best practices for writing idiomatic Relay code. ALWAYS use this skill when writing or modifying React components that use Relay for data fetching. Covers fragments, queries, mutations, pagination, and common anti-patterns. Use when you see `useFragment`, `useLazyLoadQuery`, `usePreloadedQuery`, `useMutation`, `usePaginationFragment`, `graphql` template literals, `react-relay` imports, or `__generated__\u002F*.graphql` files. Also use when asked to explain Relay concepts, debug Relay issues, or review Relay code.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},"meta","Meta Open Source","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fmeta.png","facebook",[13,17,20,23,26],{"name":14,"slug":15,"type":16},"React","react","tag",{"name":18,"slug":19,"type":16},"Relay","relay",{"name":21,"slug":22,"type":16},"GraphQL","graphql",{"name":24,"slug":25,"type":16},"Engineering","engineering",{"name":27,"slug":28,"type":16},"Frontend","frontend",18950,"https:\u002F\u002Fgithub.com\u002Ffacebook\u002Frelay","2026-04-22T04:58:15.370563",null,1895,[],{"repoUrl":30,"stars":29,"forks":33,"topics":36,"description":37},[],"Relay is a JavaScript framework for building data-driven React applications.","https:\u002F\u002Fgithub.com\u002Ffacebook\u002Frelay\u002Ftree\u002FHEAD\u002Fskills\u002Frelay-best-practices","---\nname: relay-best-practices\ndescription: >-\n  Best practices for writing idiomatic Relay code. ALWAYS use this skill when\n  writing or modifying React components that use Relay for data fetching. Covers\n  fragments, queries, mutations, pagination, and common anti-patterns. Use when\n  you see `useFragment`, `useLazyLoadQuery`, `usePreloadedQuery`, `useMutation`,\n  `usePaginationFragment`, `graphql` template literals, `react-relay` imports,\n  or `__generated__\u002F*.graphql` files. Also use when asked to explain Relay\n  concepts, debug Relay issues, or review Relay code.\n---\n\n# Relay Best Practices\n\nRelay is a GraphQL client for React that enforces colocated, composable, and\ntype-safe data fetching. Its core insight is that each component should declare\nexactly what data it needs via GraphQL fragments, and Relay handles the rest —\nfetching, caching, consistency, and updates.\n\nThis skill provides opinionated guidance on which patterns to prefer. For\ndetailed API documentation, read the relevant page from the doc map below.\n\n## Documentation\n\nRelay ships LLM-friendly docs in `node_modules\u002Frelay-runtime\u002Fllm-docs\u002F`\n(available after v20.1.1). For older versions, fetch the same files from\n`https:\u002F\u002Fraw.githubusercontent.com\u002Ffacebook\u002Frelay\u002Fmain\u002Fwebsite\u002Fdocs\u002F`.\n\nPaths below are relative to this directory (`\u003Cllm-docs>\u002F`). Read the relevant\npage before writing Relay code. Key docs:\n\n| Topic | Path |\n|-------|------|\n| Core concepts & philosophy | `principles-and-architecture\u002Fthinking-in-relay.mdx` |\n| Fragments | `guided-tour\u002Frendering\u002Ffragments.mdx` |\n| Queries | `guided-tour\u002Frendering\u002Fqueries.mdx` |\n| Mutations | `guided-tour\u002Fupdating-data\u002Fgraphql-mutations.mdx` |\n| Pagination | `guided-tour\u002Flist-data\u002Fpagination.mdx` |\n| Refetching | `guided-tour\u002Frefetching\u002Frefetching-queries-with-different-data.mdx` |\n| `useFragment` | `api-reference\u002Fhooks\u002Fuse-fragment.mdx` |\n| `usePreloadedQuery` | `api-reference\u002Fhooks\u002Fuse-preloaded-query.mdx` |\n| `useQueryLoader` \u002F `loadQuery` | `api-reference\u002Fhooks\u002Fload-query.mdx` |\n| `useMutation` | `api-reference\u002Fhooks\u002Fuse-mutation.mdx` |\n| `usePaginationFragment` | `api-reference\u002Fhooks\u002Fuse-pagination-fragment.mdx` |\n| `@throwOnFieldError` | `guides\u002Fthrow-on-field-error-directive.mdx` |\n| `@catch` directive | `guides\u002Fcatch-directive.mdx` |\n| Semantic nullability | `guides\u002Fsemantic-nullability.mdx` |\n| Relay Resolvers | `guides\u002Frelay-resolvers\u002Fintroduction.mdx` |\n| Testing | `guides\u002Ftesting-relay-components.mdx` |\n| Compiler setup | `getting-started\u002Fcompiler.mdx` |\n| Compiler configuration | `getting-started\u002Fcompiler-config.mdx` |\n| Lint rules (ESLint plugin) | `getting-started\u002Flint-rules.mdx` |\n\n**For performance-specific guidance** (query placement, `@defer`, pagination,\nfetch policies, caching, fragment granularity), see the companion\n`relay-performance` skill.\n\n## Core Philosophy\n\nThese principles are the foundation of every decision below. When in doubt,\nrefer back to them.\n\n- **Co-location**: Each component declares its own data requirements via a\n  GraphQL fragment, right next to the rendering code. Data needs travel with the\n  component, not separately.\n- **Data masking**: A component can only access the fields it explicitly\n  selected in its own fragment. Parents cannot see child fragment data, and vice\n  versa. This prevents implicit coupling between components.\n- **Composition**: Fragments compose into parent fragments and ultimately into\n  queries, forming a tree that mirrors the component tree. The compiler flattens\n  this into a single network request per query.\n- **Render-as-you-fetch**: Start fetching data before the component that needs\n  it renders. This avoids sequential request waterfalls.\n- **Normalized store**: Relay maintains a flat, ID-keyed cache. When a mutation\n  returns updated data, every component reading that data re-renders\n  automatically. You do not need to manually propagate changes.\n\n## Compiler Workflow\n\nRelay uses an ahead-of-time compiler that reads `graphql` tagged template\nliterals in your code and generates runtime artifacts and TypeScript\u002FFlow types.\n\n### Finding the config\n\nThe compiler looks for its config in these locations (checked in order):\n- `relay.config.{json,js,mjs,ts}` in the project root\n- A `\"relay\"` key in `package.json`\n\nSee `\u003Cllm-docs>\u002Fgetting-started\u002Fcompiler-config.mdx` for the full config schema.\nYou can also emit a JSON Schema for the config by running\n`npx relay-compiler config-json-schema`.\n\n### Running the compiler\n\nRun `npx relay-compiler` after any change to the contents of a `graphql` tagged\ntemplate literal or the docblock of a Relay Resolver. Some projects add this as\na script in `package.json` (e.g., `yarn relay`). The compiler also supports watch mode (`--watch`), but avoid using it in\nnon-interactive contexts since the process never exits.\n\nGenerated files go into `__generated__\u002F` directories next to the source files.\nNever edit these files — they are overwritten on every compiler run. If you see\ntype errors about missing generated types, run the compiler first — the\ntypes are likely just out of date.\n\n## Lint Rules\n\nRelay's ESLint plugin (`eslint-plugin-relay`) is a key part of the developer\nexperience. Two rules are especially important:\n\n- `relay\u002Funused-fields` — detects GraphQL fields that are selected in a\n  fragment but never read in the component. This prevents the \"append-only\n  query\" problem where fragments accumulate unused fields over time, fetching\n  data no component actually needs.\n- `relay\u002Fno-future-added-value` — prevents explicitly handling the\n  `\"%future added value\"` enum placeholder that Relay inserts to ensure you\n  handle the possibility of new enum variants being added by the server.\n\nSee `\u003Cllm-docs>\u002Fgetting-started\u002Flint-rules.mdx` for installation and configuration.\n\n## Decision Rules\n\n### Queries belong at roots — never in hooks\n\nQueries belong at **route entrypoints** (the top-level component for a URL).\nHooks are leaves — reused across many components. A query inside a hook fires\nlate (after the hook's host renders) AND duplicates across every caller. The fix\nis always: accept a fragment key as a parameter, use `useFragment`.\n\nUse `usePreloadedQuery` + `useQueryLoader` (or `loadQuery`). Start the fetch in\nan event handler, route transition, or during app initialization — before the\ncomponent renders. `useLazyLoadQuery` does not start fetching until render,\ncreating waterfalls. See `\u003Cllm-docs>\u002Fguided-tour\u002Frendering\u002Fqueries.mdx` for the full pattern.\n\n### Before fixing where a query lives, ask if it should exist\n\n| Question | If YES |\n|----------|--------|\n| Parent already fetches this GraphQL type? | Delete query, use `useFragment` |\n| Component only fetches and passes data down? | Delete the wrapper component entirely (loader anti-pattern) |\n| Query is inside a custom hook? | Delete query, accept a fragment key param |\n| Two components fetch the same data? | Delete one query, fetch in a common ancestor |\n| Data is only used for logging\u002Fanalytics? | Move to `@defer` or server-side logging |\n| Data is static config (same for every user)? | Inject server-side, no round-trip needed |\n\n### `loadQuery` in `useEffect` is worse than `useLazyLoadQuery`\n\n`useEffect` runs **after paint**, so the fetch starts even later than\n`useLazyLoadQuery` (which at least starts during render). Call `loadQuery` in\nevent handlers, route transitions, or app initialization — never in effects.\n\n### Walking the ancestor tree for `useFragment`\n\nWhen deciding whether `useFragment` can replace a query, walk **up** the\ncomponent tree from your component. Stop at: route boundaries, feature gates,\nconditional renders, or user-triggered interactions. Keep walking through:\nunconditional renders, layout\u002Fwrapper components, context providers.\n\nIf any ancestor already queries the GraphQL type you need, `useFragment` is\nthe answer. Threading fragment keys through several layers of props is fine —\nit IS the correct pattern.\n\n### Don't default query variables to empty values\n\nNever default a query variable to `''`, `0`, or `null` when the real value is\nunavailable. This fires the query with bad data, returning wrong results or\nerrors. Instead, use conditional rendering (`if (!id) return null`) or\n`@include`\u002F`@skip` directives to omit the field entirely.\n\n### Data flow: Always use fragments\n\nEvery component that displays server data should declare a fragment and receive\na fragment reference (the `$key` type) as a prop. The parent spreads the\nchild's fragment in its own query or fragment and passes the result down. See\nthe \"Maintain fragment co-location\" anti-pattern below for an example.\n\n### Mutations: Spread fragments into responses\n\nSpread the consuming component's fragment into the mutation response rather than\nselecting fields individually. This keeps them in sync automatically. See the\nanti-pattern example below.\n\n### Error handling: Use `@throwOnFieldError` and `@catch`\n\nThe recommended approach for handling field errors and nullability is to add\n`@throwOnFieldError` to your fragment or query. This causes Relay to throw a\nJavaScript exception if a field error is encountered, which can be caught by a\nReact error boundary. It also enables non-null types for `@semanticNonNull`\nfields, eliminating unnecessary null checks. Note that this pattern depends on\nReact error boundaries being configured in your application — proceed with\ncaution if error boundaries are not set up robustly.\n\nFor fields where you want to handle errors locally instead of throwing, use\n`@catch` to receive errors inline as `{ ok: true, value: T } | { ok: false,\nerrors: [...] }`.\n\n`@required` is also available for declaring that specific fields must be\nnon-null, but `@throwOnFieldError` + `@catch` is the preferred pattern for new\ncode.\n\nSee `\u003Cllm-docs>\u002Fguides\u002Fthrow-on-field-error-directive.mdx`, `\u003Cllm-docs>\u002Fguides\u002Fcatch-directive.mdx`,\nand `\u003Cllm-docs>\u002Fguides\u002Fsemantic-nullability.mdx`.\n\n### Pagination: Use the three-directive pattern\n\nAlways use `@argumentDefinitions` (for cursor\u002Fcount variables),\n`@refetchable` (to auto-generate the pagination query), and `@connection` (to\nidentify the connection for store management) together. Never write manual\npagination queries. See `\u003Cllm-docs>\u002Fguided-tour\u002Flist-data\u002Fpagination.mdx`.\n\n### Client state: Prefer Relay Resolvers\n\nWhen multiple components need to read client-side data, use Relay Resolvers to\ndefine client-only fields on the GraphQL schema rather than prop-drilling or\nReact context. This gives client state the same composability and caching\nguarantees as server data. Use `useClientQuery` for queries that read only\nresolver-defined fields.\n\nSee `\u003Cllm-docs>\u002Fguides\u002Frelay-resolvers\u002Fintroduction.mdx` for how to define resolvers.\n\n## Critical Anti-Patterns\n\n### Never copy Relay data into React state\n\nThis is the single most important rule. Do not read data from `useFragment` and\ncopy it into `useState`, and do not update that state manually in mutation\n`onCompleted` callbacks.\n\n```tsx\n\u002F\u002F WRONG: Copying Relay data into React state\nfunction UserProfile({userKey}) {\n  const data = useFragment(UserProfileFragment, userKey);\n  const [name, setName] = useState(data.name); \u002F\u002F broken\n\n  const [commit] = useMutation(UpdateNameMutation);\n  const handleSave = (newName) => {\n    commit({\n      variables: {name: newName},\n      onCompleted: (response) => {\n        setName(response.updateName.user.name); \u002F\u002F broken\n      },\n    });\n  };\n  return \u003Cspan>{name}\u003C\u002Fspan>;\n}\n```\n\nWhy this is wrong: Relay's normalized store is the single source of truth. When\na mutation returns updated data with a matching `id`, Relay automatically\nupdates every component reading that data via `useFragment`. By copying into\n`useState`, you create a second source of truth that Relay cannot update. The\ncomponent will show stale data whenever the record is updated by another\nmutation, subscription, or refetch elsewhere in the app.\n\n```tsx\n\u002F\u002F CORRECT: Read directly from the fragment\nfunction UserProfile({userKey}) {\n  const data = useFragment(UserProfileFragment, userKey);\n  const [commit, isInFlight] = useMutation(UpdateNameMutation);\n\n  const handleSave = (newName) => {\n    commit({variables: {name: newName}});\n    \u002F\u002F No onCompleted needed — Relay updates the store automatically,\n    \u002F\u002F and useFragment re-renders this component with the new data.\n  };\n  return \u003Cspan>{data.name}\u003C\u002Fspan>;\n}\n```\n\nSimilarly, do not store a fragment key (the `$key` prop) in React state. Relay\ngarbage collects data that is no longer retained by a mounted query component —\nif the component that originally fetched the data unmounts, a stashed key may\npoint to data that is no longer in the store.\n\n### Maintain fragment co-location\n\nDo not fetch all data in a parent's query and pass raw data objects as props to\nchildren. This defeats data masking and creates tight coupling — adding a field\nto a child component requires editing the parent's query. Note that the\n`relay\u002Funused-fields` lint rule will flag fields selected in the parent that\nare only used by children — this is a good signal that you need to extract a\nfragment.\n\n```tsx\n\u002F\u002F WRONG: Parent fetches everything, passes raw data\nfunction Parent({queryRef}) {\n  const data = usePreloadedQuery(graphql`\n    query ParentQuery {\n      user {\n        name\n        email\n        avatarUrl\n      }\n    }\n  `, queryRef);\n  return \u003CUserCard name={data.user.name} avatarUrl={data.user.avatarUrl} \u002F>;\n}\n\n\u002F\u002F CORRECT: Child declares its own fragment\nfunction Parent({queryRef}) {\n  const data = usePreloadedQuery(graphql`\n    query ParentQuery {\n      user {\n        ...UserCard_user\n      }\n    }\n  `, queryRef);\n  return \u003CUserCard user={data.user} \u002F>;\n}\n```\n\n### Spread fragments into mutation responses\n\nDo not select fields individually in both a fragment and a mutation response —\nthey will drift out of sync. Spread the fragment instead:\n\n```graphql\n# WRONG\nmutation UpdateUserMutation($input: UpdateUserInput!) {\n  updateUser(input: $input) {\n    user { id, name, email, avatarUrl }\n  }\n}\n\n# CORRECT\nmutation UpdateUserMutation($input: UpdateUserInput!) {\n  updateUser(input: $input) {\n    user { ...UserCard_user }\n  }\n}\n```\n\n## Correctness\n\n### Use `optimisticUpdater` for store-dependent values\n\nIf an optimistic value depends on current store state (e.g., incrementing a\nlike count), use `optimisticUpdater` instead of `optimisticResponse`. Multiple\noverlapping optimistic responses can compound incorrectly — two simultaneous\n\"like\" mutations both read count=5 and set count=6, instead of 5→6→7. When one\nrolls back, the store is left in an inconsistent state.\n\n### Invalidate data after wide-effect mutations\n\nWhen a mutation has side effects too broad to capture in a single response\npayload, use `invalidateRecord()` for targeted invalidation or\n`invalidateStore()` for global invalidation. Pair with\n`useSubscribeToInvalidationState` on mounted components to trigger refetches\nfor stale data automatically.\n\n### Handle staleness explicitly\n\nRelay treats cached data as fresh **indefinitely** by default. Two approaches:\n\n- **Time-based**: Set `queryCacheExpirationTime` on the Relay Store to\n  automatically mark data stale after a duration.\n- **Event-based**: Call `invalidateRecord()` after mutations whose side effects\n  extend beyond the mutation response payload.\n\nWithout explicit staleness handling, components can display arbitrarily old data\nafter the user returns to a previously visited screen.\n\n### Single artifact directory for type safety\n\nWithout setting `artifactDirectory` in the compiler config, fragment references\nmay default to `any`, losing type safety. Set it and align your bundler's\nmodule resolution accordingly. See `\u003Cllm-docs>\u002Fgetting-started\u002Fcompiler-config.mdx`.\n\n### Use `@updatable` for store manipulation\n\nPrefer typesafe updatable queries\u002Ffragments over raw store manipulation with\nstring-based field access (e.g., `store.get(id).setValue(newName, 'name')`).\nUpdatable fragments provide getters and setters, reducing the risk of typos\nand type mismatches.\n\n### Avoid unnecessary refetches after mutations\n\nDo not call `refetch()` or `fetchQuery()` after mutations when spreading\ncomponent fragments in the mutation response would auto-update the store. Each\nunnecessary refetch is a wasted network request and delays the UI update.\n\nReserve manual refetches for cases where the mutation's side effects are too\nbroad to capture in the response payload — and in those cases, prefer\n`invalidateRecord()` (see above).\n\n### Use subscriptions for real-time data\n\nPrefer GraphQL Subscriptions over polling (`setInterval` + `fetchQuery`) or\nmanual refresh buttons for data that must stay current. Subscriptions push\nupdates only when data changes and integrate with Relay's normalized store\nautomatically.\n\n## Naming Conventions\n\nRelay **enforces** that operation names match the module (file) they are defined\nin. Mismatched names cause compiler errors, not just style warnings.\n\n| Element | Convention | Example |\n|---------|-----------|---------|\n| Fragment | `ComponentName_propName` | `UserCard_user` |\n| Query | `ComponentNameQuery` | `HomePageQuery` |\n| Mutation | `ComponentNameMutation` | `LikeButtonMutation` |\n| Generated files | `__generated__\u002F*.graphql` | Never edit these |\n\nThe module name is the filename stripped of all extensions (`.react.js`, `.tsx`,\netc.). `UserCard.react.js` → module name `UserCard`.\n\n### Renaming operations when extracting to a new file\n\nWhen moving a component to a new file, rename only the operations **defined in\nthat file** to match the new filename. Do NOT rename fragment spreads that\nreference fragments owned by other modules — those names belong to their\ndefining component.\n\nRenaming an operation also changes its generated type name (e.g.,\n`UserCard_user$key` → `ProfileCard_user$key`), so update all downstream imports\nof those generated types.\n\n### Never hand-edit `__generated__\u002F` files\n\nThe next compiler run overwrites any manual edits. If you see type errors about\nmissing generated types, run the compiler first — the types are just out of\ndate, not missing.\n\n### Verify mutation variable keys after auto-formatting\n\nAuto-formatters and linters can rename variables in ways that silently break\nmutation calls. After running lint auto-fix, verify that variable keys in\n`commit({ variables: { ... } })` still match the generated `Mutation$variables`\ntype (check for `data` vs `input` mismatches in particular).\n",{"data":41,"body":42},{"name":4,"description":6},{"type":43,"children":44},"root",[45,53,59,64,71,93,106,495,522,528,533,588,594,606,613,618,651,671,677,720,733,739,752,785,797,803,809,827,869,875,987,1011,1042,1053,1072,1084,1090,1143,1149,1162,1168,1173,1191,1211,1230,1254,1280,1286,1322,1328,1341,1353,1359,1365,1393,1909,1936,2242,2254,2260,2272,2712,2718,2723,2829,2835,2848,2868,2874,2903,2909,2921,2959,2964,2970,2997,3010,3023,3029,3050,3062,3068,3088,3094,3106,3233,3267,3273,3285,3306,3319,3324,3330,3366],{"type":46,"tag":47,"props":48,"children":49},"element","h1",{"id":4},[50],{"type":51,"value":52},"text","Relay Best Practices",{"type":46,"tag":54,"props":55,"children":56},"p",{},[57],{"type":51,"value":58},"Relay is a GraphQL client for React that enforces colocated, composable, and\ntype-safe data fetching. Its core insight is that each component should declare\nexactly what data it needs via GraphQL fragments, and Relay handles the rest —\nfetching, caching, consistency, and updates.",{"type":46,"tag":54,"props":60,"children":61},{},[62],{"type":51,"value":63},"This skill provides opinionated guidance on which patterns to prefer. For\ndetailed API documentation, read the relevant page from the doc map below.",{"type":46,"tag":65,"props":66,"children":68},"h2",{"id":67},"documentation",[69],{"type":51,"value":70},"Documentation",{"type":46,"tag":54,"props":72,"children":73},{},[74,76,83,85,91],{"type":51,"value":75},"Relay ships LLM-friendly docs in ",{"type":46,"tag":77,"props":78,"children":80},"code",{"className":79},[],[81],{"type":51,"value":82},"node_modules\u002Frelay-runtime\u002Fllm-docs\u002F",{"type":51,"value":84},"\n(available after v20.1.1). For older versions, fetch the same files from\n",{"type":46,"tag":77,"props":86,"children":88},{"className":87},[],[89],{"type":51,"value":90},"https:\u002F\u002Fraw.githubusercontent.com\u002Ffacebook\u002Frelay\u002Fmain\u002Fwebsite\u002Fdocs\u002F",{"type":51,"value":92},".",{"type":46,"tag":54,"props":94,"children":95},{},[96,98,104],{"type":51,"value":97},"Paths below are relative to this directory (",{"type":46,"tag":77,"props":99,"children":101},{"className":100},[],[102],{"type":51,"value":103},"\u003Cllm-docs>\u002F",{"type":51,"value":105},"). Read the relevant\npage before writing Relay code. Key docs:",{"type":46,"tag":107,"props":108,"children":109},"table",{},[110,129],{"type":46,"tag":111,"props":112,"children":113},"thead",{},[114],{"type":46,"tag":115,"props":116,"children":117},"tr",{},[118,124],{"type":46,"tag":119,"props":120,"children":121},"th",{},[122],{"type":51,"value":123},"Topic",{"type":46,"tag":119,"props":125,"children":126},{},[127],{"type":51,"value":128},"Path",{"type":46,"tag":130,"props":131,"children":132},"tbody",{},[133,151,168,185,202,219,236,257,278,307,328,349,370,393,410,427,444,461,478],{"type":46,"tag":115,"props":134,"children":135},{},[136,142],{"type":46,"tag":137,"props":138,"children":139},"td",{},[140],{"type":51,"value":141},"Core concepts & philosophy",{"type":46,"tag":137,"props":143,"children":144},{},[145],{"type":46,"tag":77,"props":146,"children":148},{"className":147},[],[149],{"type":51,"value":150},"principles-and-architecture\u002Fthinking-in-relay.mdx",{"type":46,"tag":115,"props":152,"children":153},{},[154,159],{"type":46,"tag":137,"props":155,"children":156},{},[157],{"type":51,"value":158},"Fragments",{"type":46,"tag":137,"props":160,"children":161},{},[162],{"type":46,"tag":77,"props":163,"children":165},{"className":164},[],[166],{"type":51,"value":167},"guided-tour\u002Frendering\u002Ffragments.mdx",{"type":46,"tag":115,"props":169,"children":170},{},[171,176],{"type":46,"tag":137,"props":172,"children":173},{},[174],{"type":51,"value":175},"Queries",{"type":46,"tag":137,"props":177,"children":178},{},[179],{"type":46,"tag":77,"props":180,"children":182},{"className":181},[],[183],{"type":51,"value":184},"guided-tour\u002Frendering\u002Fqueries.mdx",{"type":46,"tag":115,"props":186,"children":187},{},[188,193],{"type":46,"tag":137,"props":189,"children":190},{},[191],{"type":51,"value":192},"Mutations",{"type":46,"tag":137,"props":194,"children":195},{},[196],{"type":46,"tag":77,"props":197,"children":199},{"className":198},[],[200],{"type":51,"value":201},"guided-tour\u002Fupdating-data\u002Fgraphql-mutations.mdx",{"type":46,"tag":115,"props":203,"children":204},{},[205,210],{"type":46,"tag":137,"props":206,"children":207},{},[208],{"type":51,"value":209},"Pagination",{"type":46,"tag":137,"props":211,"children":212},{},[213],{"type":46,"tag":77,"props":214,"children":216},{"className":215},[],[217],{"type":51,"value":218},"guided-tour\u002Flist-data\u002Fpagination.mdx",{"type":46,"tag":115,"props":220,"children":221},{},[222,227],{"type":46,"tag":137,"props":223,"children":224},{},[225],{"type":51,"value":226},"Refetching",{"type":46,"tag":137,"props":228,"children":229},{},[230],{"type":46,"tag":77,"props":231,"children":233},{"className":232},[],[234],{"type":51,"value":235},"guided-tour\u002Frefetching\u002Frefetching-queries-with-different-data.mdx",{"type":46,"tag":115,"props":237,"children":238},{},[239,248],{"type":46,"tag":137,"props":240,"children":241},{},[242],{"type":46,"tag":77,"props":243,"children":245},{"className":244},[],[246],{"type":51,"value":247},"useFragment",{"type":46,"tag":137,"props":249,"children":250},{},[251],{"type":46,"tag":77,"props":252,"children":254},{"className":253},[],[255],{"type":51,"value":256},"api-reference\u002Fhooks\u002Fuse-fragment.mdx",{"type":46,"tag":115,"props":258,"children":259},{},[260,269],{"type":46,"tag":137,"props":261,"children":262},{},[263],{"type":46,"tag":77,"props":264,"children":266},{"className":265},[],[267],{"type":51,"value":268},"usePreloadedQuery",{"type":46,"tag":137,"props":270,"children":271},{},[272],{"type":46,"tag":77,"props":273,"children":275},{"className":274},[],[276],{"type":51,"value":277},"api-reference\u002Fhooks\u002Fuse-preloaded-query.mdx",{"type":46,"tag":115,"props":279,"children":280},{},[281,298],{"type":46,"tag":137,"props":282,"children":283},{},[284,290,292],{"type":46,"tag":77,"props":285,"children":287},{"className":286},[],[288],{"type":51,"value":289},"useQueryLoader",{"type":51,"value":291}," \u002F ",{"type":46,"tag":77,"props":293,"children":295},{"className":294},[],[296],{"type":51,"value":297},"loadQuery",{"type":46,"tag":137,"props":299,"children":300},{},[301],{"type":46,"tag":77,"props":302,"children":304},{"className":303},[],[305],{"type":51,"value":306},"api-reference\u002Fhooks\u002Fload-query.mdx",{"type":46,"tag":115,"props":308,"children":309},{},[310,319],{"type":46,"tag":137,"props":311,"children":312},{},[313],{"type":46,"tag":77,"props":314,"children":316},{"className":315},[],[317],{"type":51,"value":318},"useMutation",{"type":46,"tag":137,"props":320,"children":321},{},[322],{"type":46,"tag":77,"props":323,"children":325},{"className":324},[],[326],{"type":51,"value":327},"api-reference\u002Fhooks\u002Fuse-mutation.mdx",{"type":46,"tag":115,"props":329,"children":330},{},[331,340],{"type":46,"tag":137,"props":332,"children":333},{},[334],{"type":46,"tag":77,"props":335,"children":337},{"className":336},[],[338],{"type":51,"value":339},"usePaginationFragment",{"type":46,"tag":137,"props":341,"children":342},{},[343],{"type":46,"tag":77,"props":344,"children":346},{"className":345},[],[347],{"type":51,"value":348},"api-reference\u002Fhooks\u002Fuse-pagination-fragment.mdx",{"type":46,"tag":115,"props":350,"children":351},{},[352,361],{"type":46,"tag":137,"props":353,"children":354},{},[355],{"type":46,"tag":77,"props":356,"children":358},{"className":357},[],[359],{"type":51,"value":360},"@throwOnFieldError",{"type":46,"tag":137,"props":362,"children":363},{},[364],{"type":46,"tag":77,"props":365,"children":367},{"className":366},[],[368],{"type":51,"value":369},"guides\u002Fthrow-on-field-error-directive.mdx",{"type":46,"tag":115,"props":371,"children":372},{},[373,384],{"type":46,"tag":137,"props":374,"children":375},{},[376,382],{"type":46,"tag":77,"props":377,"children":379},{"className":378},[],[380],{"type":51,"value":381},"@catch",{"type":51,"value":383}," directive",{"type":46,"tag":137,"props":385,"children":386},{},[387],{"type":46,"tag":77,"props":388,"children":390},{"className":389},[],[391],{"type":51,"value":392},"guides\u002Fcatch-directive.mdx",{"type":46,"tag":115,"props":394,"children":395},{},[396,401],{"type":46,"tag":137,"props":397,"children":398},{},[399],{"type":51,"value":400},"Semantic nullability",{"type":46,"tag":137,"props":402,"children":403},{},[404],{"type":46,"tag":77,"props":405,"children":407},{"className":406},[],[408],{"type":51,"value":409},"guides\u002Fsemantic-nullability.mdx",{"type":46,"tag":115,"props":411,"children":412},{},[413,418],{"type":46,"tag":137,"props":414,"children":415},{},[416],{"type":51,"value":417},"Relay Resolvers",{"type":46,"tag":137,"props":419,"children":420},{},[421],{"type":46,"tag":77,"props":422,"children":424},{"className":423},[],[425],{"type":51,"value":426},"guides\u002Frelay-resolvers\u002Fintroduction.mdx",{"type":46,"tag":115,"props":428,"children":429},{},[430,435],{"type":46,"tag":137,"props":431,"children":432},{},[433],{"type":51,"value":434},"Testing",{"type":46,"tag":137,"props":436,"children":437},{},[438],{"type":46,"tag":77,"props":439,"children":441},{"className":440},[],[442],{"type":51,"value":443},"guides\u002Ftesting-relay-components.mdx",{"type":46,"tag":115,"props":445,"children":446},{},[447,452],{"type":46,"tag":137,"props":448,"children":449},{},[450],{"type":51,"value":451},"Compiler setup",{"type":46,"tag":137,"props":453,"children":454},{},[455],{"type":46,"tag":77,"props":456,"children":458},{"className":457},[],[459],{"type":51,"value":460},"getting-started\u002Fcompiler.mdx",{"type":46,"tag":115,"props":462,"children":463},{},[464,469],{"type":46,"tag":137,"props":465,"children":466},{},[467],{"type":51,"value":468},"Compiler configuration",{"type":46,"tag":137,"props":470,"children":471},{},[472],{"type":46,"tag":77,"props":473,"children":475},{"className":474},[],[476],{"type":51,"value":477},"getting-started\u002Fcompiler-config.mdx",{"type":46,"tag":115,"props":479,"children":480},{},[481,486],{"type":46,"tag":137,"props":482,"children":483},{},[484],{"type":51,"value":485},"Lint rules (ESLint plugin)",{"type":46,"tag":137,"props":487,"children":488},{},[489],{"type":46,"tag":77,"props":490,"children":492},{"className":491},[],[493],{"type":51,"value":494},"getting-started\u002Flint-rules.mdx",{"type":46,"tag":54,"props":496,"children":497},{},[498,504,506,512,514,520],{"type":46,"tag":499,"props":500,"children":501},"strong",{},[502],{"type":51,"value":503},"For performance-specific guidance",{"type":51,"value":505}," (query placement, ",{"type":46,"tag":77,"props":507,"children":509},{"className":508},[],[510],{"type":51,"value":511},"@defer",{"type":51,"value":513},", pagination,\nfetch policies, caching, fragment granularity), see the companion\n",{"type":46,"tag":77,"props":515,"children":517},{"className":516},[],[518],{"type":51,"value":519},"relay-performance",{"type":51,"value":521}," skill.",{"type":46,"tag":65,"props":523,"children":525},{"id":524},"core-philosophy",[526],{"type":51,"value":527},"Core Philosophy",{"type":46,"tag":54,"props":529,"children":530},{},[531],{"type":51,"value":532},"These principles are the foundation of every decision below. When in doubt,\nrefer back to them.",{"type":46,"tag":534,"props":535,"children":536},"ul",{},[537,548,558,568,578],{"type":46,"tag":538,"props":539,"children":540},"li",{},[541,546],{"type":46,"tag":499,"props":542,"children":543},{},[544],{"type":51,"value":545},"Co-location",{"type":51,"value":547},": Each component declares its own data requirements via a\nGraphQL fragment, right next to the rendering code. Data needs travel with the\ncomponent, not separately.",{"type":46,"tag":538,"props":549,"children":550},{},[551,556],{"type":46,"tag":499,"props":552,"children":553},{},[554],{"type":51,"value":555},"Data masking",{"type":51,"value":557},": A component can only access the fields it explicitly\nselected in its own fragment. Parents cannot see child fragment data, and vice\nversa. This prevents implicit coupling between components.",{"type":46,"tag":538,"props":559,"children":560},{},[561,566],{"type":46,"tag":499,"props":562,"children":563},{},[564],{"type":51,"value":565},"Composition",{"type":51,"value":567},": Fragments compose into parent fragments and ultimately into\nqueries, forming a tree that mirrors the component tree. The compiler flattens\nthis into a single network request per query.",{"type":46,"tag":538,"props":569,"children":570},{},[571,576],{"type":46,"tag":499,"props":572,"children":573},{},[574],{"type":51,"value":575},"Render-as-you-fetch",{"type":51,"value":577},": Start fetching data before the component that needs\nit renders. This avoids sequential request waterfalls.",{"type":46,"tag":538,"props":579,"children":580},{},[581,586],{"type":46,"tag":499,"props":582,"children":583},{},[584],{"type":51,"value":585},"Normalized store",{"type":51,"value":587},": Relay maintains a flat, ID-keyed cache. When a mutation\nreturns updated data, every component reading that data re-renders\nautomatically. You do not need to manually propagate changes.",{"type":46,"tag":65,"props":589,"children":591},{"id":590},"compiler-workflow",[592],{"type":51,"value":593},"Compiler Workflow",{"type":46,"tag":54,"props":595,"children":596},{},[597,599,604],{"type":51,"value":598},"Relay uses an ahead-of-time compiler that reads ",{"type":46,"tag":77,"props":600,"children":602},{"className":601},[],[603],{"type":51,"value":22},{"type":51,"value":605}," tagged template\nliterals in your code and generates runtime artifacts and TypeScript\u002FFlow types.",{"type":46,"tag":607,"props":608,"children":610},"h3",{"id":609},"finding-the-config",[611],{"type":51,"value":612},"Finding the config",{"type":46,"tag":54,"props":614,"children":615},{},[616],{"type":51,"value":617},"The compiler looks for its config in these locations (checked in order):",{"type":46,"tag":534,"props":619,"children":620},{},[621,632],{"type":46,"tag":538,"props":622,"children":623},{},[624,630],{"type":46,"tag":77,"props":625,"children":627},{"className":626},[],[628],{"type":51,"value":629},"relay.config.{json,js,mjs,ts}",{"type":51,"value":631}," in the project root",{"type":46,"tag":538,"props":633,"children":634},{},[635,637,643,645],{"type":51,"value":636},"A ",{"type":46,"tag":77,"props":638,"children":640},{"className":639},[],[641],{"type":51,"value":642},"\"relay\"",{"type":51,"value":644}," key in ",{"type":46,"tag":77,"props":646,"children":648},{"className":647},[],[649],{"type":51,"value":650},"package.json",{"type":46,"tag":54,"props":652,"children":653},{},[654,656,662,664,670],{"type":51,"value":655},"See ",{"type":46,"tag":77,"props":657,"children":659},{"className":658},[],[660],{"type":51,"value":661},"\u003Cllm-docs>\u002Fgetting-started\u002Fcompiler-config.mdx",{"type":51,"value":663}," for the full config schema.\nYou can also emit a JSON Schema for the config by running\n",{"type":46,"tag":77,"props":665,"children":667},{"className":666},[],[668],{"type":51,"value":669},"npx relay-compiler config-json-schema",{"type":51,"value":92},{"type":46,"tag":607,"props":672,"children":674},{"id":673},"running-the-compiler",[675],{"type":51,"value":676},"Running the compiler",{"type":46,"tag":54,"props":678,"children":679},{},[680,682,688,690,695,697,702,704,710,712,718],{"type":51,"value":681},"Run ",{"type":46,"tag":77,"props":683,"children":685},{"className":684},[],[686],{"type":51,"value":687},"npx relay-compiler",{"type":51,"value":689}," after any change to the contents of a ",{"type":46,"tag":77,"props":691,"children":693},{"className":692},[],[694],{"type":51,"value":22},{"type":51,"value":696}," tagged\ntemplate literal or the docblock of a Relay Resolver. Some projects add this as\na script in ",{"type":46,"tag":77,"props":698,"children":700},{"className":699},[],[701],{"type":51,"value":650},{"type":51,"value":703}," (e.g., ",{"type":46,"tag":77,"props":705,"children":707},{"className":706},[],[708],{"type":51,"value":709},"yarn relay",{"type":51,"value":711},"). The compiler also supports watch mode (",{"type":46,"tag":77,"props":713,"children":715},{"className":714},[],[716],{"type":51,"value":717},"--watch",{"type":51,"value":719},"), but avoid using it in\nnon-interactive contexts since the process never exits.",{"type":46,"tag":54,"props":721,"children":722},{},[723,725,731],{"type":51,"value":724},"Generated files go into ",{"type":46,"tag":77,"props":726,"children":728},{"className":727},[],[729],{"type":51,"value":730},"__generated__\u002F",{"type":51,"value":732}," directories next to the source files.\nNever edit these files — they are overwritten on every compiler run. If you see\ntype errors about missing generated types, run the compiler first — the\ntypes are likely just out of date.",{"type":46,"tag":65,"props":734,"children":736},{"id":735},"lint-rules",[737],{"type":51,"value":738},"Lint Rules",{"type":46,"tag":54,"props":740,"children":741},{},[742,744,750],{"type":51,"value":743},"Relay's ESLint plugin (",{"type":46,"tag":77,"props":745,"children":747},{"className":746},[],[748],{"type":51,"value":749},"eslint-plugin-relay",{"type":51,"value":751},") is a key part of the developer\nexperience. Two rules are especially important:",{"type":46,"tag":534,"props":753,"children":754},{},[755,766],{"type":46,"tag":538,"props":756,"children":757},{},[758,764],{"type":46,"tag":77,"props":759,"children":761},{"className":760},[],[762],{"type":51,"value":763},"relay\u002Funused-fields",{"type":51,"value":765}," — detects GraphQL fields that are selected in a\nfragment but never read in the component. This prevents the \"append-only\nquery\" problem where fragments accumulate unused fields over time, fetching\ndata no component actually needs.",{"type":46,"tag":538,"props":767,"children":768},{},[769,775,777,783],{"type":46,"tag":77,"props":770,"children":772},{"className":771},[],[773],{"type":51,"value":774},"relay\u002Fno-future-added-value",{"type":51,"value":776}," — prevents explicitly handling the\n",{"type":46,"tag":77,"props":778,"children":780},{"className":779},[],[781],{"type":51,"value":782},"\"%future added value\"",{"type":51,"value":784}," enum placeholder that Relay inserts to ensure you\nhandle the possibility of new enum variants being added by the server.",{"type":46,"tag":54,"props":786,"children":787},{},[788,789,795],{"type":51,"value":655},{"type":46,"tag":77,"props":790,"children":792},{"className":791},[],[793],{"type":51,"value":794},"\u003Cllm-docs>\u002Fgetting-started\u002Flint-rules.mdx",{"type":51,"value":796}," for installation and configuration.",{"type":46,"tag":65,"props":798,"children":800},{"id":799},"decision-rules",[801],{"type":51,"value":802},"Decision Rules",{"type":46,"tag":607,"props":804,"children":806},{"id":805},"queries-belong-at-roots-never-in-hooks",[807],{"type":51,"value":808},"Queries belong at roots — never in hooks",{"type":46,"tag":54,"props":810,"children":811},{},[812,814,819,821,826],{"type":51,"value":813},"Queries belong at ",{"type":46,"tag":499,"props":815,"children":816},{},[817],{"type":51,"value":818},"route entrypoints",{"type":51,"value":820}," (the top-level component for a URL).\nHooks are leaves — reused across many components. A query inside a hook fires\nlate (after the hook's host renders) AND duplicates across every caller. The fix\nis always: accept a fragment key as a parameter, use ",{"type":46,"tag":77,"props":822,"children":824},{"className":823},[],[825],{"type":51,"value":247},{"type":51,"value":92},{"type":46,"tag":54,"props":828,"children":829},{},[830,832,837,839,844,846,851,853,859,861,867],{"type":51,"value":831},"Use ",{"type":46,"tag":77,"props":833,"children":835},{"className":834},[],[836],{"type":51,"value":268},{"type":51,"value":838}," + ",{"type":46,"tag":77,"props":840,"children":842},{"className":841},[],[843],{"type":51,"value":289},{"type":51,"value":845}," (or ",{"type":46,"tag":77,"props":847,"children":849},{"className":848},[],[850],{"type":51,"value":297},{"type":51,"value":852},"). Start the fetch in\nan event handler, route transition, or during app initialization — before the\ncomponent renders. ",{"type":46,"tag":77,"props":854,"children":856},{"className":855},[],[857],{"type":51,"value":858},"useLazyLoadQuery",{"type":51,"value":860}," does not start fetching until render,\ncreating waterfalls. See ",{"type":46,"tag":77,"props":862,"children":864},{"className":863},[],[865],{"type":51,"value":866},"\u003Cllm-docs>\u002Fguided-tour\u002Frendering\u002Fqueries.mdx",{"type":51,"value":868}," for the full pattern.",{"type":46,"tag":607,"props":870,"children":872},{"id":871},"before-fixing-where-a-query-lives-ask-if-it-should-exist",[873],{"type":51,"value":874},"Before fixing where a query lives, ask if it should exist",{"type":46,"tag":107,"props":876,"children":877},{},[878,894],{"type":46,"tag":111,"props":879,"children":880},{},[881],{"type":46,"tag":115,"props":882,"children":883},{},[884,889],{"type":46,"tag":119,"props":885,"children":886},{},[887],{"type":51,"value":888},"Question",{"type":46,"tag":119,"props":890,"children":891},{},[892],{"type":51,"value":893},"If YES",{"type":46,"tag":130,"props":895,"children":896},{},[897,915,928,941,954,974],{"type":46,"tag":115,"props":898,"children":899},{},[900,905],{"type":46,"tag":137,"props":901,"children":902},{},[903],{"type":51,"value":904},"Parent already fetches this GraphQL type?",{"type":46,"tag":137,"props":906,"children":907},{},[908,910],{"type":51,"value":909},"Delete query, use ",{"type":46,"tag":77,"props":911,"children":913},{"className":912},[],[914],{"type":51,"value":247},{"type":46,"tag":115,"props":916,"children":917},{},[918,923],{"type":46,"tag":137,"props":919,"children":920},{},[921],{"type":51,"value":922},"Component only fetches and passes data down?",{"type":46,"tag":137,"props":924,"children":925},{},[926],{"type":51,"value":927},"Delete the wrapper component entirely (loader anti-pattern)",{"type":46,"tag":115,"props":929,"children":930},{},[931,936],{"type":46,"tag":137,"props":932,"children":933},{},[934],{"type":51,"value":935},"Query is inside a custom hook?",{"type":46,"tag":137,"props":937,"children":938},{},[939],{"type":51,"value":940},"Delete query, accept a fragment key param",{"type":46,"tag":115,"props":942,"children":943},{},[944,949],{"type":46,"tag":137,"props":945,"children":946},{},[947],{"type":51,"value":948},"Two components fetch the same data?",{"type":46,"tag":137,"props":950,"children":951},{},[952],{"type":51,"value":953},"Delete one query, fetch in a common ancestor",{"type":46,"tag":115,"props":955,"children":956},{},[957,962],{"type":46,"tag":137,"props":958,"children":959},{},[960],{"type":51,"value":961},"Data is only used for logging\u002Fanalytics?",{"type":46,"tag":137,"props":963,"children":964},{},[965,967,972],{"type":51,"value":966},"Move to ",{"type":46,"tag":77,"props":968,"children":970},{"className":969},[],[971],{"type":51,"value":511},{"type":51,"value":973}," or server-side logging",{"type":46,"tag":115,"props":975,"children":976},{},[977,982],{"type":46,"tag":137,"props":978,"children":979},{},[980],{"type":51,"value":981},"Data is static config (same for every user)?",{"type":46,"tag":137,"props":983,"children":984},{},[985],{"type":51,"value":986},"Inject server-side, no round-trip needed",{"type":46,"tag":607,"props":988,"children":990},{"id":989},"loadquery-in-useeffect-is-worse-than-uselazyloadquery",[991,996,998,1004,1006],{"type":46,"tag":77,"props":992,"children":994},{"className":993},[],[995],{"type":51,"value":297},{"type":51,"value":997}," in ",{"type":46,"tag":77,"props":999,"children":1001},{"className":1000},[],[1002],{"type":51,"value":1003},"useEffect",{"type":51,"value":1005}," is worse than ",{"type":46,"tag":77,"props":1007,"children":1009},{"className":1008},[],[1010],{"type":51,"value":858},{"type":46,"tag":54,"props":1012,"children":1013},{},[1014,1019,1021,1026,1028,1033,1035,1040],{"type":46,"tag":77,"props":1015,"children":1017},{"className":1016},[],[1018],{"type":51,"value":1003},{"type":51,"value":1020}," runs ",{"type":46,"tag":499,"props":1022,"children":1023},{},[1024],{"type":51,"value":1025},"after paint",{"type":51,"value":1027},", so the fetch starts even later than\n",{"type":46,"tag":77,"props":1029,"children":1031},{"className":1030},[],[1032],{"type":51,"value":858},{"type":51,"value":1034}," (which at least starts during render). Call ",{"type":46,"tag":77,"props":1036,"children":1038},{"className":1037},[],[1039],{"type":51,"value":297},{"type":51,"value":1041}," in\nevent handlers, route transitions, or app initialization — never in effects.",{"type":46,"tag":607,"props":1043,"children":1045},{"id":1044},"walking-the-ancestor-tree-for-usefragment",[1046,1048],{"type":51,"value":1047},"Walking the ancestor tree for ",{"type":46,"tag":77,"props":1049,"children":1051},{"className":1050},[],[1052],{"type":51,"value":247},{"type":46,"tag":54,"props":1054,"children":1055},{},[1056,1058,1063,1065,1070],{"type":51,"value":1057},"When deciding whether ",{"type":46,"tag":77,"props":1059,"children":1061},{"className":1060},[],[1062],{"type":51,"value":247},{"type":51,"value":1064}," can replace a query, walk ",{"type":46,"tag":499,"props":1066,"children":1067},{},[1068],{"type":51,"value":1069},"up",{"type":51,"value":1071}," the\ncomponent tree from your component. Stop at: route boundaries, feature gates,\nconditional renders, or user-triggered interactions. Keep walking through:\nunconditional renders, layout\u002Fwrapper components, context providers.",{"type":46,"tag":54,"props":1073,"children":1074},{},[1075,1077,1082],{"type":51,"value":1076},"If any ancestor already queries the GraphQL type you need, ",{"type":46,"tag":77,"props":1078,"children":1080},{"className":1079},[],[1081],{"type":51,"value":247},{"type":51,"value":1083}," is\nthe answer. Threading fragment keys through several layers of props is fine —\nit IS the correct pattern.",{"type":46,"tag":607,"props":1085,"children":1087},{"id":1086},"dont-default-query-variables-to-empty-values",[1088],{"type":51,"value":1089},"Don't default query variables to empty values",{"type":46,"tag":54,"props":1091,"children":1092},{},[1093,1095,1101,1103,1109,1111,1117,1119,1125,1127,1133,1135,1141],{"type":51,"value":1094},"Never default a query variable to ",{"type":46,"tag":77,"props":1096,"children":1098},{"className":1097},[],[1099],{"type":51,"value":1100},"''",{"type":51,"value":1102},", ",{"type":46,"tag":77,"props":1104,"children":1106},{"className":1105},[],[1107],{"type":51,"value":1108},"0",{"type":51,"value":1110},", or ",{"type":46,"tag":77,"props":1112,"children":1114},{"className":1113},[],[1115],{"type":51,"value":1116},"null",{"type":51,"value":1118}," when the real value is\nunavailable. This fires the query with bad data, returning wrong results or\nerrors. Instead, use conditional rendering (",{"type":46,"tag":77,"props":1120,"children":1122},{"className":1121},[],[1123],{"type":51,"value":1124},"if (!id) return null",{"type":51,"value":1126},") or\n",{"type":46,"tag":77,"props":1128,"children":1130},{"className":1129},[],[1131],{"type":51,"value":1132},"@include",{"type":51,"value":1134},"\u002F",{"type":46,"tag":77,"props":1136,"children":1138},{"className":1137},[],[1139],{"type":51,"value":1140},"@skip",{"type":51,"value":1142}," directives to omit the field entirely.",{"type":46,"tag":607,"props":1144,"children":1146},{"id":1145},"data-flow-always-use-fragments",[1147],{"type":51,"value":1148},"Data flow: Always use fragments",{"type":46,"tag":54,"props":1150,"children":1151},{},[1152,1154,1160],{"type":51,"value":1153},"Every component that displays server data should declare a fragment and receive\na fragment reference (the ",{"type":46,"tag":77,"props":1155,"children":1157},{"className":1156},[],[1158],{"type":51,"value":1159},"$key",{"type":51,"value":1161}," type) as a prop. The parent spreads the\nchild's fragment in its own query or fragment and passes the result down. See\nthe \"Maintain fragment co-location\" anti-pattern below for an example.",{"type":46,"tag":607,"props":1163,"children":1165},{"id":1164},"mutations-spread-fragments-into-responses",[1166],{"type":51,"value":1167},"Mutations: Spread fragments into responses",{"type":46,"tag":54,"props":1169,"children":1170},{},[1171],{"type":51,"value":1172},"Spread the consuming component's fragment into the mutation response rather than\nselecting fields individually. This keeps them in sync automatically. See the\nanti-pattern example below.",{"type":46,"tag":607,"props":1174,"children":1176},{"id":1175},"error-handling-use-throwonfielderror-and-catch",[1177,1179,1184,1186],{"type":51,"value":1178},"Error handling: Use ",{"type":46,"tag":77,"props":1180,"children":1182},{"className":1181},[],[1183],{"type":51,"value":360},{"type":51,"value":1185}," and ",{"type":46,"tag":77,"props":1187,"children":1189},{"className":1188},[],[1190],{"type":51,"value":381},{"type":46,"tag":54,"props":1192,"children":1193},{},[1194,1196,1201,1203,1209],{"type":51,"value":1195},"The recommended approach for handling field errors and nullability is to add\n",{"type":46,"tag":77,"props":1197,"children":1199},{"className":1198},[],[1200],{"type":51,"value":360},{"type":51,"value":1202}," to your fragment or query. This causes Relay to throw a\nJavaScript exception if a field error is encountered, which can be caught by a\nReact error boundary. It also enables non-null types for ",{"type":46,"tag":77,"props":1204,"children":1206},{"className":1205},[],[1207],{"type":51,"value":1208},"@semanticNonNull",{"type":51,"value":1210},"\nfields, eliminating unnecessary null checks. Note that this pattern depends on\nReact error boundaries being configured in your application — proceed with\ncaution if error boundaries are not set up robustly.",{"type":46,"tag":54,"props":1212,"children":1213},{},[1214,1216,1221,1223,1229],{"type":51,"value":1215},"For fields where you want to handle errors locally instead of throwing, use\n",{"type":46,"tag":77,"props":1217,"children":1219},{"className":1218},[],[1220],{"type":51,"value":381},{"type":51,"value":1222}," to receive errors inline as ",{"type":46,"tag":77,"props":1224,"children":1226},{"className":1225},[],[1227],{"type":51,"value":1228},"{ ok: true, value: T } | { ok: false, errors: [...] }",{"type":51,"value":92},{"type":46,"tag":54,"props":1231,"children":1232},{},[1233,1239,1241,1246,1247,1252],{"type":46,"tag":77,"props":1234,"children":1236},{"className":1235},[],[1237],{"type":51,"value":1238},"@required",{"type":51,"value":1240}," is also available for declaring that specific fields must be\nnon-null, but ",{"type":46,"tag":77,"props":1242,"children":1244},{"className":1243},[],[1245],{"type":51,"value":360},{"type":51,"value":838},{"type":46,"tag":77,"props":1248,"children":1250},{"className":1249},[],[1251],{"type":51,"value":381},{"type":51,"value":1253}," is the preferred pattern for new\ncode.",{"type":46,"tag":54,"props":1255,"children":1256},{},[1257,1258,1264,1265,1271,1273,1279],{"type":51,"value":655},{"type":46,"tag":77,"props":1259,"children":1261},{"className":1260},[],[1262],{"type":51,"value":1263},"\u003Cllm-docs>\u002Fguides\u002Fthrow-on-field-error-directive.mdx",{"type":51,"value":1102},{"type":46,"tag":77,"props":1266,"children":1268},{"className":1267},[],[1269],{"type":51,"value":1270},"\u003Cllm-docs>\u002Fguides\u002Fcatch-directive.mdx",{"type":51,"value":1272},",\nand ",{"type":46,"tag":77,"props":1274,"children":1276},{"className":1275},[],[1277],{"type":51,"value":1278},"\u003Cllm-docs>\u002Fguides\u002Fsemantic-nullability.mdx",{"type":51,"value":92},{"type":46,"tag":607,"props":1281,"children":1283},{"id":1282},"pagination-use-the-three-directive-pattern",[1284],{"type":51,"value":1285},"Pagination: Use the three-directive pattern",{"type":46,"tag":54,"props":1287,"children":1288},{},[1289,1291,1297,1299,1305,1307,1313,1315,1321],{"type":51,"value":1290},"Always use ",{"type":46,"tag":77,"props":1292,"children":1294},{"className":1293},[],[1295],{"type":51,"value":1296},"@argumentDefinitions",{"type":51,"value":1298}," (for cursor\u002Fcount variables),\n",{"type":46,"tag":77,"props":1300,"children":1302},{"className":1301},[],[1303],{"type":51,"value":1304},"@refetchable",{"type":51,"value":1306}," (to auto-generate the pagination query), and ",{"type":46,"tag":77,"props":1308,"children":1310},{"className":1309},[],[1311],{"type":51,"value":1312},"@connection",{"type":51,"value":1314}," (to\nidentify the connection for store management) together. Never write manual\npagination queries. See ",{"type":46,"tag":77,"props":1316,"children":1318},{"className":1317},[],[1319],{"type":51,"value":1320},"\u003Cllm-docs>\u002Fguided-tour\u002Flist-data\u002Fpagination.mdx",{"type":51,"value":92},{"type":46,"tag":607,"props":1323,"children":1325},{"id":1324},"client-state-prefer-relay-resolvers",[1326],{"type":51,"value":1327},"Client state: Prefer Relay Resolvers",{"type":46,"tag":54,"props":1329,"children":1330},{},[1331,1333,1339],{"type":51,"value":1332},"When multiple components need to read client-side data, use Relay Resolvers to\ndefine client-only fields on the GraphQL schema rather than prop-drilling or\nReact context. This gives client state the same composability and caching\nguarantees as server data. Use ",{"type":46,"tag":77,"props":1334,"children":1336},{"className":1335},[],[1337],{"type":51,"value":1338},"useClientQuery",{"type":51,"value":1340}," for queries that read only\nresolver-defined fields.",{"type":46,"tag":54,"props":1342,"children":1343},{},[1344,1345,1351],{"type":51,"value":655},{"type":46,"tag":77,"props":1346,"children":1348},{"className":1347},[],[1349],{"type":51,"value":1350},"\u003Cllm-docs>\u002Fguides\u002Frelay-resolvers\u002Fintroduction.mdx",{"type":51,"value":1352}," for how to define resolvers.",{"type":46,"tag":65,"props":1354,"children":1356},{"id":1355},"critical-anti-patterns",[1357],{"type":51,"value":1358},"Critical Anti-Patterns",{"type":46,"tag":607,"props":1360,"children":1362},{"id":1361},"never-copy-relay-data-into-react-state",[1363],{"type":51,"value":1364},"Never copy Relay data into React state",{"type":46,"tag":54,"props":1366,"children":1367},{},[1368,1370,1375,1377,1383,1385,1391],{"type":51,"value":1369},"This is the single most important rule. Do not read data from ",{"type":46,"tag":77,"props":1371,"children":1373},{"className":1372},[],[1374],{"type":51,"value":247},{"type":51,"value":1376}," and\ncopy it into ",{"type":46,"tag":77,"props":1378,"children":1380},{"className":1379},[],[1381],{"type":51,"value":1382},"useState",{"type":51,"value":1384},", and do not update that state manually in mutation\n",{"type":46,"tag":77,"props":1386,"children":1388},{"className":1387},[],[1389],{"type":51,"value":1390},"onCompleted",{"type":51,"value":1392}," callbacks.",{"type":46,"tag":1394,"props":1395,"children":1400},"pre",{"className":1396,"code":1397,"language":1398,"meta":1399,"style":1399},"language-tsx shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F WRONG: Copying Relay data into React state\nfunction UserProfile({userKey}) {\n  const data = useFragment(UserProfileFragment, userKey);\n  const [name, setName] = useState(data.name); \u002F\u002F broken\n\n  const [commit] = useMutation(UpdateNameMutation);\n  const handleSave = (newName) => {\n    commit({\n      variables: {name: newName},\n      onCompleted: (response) => {\n        setName(response.updateName.user.name); \u002F\u002F broken\n      },\n    });\n  };\n  return \u003Cspan>{name}\u003C\u002Fspan>;\n}\n","tsx","",[1401],{"type":46,"tag":77,"props":1402,"children":1403},{"__ignoreMap":1399},[1404,1416,1454,1510,1582,1592,1639,1679,1697,1734,1768,1823,1832,1849,1858,1900],{"type":46,"tag":1405,"props":1406,"children":1409},"span",{"class":1407,"line":1408},"line",1,[1410],{"type":46,"tag":1405,"props":1411,"children":1413},{"style":1412},"--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic",[1414],{"type":51,"value":1415},"\u002F\u002F WRONG: Copying Relay data into React state\n",{"type":46,"tag":1405,"props":1417,"children":1419},{"class":1407,"line":1418},2,[1420,1426,1432,1438,1444,1449],{"type":46,"tag":1405,"props":1421,"children":1423},{"style":1422},"--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA",[1424],{"type":51,"value":1425},"function",{"type":46,"tag":1405,"props":1427,"children":1429},{"style":1428},"--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF",[1430],{"type":51,"value":1431}," UserProfile",{"type":46,"tag":1405,"props":1433,"children":1435},{"style":1434},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF",[1436],{"type":51,"value":1437},"({",{"type":46,"tag":1405,"props":1439,"children":1441},{"style":1440},"--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic",[1442],{"type":51,"value":1443},"userKey",{"type":46,"tag":1405,"props":1445,"children":1446},{"style":1434},[1447],{"type":51,"value":1448},"})",{"type":46,"tag":1405,"props":1450,"children":1451},{"style":1434},[1452],{"type":51,"value":1453}," {\n",{"type":46,"tag":1405,"props":1455,"children":1457},{"class":1407,"line":1456},3,[1458,1463,1469,1474,1479,1485,1490,1495,1500,1505],{"type":46,"tag":1405,"props":1459,"children":1460},{"style":1422},[1461],{"type":51,"value":1462},"  const",{"type":46,"tag":1405,"props":1464,"children":1466},{"style":1465},"--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8",[1467],{"type":51,"value":1468}," data",{"type":46,"tag":1405,"props":1470,"children":1471},{"style":1434},[1472],{"type":51,"value":1473}," =",{"type":46,"tag":1405,"props":1475,"children":1476},{"style":1428},[1477],{"type":51,"value":1478}," useFragment",{"type":46,"tag":1405,"props":1480,"children":1482},{"style":1481},"--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178",[1483],{"type":51,"value":1484},"(",{"type":46,"tag":1405,"props":1486,"children":1487},{"style":1465},[1488],{"type":51,"value":1489},"UserProfileFragment",{"type":46,"tag":1405,"props":1491,"children":1492},{"style":1434},[1493],{"type":51,"value":1494},",",{"type":46,"tag":1405,"props":1496,"children":1497},{"style":1465},[1498],{"type":51,"value":1499}," userKey",{"type":46,"tag":1405,"props":1501,"children":1502},{"style":1481},[1503],{"type":51,"value":1504},")",{"type":46,"tag":1405,"props":1506,"children":1507},{"style":1434},[1508],{"type":51,"value":1509},";\n",{"type":46,"tag":1405,"props":1511,"children":1513},{"class":1407,"line":1512},4,[1514,1518,1523,1528,1532,1537,1542,1546,1551,1555,1560,1564,1568,1572,1577],{"type":46,"tag":1405,"props":1515,"children":1516},{"style":1422},[1517],{"type":51,"value":1462},{"type":46,"tag":1405,"props":1519,"children":1520},{"style":1434},[1521],{"type":51,"value":1522}," [",{"type":46,"tag":1405,"props":1524,"children":1525},{"style":1465},[1526],{"type":51,"value":1527},"name",{"type":46,"tag":1405,"props":1529,"children":1530},{"style":1434},[1531],{"type":51,"value":1494},{"type":46,"tag":1405,"props":1533,"children":1534},{"style":1465},[1535],{"type":51,"value":1536}," setName",{"type":46,"tag":1405,"props":1538,"children":1539},{"style":1434},[1540],{"type":51,"value":1541},"]",{"type":46,"tag":1405,"props":1543,"children":1544},{"style":1434},[1545],{"type":51,"value":1473},{"type":46,"tag":1405,"props":1547,"children":1548},{"style":1428},[1549],{"type":51,"value":1550}," useState",{"type":46,"tag":1405,"props":1552,"children":1553},{"style":1481},[1554],{"type":51,"value":1484},{"type":46,"tag":1405,"props":1556,"children":1557},{"style":1465},[1558],{"type":51,"value":1559},"data",{"type":46,"tag":1405,"props":1561,"children":1562},{"style":1434},[1563],{"type":51,"value":92},{"type":46,"tag":1405,"props":1565,"children":1566},{"style":1465},[1567],{"type":51,"value":1527},{"type":46,"tag":1405,"props":1569,"children":1570},{"style":1481},[1571],{"type":51,"value":1504},{"type":46,"tag":1405,"props":1573,"children":1574},{"style":1434},[1575],{"type":51,"value":1576},";",{"type":46,"tag":1405,"props":1578,"children":1579},{"style":1412},[1580],{"type":51,"value":1581}," \u002F\u002F broken\n",{"type":46,"tag":1405,"props":1583,"children":1585},{"class":1407,"line":1584},5,[1586],{"type":46,"tag":1405,"props":1587,"children":1589},{"emptyLinePlaceholder":1588},true,[1590],{"type":51,"value":1591},"\n",{"type":46,"tag":1405,"props":1593,"children":1595},{"class":1407,"line":1594},6,[1596,1600,1604,1609,1613,1617,1622,1626,1631,1635],{"type":46,"tag":1405,"props":1597,"children":1598},{"style":1422},[1599],{"type":51,"value":1462},{"type":46,"tag":1405,"props":1601,"children":1602},{"style":1434},[1603],{"type":51,"value":1522},{"type":46,"tag":1405,"props":1605,"children":1606},{"style":1465},[1607],{"type":51,"value":1608},"commit",{"type":46,"tag":1405,"props":1610,"children":1611},{"style":1434},[1612],{"type":51,"value":1541},{"type":46,"tag":1405,"props":1614,"children":1615},{"style":1434},[1616],{"type":51,"value":1473},{"type":46,"tag":1405,"props":1618,"children":1619},{"style":1428},[1620],{"type":51,"value":1621}," useMutation",{"type":46,"tag":1405,"props":1623,"children":1624},{"style":1481},[1625],{"type":51,"value":1484},{"type":46,"tag":1405,"props":1627,"children":1628},{"style":1465},[1629],{"type":51,"value":1630},"UpdateNameMutation",{"type":46,"tag":1405,"props":1632,"children":1633},{"style":1481},[1634],{"type":51,"value":1504},{"type":46,"tag":1405,"props":1636,"children":1637},{"style":1434},[1638],{"type":51,"value":1509},{"type":46,"tag":1405,"props":1640,"children":1642},{"class":1407,"line":1641},7,[1643,1647,1652,1656,1661,1666,1670,1675],{"type":46,"tag":1405,"props":1644,"children":1645},{"style":1422},[1646],{"type":51,"value":1462},{"type":46,"tag":1405,"props":1648,"children":1649},{"style":1465},[1650],{"type":51,"value":1651}," handleSave",{"type":46,"tag":1405,"props":1653,"children":1654},{"style":1434},[1655],{"type":51,"value":1473},{"type":46,"tag":1405,"props":1657,"children":1658},{"style":1434},[1659],{"type":51,"value":1660}," (",{"type":46,"tag":1405,"props":1662,"children":1663},{"style":1440},[1664],{"type":51,"value":1665},"newName",{"type":46,"tag":1405,"props":1667,"children":1668},{"style":1434},[1669],{"type":51,"value":1504},{"type":46,"tag":1405,"props":1671,"children":1672},{"style":1422},[1673],{"type":51,"value":1674}," =>",{"type":46,"tag":1405,"props":1676,"children":1677},{"style":1434},[1678],{"type":51,"value":1453},{"type":46,"tag":1405,"props":1680,"children":1682},{"class":1407,"line":1681},8,[1683,1688,1692],{"type":46,"tag":1405,"props":1684,"children":1685},{"style":1428},[1686],{"type":51,"value":1687},"    commit",{"type":46,"tag":1405,"props":1689,"children":1690},{"style":1481},[1691],{"type":51,"value":1484},{"type":46,"tag":1405,"props":1693,"children":1694},{"style":1434},[1695],{"type":51,"value":1696},"{\n",{"type":46,"tag":1405,"props":1698,"children":1700},{"class":1407,"line":1699},9,[1701,1706,1711,1716,1720,1724,1729],{"type":46,"tag":1405,"props":1702,"children":1703},{"style":1481},[1704],{"type":51,"value":1705},"      variables",{"type":46,"tag":1405,"props":1707,"children":1708},{"style":1434},[1709],{"type":51,"value":1710},":",{"type":46,"tag":1405,"props":1712,"children":1713},{"style":1434},[1714],{"type":51,"value":1715}," {",{"type":46,"tag":1405,"props":1717,"children":1718},{"style":1481},[1719],{"type":51,"value":1527},{"type":46,"tag":1405,"props":1721,"children":1722},{"style":1434},[1723],{"type":51,"value":1710},{"type":46,"tag":1405,"props":1725,"children":1726},{"style":1465},[1727],{"type":51,"value":1728}," newName",{"type":46,"tag":1405,"props":1730,"children":1731},{"style":1434},[1732],{"type":51,"value":1733},"},\n",{"type":46,"tag":1405,"props":1735,"children":1737},{"class":1407,"line":1736},10,[1738,1743,1747,1751,1756,1760,1764],{"type":46,"tag":1405,"props":1739,"children":1740},{"style":1428},[1741],{"type":51,"value":1742},"      onCompleted",{"type":46,"tag":1405,"props":1744,"children":1745},{"style":1434},[1746],{"type":51,"value":1710},{"type":46,"tag":1405,"props":1748,"children":1749},{"style":1434},[1750],{"type":51,"value":1660},{"type":46,"tag":1405,"props":1752,"children":1753},{"style":1440},[1754],{"type":51,"value":1755},"response",{"type":46,"tag":1405,"props":1757,"children":1758},{"style":1434},[1759],{"type":51,"value":1504},{"type":46,"tag":1405,"props":1761,"children":1762},{"style":1422},[1763],{"type":51,"value":1674},{"type":46,"tag":1405,"props":1765,"children":1766},{"style":1434},[1767],{"type":51,"value":1453},{"type":46,"tag":1405,"props":1769,"children":1771},{"class":1407,"line":1770},11,[1772,1777,1781,1785,1789,1794,1798,1803,1807,1811,1815,1819],{"type":46,"tag":1405,"props":1773,"children":1774},{"style":1428},[1775],{"type":51,"value":1776},"        setName",{"type":46,"tag":1405,"props":1778,"children":1779},{"style":1481},[1780],{"type":51,"value":1484},{"type":46,"tag":1405,"props":1782,"children":1783},{"style":1465},[1784],{"type":51,"value":1755},{"type":46,"tag":1405,"props":1786,"children":1787},{"style":1434},[1788],{"type":51,"value":92},{"type":46,"tag":1405,"props":1790,"children":1791},{"style":1465},[1792],{"type":51,"value":1793},"updateName",{"type":46,"tag":1405,"props":1795,"children":1796},{"style":1434},[1797],{"type":51,"value":92},{"type":46,"tag":1405,"props":1799,"children":1800},{"style":1465},[1801],{"type":51,"value":1802},"user",{"type":46,"tag":1405,"props":1804,"children":1805},{"style":1434},[1806],{"type":51,"value":92},{"type":46,"tag":1405,"props":1808,"children":1809},{"style":1465},[1810],{"type":51,"value":1527},{"type":46,"tag":1405,"props":1812,"children":1813},{"style":1481},[1814],{"type":51,"value":1504},{"type":46,"tag":1405,"props":1816,"children":1817},{"style":1434},[1818],{"type":51,"value":1576},{"type":46,"tag":1405,"props":1820,"children":1821},{"style":1412},[1822],{"type":51,"value":1581},{"type":46,"tag":1405,"props":1824,"children":1826},{"class":1407,"line":1825},12,[1827],{"type":46,"tag":1405,"props":1828,"children":1829},{"style":1434},[1830],{"type":51,"value":1831},"      },\n",{"type":46,"tag":1405,"props":1833,"children":1835},{"class":1407,"line":1834},13,[1836,1841,1845],{"type":46,"tag":1405,"props":1837,"children":1838},{"style":1434},[1839],{"type":51,"value":1840},"    }",{"type":46,"tag":1405,"props":1842,"children":1843},{"style":1481},[1844],{"type":51,"value":1504},{"type":46,"tag":1405,"props":1846,"children":1847},{"style":1434},[1848],{"type":51,"value":1509},{"type":46,"tag":1405,"props":1850,"children":1852},{"class":1407,"line":1851},14,[1853],{"type":46,"tag":1405,"props":1854,"children":1855},{"style":1434},[1856],{"type":51,"value":1857},"  };\n",{"type":46,"tag":1405,"props":1859,"children":1861},{"class":1407,"line":1860},15,[1862,1868,1873,1877,1882,1886,1891,1895],{"type":46,"tag":1405,"props":1863,"children":1865},{"style":1864},"--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic",[1866],{"type":51,"value":1867},"  return",{"type":46,"tag":1405,"props":1869,"children":1870},{"style":1434},[1871],{"type":51,"value":1872}," \u003C",{"type":46,"tag":1405,"props":1874,"children":1875},{"style":1481},[1876],{"type":51,"value":1405},{"type":46,"tag":1405,"props":1878,"children":1879},{"style":1434},[1880],{"type":51,"value":1881},">{",{"type":46,"tag":1405,"props":1883,"children":1884},{"style":1465},[1885],{"type":51,"value":1527},{"type":46,"tag":1405,"props":1887,"children":1888},{"style":1434},[1889],{"type":51,"value":1890},"}\u003C\u002F",{"type":46,"tag":1405,"props":1892,"children":1893},{"style":1481},[1894],{"type":51,"value":1405},{"type":46,"tag":1405,"props":1896,"children":1897},{"style":1434},[1898],{"type":51,"value":1899},">;\n",{"type":46,"tag":1405,"props":1901,"children":1903},{"class":1407,"line":1902},16,[1904],{"type":46,"tag":1405,"props":1905,"children":1906},{"style":1434},[1907],{"type":51,"value":1908},"}\n",{"type":46,"tag":54,"props":1910,"children":1911},{},[1912,1914,1920,1922,1927,1929,1934],{"type":51,"value":1913},"Why this is wrong: Relay's normalized store is the single source of truth. When\na mutation returns updated data with a matching ",{"type":46,"tag":77,"props":1915,"children":1917},{"className":1916},[],[1918],{"type":51,"value":1919},"id",{"type":51,"value":1921},", Relay automatically\nupdates every component reading that data via ",{"type":46,"tag":77,"props":1923,"children":1925},{"className":1924},[],[1926],{"type":51,"value":247},{"type":51,"value":1928},". By copying into\n",{"type":46,"tag":77,"props":1930,"children":1932},{"className":1931},[],[1933],{"type":51,"value":1382},{"type":51,"value":1935},", you create a second source of truth that Relay cannot update. The\ncomponent will show stale data whenever the record is updated by another\nmutation, subscription, or refetch elsewhere in the app.",{"type":46,"tag":1394,"props":1937,"children":1939},{"className":1396,"code":1938,"language":1398,"meta":1399,"style":1399},"\u002F\u002F CORRECT: Read directly from the fragment\nfunction UserProfile({userKey}) {\n  const data = useFragment(UserProfileFragment, userKey);\n  const [commit, isInFlight] = useMutation(UpdateNameMutation);\n\n  const handleSave = (newName) => {\n    commit({variables: {name: newName}});\n    \u002F\u002F No onCompleted needed — Relay updates the store automatically,\n    \u002F\u002F and useFragment re-renders this component with the new data.\n  };\n  return \u003Cspan>{data.name}\u003C\u002Fspan>;\n}\n",[1940],{"type":46,"tag":77,"props":1941,"children":1942},{"__ignoreMap":1399},[1943,1951,1978,2021,2073,2080,2115,2169,2177,2185,2192,2235],{"type":46,"tag":1405,"props":1944,"children":1945},{"class":1407,"line":1408},[1946],{"type":46,"tag":1405,"props":1947,"children":1948},{"style":1412},[1949],{"type":51,"value":1950},"\u002F\u002F CORRECT: Read directly from the fragment\n",{"type":46,"tag":1405,"props":1952,"children":1953},{"class":1407,"line":1418},[1954,1958,1962,1966,1970,1974],{"type":46,"tag":1405,"props":1955,"children":1956},{"style":1422},[1957],{"type":51,"value":1425},{"type":46,"tag":1405,"props":1959,"children":1960},{"style":1428},[1961],{"type":51,"value":1431},{"type":46,"tag":1405,"props":1963,"children":1964},{"style":1434},[1965],{"type":51,"value":1437},{"type":46,"tag":1405,"props":1967,"children":1968},{"style":1440},[1969],{"type":51,"value":1443},{"type":46,"tag":1405,"props":1971,"children":1972},{"style":1434},[1973],{"type":51,"value":1448},{"type":46,"tag":1405,"props":1975,"children":1976},{"style":1434},[1977],{"type":51,"value":1453},{"type":46,"tag":1405,"props":1979,"children":1980},{"class":1407,"line":1456},[1981,1985,1989,1993,1997,2001,2005,2009,2013,2017],{"type":46,"tag":1405,"props":1982,"children":1983},{"style":1422},[1984],{"type":51,"value":1462},{"type":46,"tag":1405,"props":1986,"children":1987},{"style":1465},[1988],{"type":51,"value":1468},{"type":46,"tag":1405,"props":1990,"children":1991},{"style":1434},[1992],{"type":51,"value":1473},{"type":46,"tag":1405,"props":1994,"children":1995},{"style":1428},[1996],{"type":51,"value":1478},{"type":46,"tag":1405,"props":1998,"children":1999},{"style":1481},[2000],{"type":51,"value":1484},{"type":46,"tag":1405,"props":2002,"children":2003},{"style":1465},[2004],{"type":51,"value":1489},{"type":46,"tag":1405,"props":2006,"children":2007},{"style":1434},[2008],{"type":51,"value":1494},{"type":46,"tag":1405,"props":2010,"children":2011},{"style":1465},[2012],{"type":51,"value":1499},{"type":46,"tag":1405,"props":2014,"children":2015},{"style":1481},[2016],{"type":51,"value":1504},{"type":46,"tag":1405,"props":2018,"children":2019},{"style":1434},[2020],{"type":51,"value":1509},{"type":46,"tag":1405,"props":2022,"children":2023},{"class":1407,"line":1512},[2024,2028,2032,2036,2040,2045,2049,2053,2057,2061,2065,2069],{"type":46,"tag":1405,"props":2025,"children":2026},{"style":1422},[2027],{"type":51,"value":1462},{"type":46,"tag":1405,"props":2029,"children":2030},{"style":1434},[2031],{"type":51,"value":1522},{"type":46,"tag":1405,"props":2033,"children":2034},{"style":1465},[2035],{"type":51,"value":1608},{"type":46,"tag":1405,"props":2037,"children":2038},{"style":1434},[2039],{"type":51,"value":1494},{"type":46,"tag":1405,"props":2041,"children":2042},{"style":1465},[2043],{"type":51,"value":2044}," isInFlight",{"type":46,"tag":1405,"props":2046,"children":2047},{"style":1434},[2048],{"type":51,"value":1541},{"type":46,"tag":1405,"props":2050,"children":2051},{"style":1434},[2052],{"type":51,"value":1473},{"type":46,"tag":1405,"props":2054,"children":2055},{"style":1428},[2056],{"type":51,"value":1621},{"type":46,"tag":1405,"props":2058,"children":2059},{"style":1481},[2060],{"type":51,"value":1484},{"type":46,"tag":1405,"props":2062,"children":2063},{"style":1465},[2064],{"type":51,"value":1630},{"type":46,"tag":1405,"props":2066,"children":2067},{"style":1481},[2068],{"type":51,"value":1504},{"type":46,"tag":1405,"props":2070,"children":2071},{"style":1434},[2072],{"type":51,"value":1509},{"type":46,"tag":1405,"props":2074,"children":2075},{"class":1407,"line":1584},[2076],{"type":46,"tag":1405,"props":2077,"children":2078},{"emptyLinePlaceholder":1588},[2079],{"type":51,"value":1591},{"type":46,"tag":1405,"props":2081,"children":2082},{"class":1407,"line":1594},[2083,2087,2091,2095,2099,2103,2107,2111],{"type":46,"tag":1405,"props":2084,"children":2085},{"style":1422},[2086],{"type":51,"value":1462},{"type":46,"tag":1405,"props":2088,"children":2089},{"style":1465},[2090],{"type":51,"value":1651},{"type":46,"tag":1405,"props":2092,"children":2093},{"style":1434},[2094],{"type":51,"value":1473},{"type":46,"tag":1405,"props":2096,"children":2097},{"style":1434},[2098],{"type":51,"value":1660},{"type":46,"tag":1405,"props":2100,"children":2101},{"style":1440},[2102],{"type":51,"value":1665},{"type":46,"tag":1405,"props":2104,"children":2105},{"style":1434},[2106],{"type":51,"value":1504},{"type":46,"tag":1405,"props":2108,"children":2109},{"style":1422},[2110],{"type":51,"value":1674},{"type":46,"tag":1405,"props":2112,"children":2113},{"style":1434},[2114],{"type":51,"value":1453},{"type":46,"tag":1405,"props":2116,"children":2117},{"class":1407,"line":1641},[2118,2122,2126,2131,2136,2140,2144,2148,2152,2156,2161,2165],{"type":46,"tag":1405,"props":2119,"children":2120},{"style":1428},[2121],{"type":51,"value":1687},{"type":46,"tag":1405,"props":2123,"children":2124},{"style":1481},[2125],{"type":51,"value":1484},{"type":46,"tag":1405,"props":2127,"children":2128},{"style":1434},[2129],{"type":51,"value":2130},"{",{"type":46,"tag":1405,"props":2132,"children":2133},{"style":1481},[2134],{"type":51,"value":2135},"variables",{"type":46,"tag":1405,"props":2137,"children":2138},{"style":1434},[2139],{"type":51,"value":1710},{"type":46,"tag":1405,"props":2141,"children":2142},{"style":1434},[2143],{"type":51,"value":1715},{"type":46,"tag":1405,"props":2145,"children":2146},{"style":1481},[2147],{"type":51,"value":1527},{"type":46,"tag":1405,"props":2149,"children":2150},{"style":1434},[2151],{"type":51,"value":1710},{"type":46,"tag":1405,"props":2153,"children":2154},{"style":1465},[2155],{"type":51,"value":1728},{"type":46,"tag":1405,"props":2157,"children":2158},{"style":1434},[2159],{"type":51,"value":2160},"}}",{"type":46,"tag":1405,"props":2162,"children":2163},{"style":1481},[2164],{"type":51,"value":1504},{"type":46,"tag":1405,"props":2166,"children":2167},{"style":1434},[2168],{"type":51,"value":1509},{"type":46,"tag":1405,"props":2170,"children":2171},{"class":1407,"line":1681},[2172],{"type":46,"tag":1405,"props":2173,"children":2174},{"style":1412},[2175],{"type":51,"value":2176},"    \u002F\u002F No onCompleted needed — Relay updates the store automatically,\n",{"type":46,"tag":1405,"props":2178,"children":2179},{"class":1407,"line":1699},[2180],{"type":46,"tag":1405,"props":2181,"children":2182},{"style":1412},[2183],{"type":51,"value":2184},"    \u002F\u002F and useFragment re-renders this component with the new data.\n",{"type":46,"tag":1405,"props":2186,"children":2187},{"class":1407,"line":1736},[2188],{"type":46,"tag":1405,"props":2189,"children":2190},{"style":1434},[2191],{"type":51,"value":1857},{"type":46,"tag":1405,"props":2193,"children":2194},{"class":1407,"line":1770},[2195,2199,2203,2207,2211,2215,2219,2223,2227,2231],{"type":46,"tag":1405,"props":2196,"children":2197},{"style":1864},[2198],{"type":51,"value":1867},{"type":46,"tag":1405,"props":2200,"children":2201},{"style":1434},[2202],{"type":51,"value":1872},{"type":46,"tag":1405,"props":2204,"children":2205},{"style":1481},[2206],{"type":51,"value":1405},{"type":46,"tag":1405,"props":2208,"children":2209},{"style":1434},[2210],{"type":51,"value":1881},{"type":46,"tag":1405,"props":2212,"children":2213},{"style":1465},[2214],{"type":51,"value":1559},{"type":46,"tag":1405,"props":2216,"children":2217},{"style":1434},[2218],{"type":51,"value":92},{"type":46,"tag":1405,"props":2220,"children":2221},{"style":1465},[2222],{"type":51,"value":1527},{"type":46,"tag":1405,"props":2224,"children":2225},{"style":1434},[2226],{"type":51,"value":1890},{"type":46,"tag":1405,"props":2228,"children":2229},{"style":1481},[2230],{"type":51,"value":1405},{"type":46,"tag":1405,"props":2232,"children":2233},{"style":1434},[2234],{"type":51,"value":1899},{"type":46,"tag":1405,"props":2236,"children":2237},{"class":1407,"line":1825},[2238],{"type":46,"tag":1405,"props":2239,"children":2240},{"style":1434},[2241],{"type":51,"value":1908},{"type":46,"tag":54,"props":2243,"children":2244},{},[2245,2247,2252],{"type":51,"value":2246},"Similarly, do not store a fragment key (the ",{"type":46,"tag":77,"props":2248,"children":2250},{"className":2249},[],[2251],{"type":51,"value":1159},{"type":51,"value":2253}," prop) in React state. Relay\ngarbage collects data that is no longer retained by a mounted query component —\nif the component that originally fetched the data unmounts, a stashed key may\npoint to data that is no longer in the store.",{"type":46,"tag":607,"props":2255,"children":2257},{"id":2256},"maintain-fragment-co-location",[2258],{"type":51,"value":2259},"Maintain fragment co-location",{"type":46,"tag":54,"props":2261,"children":2262},{},[2263,2265,2270],{"type":51,"value":2264},"Do not fetch all data in a parent's query and pass raw data objects as props to\nchildren. This defeats data masking and creates tight coupling — adding a field\nto a child component requires editing the parent's query. Note that the\n",{"type":46,"tag":77,"props":2266,"children":2268},{"className":2267},[],[2269],{"type":51,"value":763},{"type":51,"value":2271}," lint rule will flag fields selected in the parent that\nare only used by children — this is a good signal that you need to extract a\nfragment.",{"type":46,"tag":1394,"props":2273,"children":2275},{"className":1396,"code":2274,"language":1398,"meta":1399,"style":1399},"\u002F\u002F WRONG: Parent fetches everything, passes raw data\nfunction Parent({queryRef}) {\n  const data = usePreloadedQuery(graphql`\n    query ParentQuery {\n      user {\n        name\n        email\n        avatarUrl\n      }\n    }\n  `, queryRef);\n  return \u003CUserCard name={data.user.name} avatarUrl={data.user.avatarUrl} \u002F>;\n}\n\n\u002F\u002F CORRECT: Child declares its own fragment\nfunction Parent({queryRef}) {\n  const data = usePreloadedQuery(graphql`\n    query ParentQuery {\n      user {\n        ...UserCard_user\n      }\n    }\n  `, queryRef);\n  return \u003CUserCard user={data.user} \u002F>;\n}\n",[2276],{"type":46,"tag":77,"props":2277,"children":2278},{"__ignoreMap":1399},[2279,2287,2316,2349,2358,2366,2374,2382,2390,2398,2406,2431,2517,2524,2531,2539,2566,2598,2606,2614,2623,2631,2639,2663,2704],{"type":46,"tag":1405,"props":2280,"children":2281},{"class":1407,"line":1408},[2282],{"type":46,"tag":1405,"props":2283,"children":2284},{"style":1412},[2285],{"type":51,"value":2286},"\u002F\u002F WRONG: Parent fetches everything, passes raw data\n",{"type":46,"tag":1405,"props":2288,"children":2289},{"class":1407,"line":1418},[2290,2294,2299,2303,2308,2312],{"type":46,"tag":1405,"props":2291,"children":2292},{"style":1422},[2293],{"type":51,"value":1425},{"type":46,"tag":1405,"props":2295,"children":2296},{"style":1428},[2297],{"type":51,"value":2298}," Parent",{"type":46,"tag":1405,"props":2300,"children":2301},{"style":1434},[2302],{"type":51,"value":1437},{"type":46,"tag":1405,"props":2304,"children":2305},{"style":1440},[2306],{"type":51,"value":2307},"queryRef",{"type":46,"tag":1405,"props":2309,"children":2310},{"style":1434},[2311],{"type":51,"value":1448},{"type":46,"tag":1405,"props":2313,"children":2314},{"style":1434},[2315],{"type":51,"value":1453},{"type":46,"tag":1405,"props":2317,"children":2318},{"class":1407,"line":1456},[2319,2323,2327,2331,2336,2340,2344],{"type":46,"tag":1405,"props":2320,"children":2321},{"style":1422},[2322],{"type":51,"value":1462},{"type":46,"tag":1405,"props":2324,"children":2325},{"style":1465},[2326],{"type":51,"value":1468},{"type":46,"tag":1405,"props":2328,"children":2329},{"style":1434},[2330],{"type":51,"value":1473},{"type":46,"tag":1405,"props":2332,"children":2333},{"style":1428},[2334],{"type":51,"value":2335}," usePreloadedQuery",{"type":46,"tag":1405,"props":2337,"children":2338},{"style":1481},[2339],{"type":51,"value":1484},{"type":46,"tag":1405,"props":2341,"children":2342},{"style":1428},[2343],{"type":51,"value":22},{"type":46,"tag":1405,"props":2345,"children":2346},{"style":1434},[2347],{"type":51,"value":2348},"`\n",{"type":46,"tag":1405,"props":2350,"children":2351},{"class":1407,"line":1512},[2352],{"type":46,"tag":1405,"props":2353,"children":2355},{"style":2354},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[2356],{"type":51,"value":2357},"    query ParentQuery {\n",{"type":46,"tag":1405,"props":2359,"children":2360},{"class":1407,"line":1584},[2361],{"type":46,"tag":1405,"props":2362,"children":2363},{"style":2354},[2364],{"type":51,"value":2365},"      user {\n",{"type":46,"tag":1405,"props":2367,"children":2368},{"class":1407,"line":1594},[2369],{"type":46,"tag":1405,"props":2370,"children":2371},{"style":2354},[2372],{"type":51,"value":2373},"        name\n",{"type":46,"tag":1405,"props":2375,"children":2376},{"class":1407,"line":1641},[2377],{"type":46,"tag":1405,"props":2378,"children":2379},{"style":2354},[2380],{"type":51,"value":2381},"        email\n",{"type":46,"tag":1405,"props":2383,"children":2384},{"class":1407,"line":1681},[2385],{"type":46,"tag":1405,"props":2386,"children":2387},{"style":2354},[2388],{"type":51,"value":2389},"        avatarUrl\n",{"type":46,"tag":1405,"props":2391,"children":2392},{"class":1407,"line":1699},[2393],{"type":46,"tag":1405,"props":2394,"children":2395},{"style":2354},[2396],{"type":51,"value":2397},"      }\n",{"type":46,"tag":1405,"props":2399,"children":2400},{"class":1407,"line":1736},[2401],{"type":46,"tag":1405,"props":2402,"children":2403},{"style":2354},[2404],{"type":51,"value":2405},"    }\n",{"type":46,"tag":1405,"props":2407,"children":2408},{"class":1407,"line":1770},[2409,2414,2418,2423,2427],{"type":46,"tag":1405,"props":2410,"children":2411},{"style":1434},[2412],{"type":51,"value":2413},"  `",{"type":46,"tag":1405,"props":2415,"children":2416},{"style":1434},[2417],{"type":51,"value":1494},{"type":46,"tag":1405,"props":2419,"children":2420},{"style":1465},[2421],{"type":51,"value":2422}," queryRef",{"type":46,"tag":1405,"props":2424,"children":2425},{"style":1481},[2426],{"type":51,"value":1504},{"type":46,"tag":1405,"props":2428,"children":2429},{"style":1434},[2430],{"type":51,"value":1509},{"type":46,"tag":1405,"props":2432,"children":2433},{"class":1407,"line":1825},[2434,2438,2442,2448,2453,2458,2462,2466,2470,2474,2478,2483,2488,2492,2496,2500,2504,2508,2512],{"type":46,"tag":1405,"props":2435,"children":2436},{"style":1864},[2437],{"type":51,"value":1867},{"type":46,"tag":1405,"props":2439,"children":2440},{"style":1434},[2441],{"type":51,"value":1872},{"type":46,"tag":1405,"props":2443,"children":2445},{"style":2444},"--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B",[2446],{"type":51,"value":2447},"UserCard",{"type":46,"tag":1405,"props":2449,"children":2450},{"style":1422},[2451],{"type":51,"value":2452}," name",{"type":46,"tag":1405,"props":2454,"children":2455},{"style":1434},[2456],{"type":51,"value":2457},"={",{"type":46,"tag":1405,"props":2459,"children":2460},{"style":1465},[2461],{"type":51,"value":1559},{"type":46,"tag":1405,"props":2463,"children":2464},{"style":1434},[2465],{"type":51,"value":92},{"type":46,"tag":1405,"props":2467,"children":2468},{"style":1465},[2469],{"type":51,"value":1802},{"type":46,"tag":1405,"props":2471,"children":2472},{"style":1434},[2473],{"type":51,"value":92},{"type":46,"tag":1405,"props":2475,"children":2476},{"style":1465},[2477],{"type":51,"value":1527},{"type":46,"tag":1405,"props":2479,"children":2480},{"style":1434},[2481],{"type":51,"value":2482},"} ",{"type":46,"tag":1405,"props":2484,"children":2485},{"style":1422},[2486],{"type":51,"value":2487},"avatarUrl",{"type":46,"tag":1405,"props":2489,"children":2490},{"style":1434},[2491],{"type":51,"value":2457},{"type":46,"tag":1405,"props":2493,"children":2494},{"style":1465},[2495],{"type":51,"value":1559},{"type":46,"tag":1405,"props":2497,"children":2498},{"style":1434},[2499],{"type":51,"value":92},{"type":46,"tag":1405,"props":2501,"children":2502},{"style":1465},[2503],{"type":51,"value":1802},{"type":46,"tag":1405,"props":2505,"children":2506},{"style":1434},[2507],{"type":51,"value":92},{"type":46,"tag":1405,"props":2509,"children":2510},{"style":1465},[2511],{"type":51,"value":2487},{"type":46,"tag":1405,"props":2513,"children":2514},{"style":1434},[2515],{"type":51,"value":2516},"} \u002F>;\n",{"type":46,"tag":1405,"props":2518,"children":2519},{"class":1407,"line":1834},[2520],{"type":46,"tag":1405,"props":2521,"children":2522},{"style":1434},[2523],{"type":51,"value":1908},{"type":46,"tag":1405,"props":2525,"children":2526},{"class":1407,"line":1851},[2527],{"type":46,"tag":1405,"props":2528,"children":2529},{"emptyLinePlaceholder":1588},[2530],{"type":51,"value":1591},{"type":46,"tag":1405,"props":2532,"children":2533},{"class":1407,"line":1860},[2534],{"type":46,"tag":1405,"props":2535,"children":2536},{"style":1412},[2537],{"type":51,"value":2538},"\u002F\u002F CORRECT: Child declares its own fragment\n",{"type":46,"tag":1405,"props":2540,"children":2541},{"class":1407,"line":1902},[2542,2546,2550,2554,2558,2562],{"type":46,"tag":1405,"props":2543,"children":2544},{"style":1422},[2545],{"type":51,"value":1425},{"type":46,"tag":1405,"props":2547,"children":2548},{"style":1428},[2549],{"type":51,"value":2298},{"type":46,"tag":1405,"props":2551,"children":2552},{"style":1434},[2553],{"type":51,"value":1437},{"type":46,"tag":1405,"props":2555,"children":2556},{"style":1440},[2557],{"type":51,"value":2307},{"type":46,"tag":1405,"props":2559,"children":2560},{"style":1434},[2561],{"type":51,"value":1448},{"type":46,"tag":1405,"props":2563,"children":2564},{"style":1434},[2565],{"type":51,"value":1453},{"type":46,"tag":1405,"props":2567,"children":2569},{"class":1407,"line":2568},17,[2570,2574,2578,2582,2586,2590,2594],{"type":46,"tag":1405,"props":2571,"children":2572},{"style":1422},[2573],{"type":51,"value":1462},{"type":46,"tag":1405,"props":2575,"children":2576},{"style":1465},[2577],{"type":51,"value":1468},{"type":46,"tag":1405,"props":2579,"children":2580},{"style":1434},[2581],{"type":51,"value":1473},{"type":46,"tag":1405,"props":2583,"children":2584},{"style":1428},[2585],{"type":51,"value":2335},{"type":46,"tag":1405,"props":2587,"children":2588},{"style":1481},[2589],{"type":51,"value":1484},{"type":46,"tag":1405,"props":2591,"children":2592},{"style":1428},[2593],{"type":51,"value":22},{"type":46,"tag":1405,"props":2595,"children":2596},{"style":1434},[2597],{"type":51,"value":2348},{"type":46,"tag":1405,"props":2599,"children":2601},{"class":1407,"line":2600},18,[2602],{"type":46,"tag":1405,"props":2603,"children":2604},{"style":2354},[2605],{"type":51,"value":2357},{"type":46,"tag":1405,"props":2607,"children":2609},{"class":1407,"line":2608},19,[2610],{"type":46,"tag":1405,"props":2611,"children":2612},{"style":2354},[2613],{"type":51,"value":2365},{"type":46,"tag":1405,"props":2615,"children":2617},{"class":1407,"line":2616},20,[2618],{"type":46,"tag":1405,"props":2619,"children":2620},{"style":2354},[2621],{"type":51,"value":2622},"        ...UserCard_user\n",{"type":46,"tag":1405,"props":2624,"children":2626},{"class":1407,"line":2625},21,[2627],{"type":46,"tag":1405,"props":2628,"children":2629},{"style":2354},[2630],{"type":51,"value":2397},{"type":46,"tag":1405,"props":2632,"children":2634},{"class":1407,"line":2633},22,[2635],{"type":46,"tag":1405,"props":2636,"children":2637},{"style":2354},[2638],{"type":51,"value":2405},{"type":46,"tag":1405,"props":2640,"children":2642},{"class":1407,"line":2641},23,[2643,2647,2651,2655,2659],{"type":46,"tag":1405,"props":2644,"children":2645},{"style":1434},[2646],{"type":51,"value":2413},{"type":46,"tag":1405,"props":2648,"children":2649},{"style":1434},[2650],{"type":51,"value":1494},{"type":46,"tag":1405,"props":2652,"children":2653},{"style":1465},[2654],{"type":51,"value":2422},{"type":46,"tag":1405,"props":2656,"children":2657},{"style":1481},[2658],{"type":51,"value":1504},{"type":46,"tag":1405,"props":2660,"children":2661},{"style":1434},[2662],{"type":51,"value":1509},{"type":46,"tag":1405,"props":2664,"children":2666},{"class":1407,"line":2665},24,[2667,2671,2675,2679,2684,2688,2692,2696,2700],{"type":46,"tag":1405,"props":2668,"children":2669},{"style":1864},[2670],{"type":51,"value":1867},{"type":46,"tag":1405,"props":2672,"children":2673},{"style":1434},[2674],{"type":51,"value":1872},{"type":46,"tag":1405,"props":2676,"children":2677},{"style":2444},[2678],{"type":51,"value":2447},{"type":46,"tag":1405,"props":2680,"children":2681},{"style":1422},[2682],{"type":51,"value":2683}," user",{"type":46,"tag":1405,"props":2685,"children":2686},{"style":1434},[2687],{"type":51,"value":2457},{"type":46,"tag":1405,"props":2689,"children":2690},{"style":1465},[2691],{"type":51,"value":1559},{"type":46,"tag":1405,"props":2693,"children":2694},{"style":1434},[2695],{"type":51,"value":92},{"type":46,"tag":1405,"props":2697,"children":2698},{"style":1465},[2699],{"type":51,"value":1802},{"type":46,"tag":1405,"props":2701,"children":2702},{"style":1434},[2703],{"type":51,"value":2516},{"type":46,"tag":1405,"props":2705,"children":2707},{"class":1407,"line":2706},25,[2708],{"type":46,"tag":1405,"props":2709,"children":2710},{"style":1434},[2711],{"type":51,"value":1908},{"type":46,"tag":607,"props":2713,"children":2715},{"id":2714},"spread-fragments-into-mutation-responses",[2716],{"type":51,"value":2717},"Spread fragments into mutation responses",{"type":46,"tag":54,"props":2719,"children":2720},{},[2721],{"type":51,"value":2722},"Do not select fields individually in both a fragment and a mutation response —\nthey will drift out of sync. Spread the fragment instead:",{"type":46,"tag":1394,"props":2724,"children":2727},{"className":2725,"code":2726,"language":22,"meta":1399,"style":1399},"language-graphql shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# WRONG\nmutation UpdateUserMutation($input: UpdateUserInput!) {\n  updateUser(input: $input) {\n    user { id, name, email, avatarUrl }\n  }\n}\n\n# CORRECT\nmutation UpdateUserMutation($input: UpdateUserInput!) {\n  updateUser(input: $input) {\n    user { ...UserCard_user }\n  }\n}\n",[2728],{"type":46,"tag":77,"props":2729,"children":2730},{"__ignoreMap":1399},[2731,2739,2747,2755,2763,2771,2778,2785,2793,2800,2807,2815,2822],{"type":46,"tag":1405,"props":2732,"children":2733},{"class":1407,"line":1408},[2734],{"type":46,"tag":1405,"props":2735,"children":2736},{},[2737],{"type":51,"value":2738},"# WRONG\n",{"type":46,"tag":1405,"props":2740,"children":2741},{"class":1407,"line":1418},[2742],{"type":46,"tag":1405,"props":2743,"children":2744},{},[2745],{"type":51,"value":2746},"mutation UpdateUserMutation($input: UpdateUserInput!) {\n",{"type":46,"tag":1405,"props":2748,"children":2749},{"class":1407,"line":1456},[2750],{"type":46,"tag":1405,"props":2751,"children":2752},{},[2753],{"type":51,"value":2754},"  updateUser(input: $input) {\n",{"type":46,"tag":1405,"props":2756,"children":2757},{"class":1407,"line":1512},[2758],{"type":46,"tag":1405,"props":2759,"children":2760},{},[2761],{"type":51,"value":2762},"    user { id, name, email, avatarUrl }\n",{"type":46,"tag":1405,"props":2764,"children":2765},{"class":1407,"line":1584},[2766],{"type":46,"tag":1405,"props":2767,"children":2768},{},[2769],{"type":51,"value":2770},"  }\n",{"type":46,"tag":1405,"props":2772,"children":2773},{"class":1407,"line":1594},[2774],{"type":46,"tag":1405,"props":2775,"children":2776},{},[2777],{"type":51,"value":1908},{"type":46,"tag":1405,"props":2779,"children":2780},{"class":1407,"line":1641},[2781],{"type":46,"tag":1405,"props":2782,"children":2783},{"emptyLinePlaceholder":1588},[2784],{"type":51,"value":1591},{"type":46,"tag":1405,"props":2786,"children":2787},{"class":1407,"line":1681},[2788],{"type":46,"tag":1405,"props":2789,"children":2790},{},[2791],{"type":51,"value":2792},"# CORRECT\n",{"type":46,"tag":1405,"props":2794,"children":2795},{"class":1407,"line":1699},[2796],{"type":46,"tag":1405,"props":2797,"children":2798},{},[2799],{"type":51,"value":2746},{"type":46,"tag":1405,"props":2801,"children":2802},{"class":1407,"line":1736},[2803],{"type":46,"tag":1405,"props":2804,"children":2805},{},[2806],{"type":51,"value":2754},{"type":46,"tag":1405,"props":2808,"children":2809},{"class":1407,"line":1770},[2810],{"type":46,"tag":1405,"props":2811,"children":2812},{},[2813],{"type":51,"value":2814},"    user { ...UserCard_user }\n",{"type":46,"tag":1405,"props":2816,"children":2817},{"class":1407,"line":1825},[2818],{"type":46,"tag":1405,"props":2819,"children":2820},{},[2821],{"type":51,"value":2770},{"type":46,"tag":1405,"props":2823,"children":2824},{"class":1407,"line":1834},[2825],{"type":46,"tag":1405,"props":2826,"children":2827},{},[2828],{"type":51,"value":1908},{"type":46,"tag":65,"props":2830,"children":2832},{"id":2831},"correctness",[2833],{"type":51,"value":2834},"Correctness",{"type":46,"tag":607,"props":2836,"children":2838},{"id":2837},"use-optimisticupdater-for-store-dependent-values",[2839,2840,2846],{"type":51,"value":831},{"type":46,"tag":77,"props":2841,"children":2843},{"className":2842},[],[2844],{"type":51,"value":2845},"optimisticUpdater",{"type":51,"value":2847}," for store-dependent values",{"type":46,"tag":54,"props":2849,"children":2850},{},[2851,2853,2858,2860,2866],{"type":51,"value":2852},"If an optimistic value depends on current store state (e.g., incrementing a\nlike count), use ",{"type":46,"tag":77,"props":2854,"children":2856},{"className":2855},[],[2857],{"type":51,"value":2845},{"type":51,"value":2859}," instead of ",{"type":46,"tag":77,"props":2861,"children":2863},{"className":2862},[],[2864],{"type":51,"value":2865},"optimisticResponse",{"type":51,"value":2867},". Multiple\noverlapping optimistic responses can compound incorrectly — two simultaneous\n\"like\" mutations both read count=5 and set count=6, instead of 5→6→7. When one\nrolls back, the store is left in an inconsistent state.",{"type":46,"tag":607,"props":2869,"children":2871},{"id":2870},"invalidate-data-after-wide-effect-mutations",[2872],{"type":51,"value":2873},"Invalidate data after wide-effect mutations",{"type":46,"tag":54,"props":2875,"children":2876},{},[2877,2879,2885,2887,2893,2895,2901],{"type":51,"value":2878},"When a mutation has side effects too broad to capture in a single response\npayload, use ",{"type":46,"tag":77,"props":2880,"children":2882},{"className":2881},[],[2883],{"type":51,"value":2884},"invalidateRecord()",{"type":51,"value":2886}," for targeted invalidation or\n",{"type":46,"tag":77,"props":2888,"children":2890},{"className":2889},[],[2891],{"type":51,"value":2892},"invalidateStore()",{"type":51,"value":2894}," for global invalidation. Pair with\n",{"type":46,"tag":77,"props":2896,"children":2898},{"className":2897},[],[2899],{"type":51,"value":2900},"useSubscribeToInvalidationState",{"type":51,"value":2902}," on mounted components to trigger refetches\nfor stale data automatically.",{"type":46,"tag":607,"props":2904,"children":2906},{"id":2905},"handle-staleness-explicitly",[2907],{"type":51,"value":2908},"Handle staleness explicitly",{"type":46,"tag":54,"props":2910,"children":2911},{},[2912,2914,2919],{"type":51,"value":2913},"Relay treats cached data as fresh ",{"type":46,"tag":499,"props":2915,"children":2916},{},[2917],{"type":51,"value":2918},"indefinitely",{"type":51,"value":2920}," by default. Two approaches:",{"type":46,"tag":534,"props":2922,"children":2923},{},[2924,2942],{"type":46,"tag":538,"props":2925,"children":2926},{},[2927,2932,2934,2940],{"type":46,"tag":499,"props":2928,"children":2929},{},[2930],{"type":51,"value":2931},"Time-based",{"type":51,"value":2933},": Set ",{"type":46,"tag":77,"props":2935,"children":2937},{"className":2936},[],[2938],{"type":51,"value":2939},"queryCacheExpirationTime",{"type":51,"value":2941}," on the Relay Store to\nautomatically mark data stale after a duration.",{"type":46,"tag":538,"props":2943,"children":2944},{},[2945,2950,2952,2957],{"type":46,"tag":499,"props":2946,"children":2947},{},[2948],{"type":51,"value":2949},"Event-based",{"type":51,"value":2951},": Call ",{"type":46,"tag":77,"props":2953,"children":2955},{"className":2954},[],[2956],{"type":51,"value":2884},{"type":51,"value":2958}," after mutations whose side effects\nextend beyond the mutation response payload.",{"type":46,"tag":54,"props":2960,"children":2961},{},[2962],{"type":51,"value":2963},"Without explicit staleness handling, components can display arbitrarily old data\nafter the user returns to a previously visited screen.",{"type":46,"tag":607,"props":2965,"children":2967},{"id":2966},"single-artifact-directory-for-type-safety",[2968],{"type":51,"value":2969},"Single artifact directory for type safety",{"type":46,"tag":54,"props":2971,"children":2972},{},[2973,2975,2981,2983,2989,2991,2996],{"type":51,"value":2974},"Without setting ",{"type":46,"tag":77,"props":2976,"children":2978},{"className":2977},[],[2979],{"type":51,"value":2980},"artifactDirectory",{"type":51,"value":2982}," in the compiler config, fragment references\nmay default to ",{"type":46,"tag":77,"props":2984,"children":2986},{"className":2985},[],[2987],{"type":51,"value":2988},"any",{"type":51,"value":2990},", losing type safety. Set it and align your bundler's\nmodule resolution accordingly. See ",{"type":46,"tag":77,"props":2992,"children":2994},{"className":2993},[],[2995],{"type":51,"value":661},{"type":51,"value":92},{"type":46,"tag":607,"props":2998,"children":3000},{"id":2999},"use-updatable-for-store-manipulation",[3001,3002,3008],{"type":51,"value":831},{"type":46,"tag":77,"props":3003,"children":3005},{"className":3004},[],[3006],{"type":51,"value":3007},"@updatable",{"type":51,"value":3009}," for store manipulation",{"type":46,"tag":54,"props":3011,"children":3012},{},[3013,3015,3021],{"type":51,"value":3014},"Prefer typesafe updatable queries\u002Ffragments over raw store manipulation with\nstring-based field access (e.g., ",{"type":46,"tag":77,"props":3016,"children":3018},{"className":3017},[],[3019],{"type":51,"value":3020},"store.get(id).setValue(newName, 'name')",{"type":51,"value":3022},").\nUpdatable fragments provide getters and setters, reducing the risk of typos\nand type mismatches.",{"type":46,"tag":607,"props":3024,"children":3026},{"id":3025},"avoid-unnecessary-refetches-after-mutations",[3027],{"type":51,"value":3028},"Avoid unnecessary refetches after mutations",{"type":46,"tag":54,"props":3030,"children":3031},{},[3032,3034,3040,3042,3048],{"type":51,"value":3033},"Do not call ",{"type":46,"tag":77,"props":3035,"children":3037},{"className":3036},[],[3038],{"type":51,"value":3039},"refetch()",{"type":51,"value":3041}," or ",{"type":46,"tag":77,"props":3043,"children":3045},{"className":3044},[],[3046],{"type":51,"value":3047},"fetchQuery()",{"type":51,"value":3049}," after mutations when spreading\ncomponent fragments in the mutation response would auto-update the store. Each\nunnecessary refetch is a wasted network request and delays the UI update.",{"type":46,"tag":54,"props":3051,"children":3052},{},[3053,3055,3060],{"type":51,"value":3054},"Reserve manual refetches for cases where the mutation's side effects are too\nbroad to capture in the response payload — and in those cases, prefer\n",{"type":46,"tag":77,"props":3056,"children":3058},{"className":3057},[],[3059],{"type":51,"value":2884},{"type":51,"value":3061}," (see above).",{"type":46,"tag":607,"props":3063,"children":3065},{"id":3064},"use-subscriptions-for-real-time-data",[3066],{"type":51,"value":3067},"Use subscriptions for real-time data",{"type":46,"tag":54,"props":3069,"children":3070},{},[3071,3073,3079,3080,3086],{"type":51,"value":3072},"Prefer GraphQL Subscriptions over polling (",{"type":46,"tag":77,"props":3074,"children":3076},{"className":3075},[],[3077],{"type":51,"value":3078},"setInterval",{"type":51,"value":838},{"type":46,"tag":77,"props":3081,"children":3083},{"className":3082},[],[3084],{"type":51,"value":3085},"fetchQuery",{"type":51,"value":3087},") or\nmanual refresh buttons for data that must stay current. Subscriptions push\nupdates only when data changes and integrate with Relay's normalized store\nautomatically.",{"type":46,"tag":65,"props":3089,"children":3091},{"id":3090},"naming-conventions",[3092],{"type":51,"value":3093},"Naming Conventions",{"type":46,"tag":54,"props":3095,"children":3096},{},[3097,3099,3104],{"type":51,"value":3098},"Relay ",{"type":46,"tag":499,"props":3100,"children":3101},{},[3102],{"type":51,"value":3103},"enforces",{"type":51,"value":3105}," that operation names match the module (file) they are defined\nin. Mismatched names cause compiler errors, not just style warnings.",{"type":46,"tag":107,"props":3107,"children":3108},{},[3109,3130],{"type":46,"tag":111,"props":3110,"children":3111},{},[3112],{"type":46,"tag":115,"props":3113,"children":3114},{},[3115,3120,3125],{"type":46,"tag":119,"props":3116,"children":3117},{},[3118],{"type":51,"value":3119},"Element",{"type":46,"tag":119,"props":3121,"children":3122},{},[3123],{"type":51,"value":3124},"Convention",{"type":46,"tag":119,"props":3126,"children":3127},{},[3128],{"type":51,"value":3129},"Example",{"type":46,"tag":130,"props":3131,"children":3132},{},[3133,3159,3185,3211],{"type":46,"tag":115,"props":3134,"children":3135},{},[3136,3141,3150],{"type":46,"tag":137,"props":3137,"children":3138},{},[3139],{"type":51,"value":3140},"Fragment",{"type":46,"tag":137,"props":3142,"children":3143},{},[3144],{"type":46,"tag":77,"props":3145,"children":3147},{"className":3146},[],[3148],{"type":51,"value":3149},"ComponentName_propName",{"type":46,"tag":137,"props":3151,"children":3152},{},[3153],{"type":46,"tag":77,"props":3154,"children":3156},{"className":3155},[],[3157],{"type":51,"value":3158},"UserCard_user",{"type":46,"tag":115,"props":3160,"children":3161},{},[3162,3167,3176],{"type":46,"tag":137,"props":3163,"children":3164},{},[3165],{"type":51,"value":3166},"Query",{"type":46,"tag":137,"props":3168,"children":3169},{},[3170],{"type":46,"tag":77,"props":3171,"children":3173},{"className":3172},[],[3174],{"type":51,"value":3175},"ComponentNameQuery",{"type":46,"tag":137,"props":3177,"children":3178},{},[3179],{"type":46,"tag":77,"props":3180,"children":3182},{"className":3181},[],[3183],{"type":51,"value":3184},"HomePageQuery",{"type":46,"tag":115,"props":3186,"children":3187},{},[3188,3193,3202],{"type":46,"tag":137,"props":3189,"children":3190},{},[3191],{"type":51,"value":3192},"Mutation",{"type":46,"tag":137,"props":3194,"children":3195},{},[3196],{"type":46,"tag":77,"props":3197,"children":3199},{"className":3198},[],[3200],{"type":51,"value":3201},"ComponentNameMutation",{"type":46,"tag":137,"props":3203,"children":3204},{},[3205],{"type":46,"tag":77,"props":3206,"children":3208},{"className":3207},[],[3209],{"type":51,"value":3210},"LikeButtonMutation",{"type":46,"tag":115,"props":3212,"children":3213},{},[3214,3219,3228],{"type":46,"tag":137,"props":3215,"children":3216},{},[3217],{"type":51,"value":3218},"Generated files",{"type":46,"tag":137,"props":3220,"children":3221},{},[3222],{"type":46,"tag":77,"props":3223,"children":3225},{"className":3224},[],[3226],{"type":51,"value":3227},"__generated__\u002F*.graphql",{"type":46,"tag":137,"props":3229,"children":3230},{},[3231],{"type":51,"value":3232},"Never edit these",{"type":46,"tag":54,"props":3234,"children":3235},{},[3236,3238,3244,3245,3251,3253,3259,3261,3266],{"type":51,"value":3237},"The module name is the filename stripped of all extensions (",{"type":46,"tag":77,"props":3239,"children":3241},{"className":3240},[],[3242],{"type":51,"value":3243},".react.js",{"type":51,"value":1102},{"type":46,"tag":77,"props":3246,"children":3248},{"className":3247},[],[3249],{"type":51,"value":3250},".tsx",{"type":51,"value":3252},",\netc.). ",{"type":46,"tag":77,"props":3254,"children":3256},{"className":3255},[],[3257],{"type":51,"value":3258},"UserCard.react.js",{"type":51,"value":3260}," → module name ",{"type":46,"tag":77,"props":3262,"children":3264},{"className":3263},[],[3265],{"type":51,"value":2447},{"type":51,"value":92},{"type":46,"tag":607,"props":3268,"children":3270},{"id":3269},"renaming-operations-when-extracting-to-a-new-file",[3271],{"type":51,"value":3272},"Renaming operations when extracting to a new file",{"type":46,"tag":54,"props":3274,"children":3275},{},[3276,3278,3283],{"type":51,"value":3277},"When moving a component to a new file, rename only the operations ",{"type":46,"tag":499,"props":3279,"children":3280},{},[3281],{"type":51,"value":3282},"defined in\nthat file",{"type":51,"value":3284}," to match the new filename. Do NOT rename fragment spreads that\nreference fragments owned by other modules — those names belong to their\ndefining component.",{"type":46,"tag":54,"props":3286,"children":3287},{},[3288,3290,3296,3298,3304],{"type":51,"value":3289},"Renaming an operation also changes its generated type name (e.g.,\n",{"type":46,"tag":77,"props":3291,"children":3293},{"className":3292},[],[3294],{"type":51,"value":3295},"UserCard_user$key",{"type":51,"value":3297}," → ",{"type":46,"tag":77,"props":3299,"children":3301},{"className":3300},[],[3302],{"type":51,"value":3303},"ProfileCard_user$key",{"type":51,"value":3305},"), so update all downstream imports\nof those generated types.",{"type":46,"tag":607,"props":3307,"children":3309},{"id":3308},"never-hand-edit-__generated__-files",[3310,3312,3317],{"type":51,"value":3311},"Never hand-edit ",{"type":46,"tag":77,"props":3313,"children":3315},{"className":3314},[],[3316],{"type":51,"value":730},{"type":51,"value":3318}," files",{"type":46,"tag":54,"props":3320,"children":3321},{},[3322],{"type":51,"value":3323},"The next compiler run overwrites any manual edits. If you see type errors about\nmissing generated types, run the compiler first — the types are just out of\ndate, not missing.",{"type":46,"tag":607,"props":3325,"children":3327},{"id":3326},"verify-mutation-variable-keys-after-auto-formatting",[3328],{"type":51,"value":3329},"Verify mutation variable keys after auto-formatting",{"type":46,"tag":54,"props":3331,"children":3332},{},[3333,3335,3341,3343,3349,3351,3356,3358,3364],{"type":51,"value":3334},"Auto-formatters and linters can rename variables in ways that silently break\nmutation calls. After running lint auto-fix, verify that variable keys in\n",{"type":46,"tag":77,"props":3336,"children":3338},{"className":3337},[],[3339],{"type":51,"value":3340},"commit({ variables: { ... } })",{"type":51,"value":3342}," still match the generated ",{"type":46,"tag":77,"props":3344,"children":3346},{"className":3345},[],[3347],{"type":51,"value":3348},"Mutation$variables",{"type":51,"value":3350},"\ntype (check for ",{"type":46,"tag":77,"props":3352,"children":3354},{"className":3353},[],[3355],{"type":51,"value":1559},{"type":51,"value":3357}," vs ",{"type":46,"tag":77,"props":3359,"children":3361},{"className":3360},[],[3362],{"type":51,"value":3363},"input",{"type":51,"value":3365}," mismatches in particular).",{"type":46,"tag":3367,"props":3368,"children":3369},"style",{},[3370],{"type":51,"value":3371},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":3373,"total":3547},[3374,3382,3395,3416,3437,3454,3465,3485,3498,3512,3524,3534],{"slug":4,"name":4,"fn":5,"description":6,"org":3375,"tags":3376,"stars":29,"repoUrl":30,"updatedAt":31},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3377,3378,3379,3380,3381],{"name":24,"slug":25,"type":16},{"name":27,"slug":28,"type":16},{"name":21,"slug":22,"type":16},{"name":14,"slug":15,"type":16},{"name":18,"slug":19,"type":16},{"slug":519,"name":519,"fn":3383,"description":3384,"org":3385,"tags":3386,"stars":29,"repoUrl":30,"updatedAt":3394},"optimize Relay application performance","Performance best practices for Relay applications. Use when optimizing data fetching, reducing re-renders, configuring caching, or improving time to first meaningful paint. Covers query placement, @defer, pagination, fetch policies, garbage collection, fragment granularity, and server-side filtering. Companion to the relay-best-practices skill which covers correctness and architecture.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3387,3388,3389,3392,3393],{"name":27,"slug":28,"type":16},{"name":21,"slug":22,"type":16},{"name":3390,"slug":3391,"type":16},"Performance","performance",{"name":14,"slug":15,"type":16},{"name":18,"slug":19,"type":16},"2026-06-10T07:30:28.726513",{"slug":3396,"name":3396,"fn":3397,"description":3398,"org":3399,"tags":3400,"stars":3413,"repoUrl":3414,"updatedAt":3415},"add-shape-types-to-torch-model","annotate PyTorch models with tensor shapes","Port a PyTorch model to use pyrefly's tensor shape type system (Tensor[[B, C, H, W]], Int[T]). Use this skill whenever the user wants to add shape annotations to a PyTorch model, type a model with tensor dimensions, port a model to use shape tracking, or annotate model forward methods with tensor shapes. Also use when the user mentions tensor shape ports, Int types for PyTorch, or pyrefly shape checking on a model file. Invoke BEFORE starting any model port — the skill's gated workflow prevents common failure modes.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3401,3404,3407,3410],{"name":3402,"slug":3403,"type":16},"Data Modeling","data-modeling",{"name":3405,"slug":3406,"type":16},"Deep Learning","deep-learning",{"name":3408,"slug":3409,"type":16},"Python","python",{"name":3411,"slug":3412,"type":16},"PyTorch","pytorch",6833,"https:\u002F\u002Fgithub.com\u002Ffacebook\u002Fpyrefly","2026-07-18T05:12:08.515952",{"slug":3417,"name":3417,"fn":3418,"description":3419,"org":3420,"tags":3421,"stars":3434,"repoUrl":3435,"updatedAt":3436},"camera-streaming","configure camera streaming and photo capture","Stream, video frames, photo capture, resolution\u002Fframe rate configuration",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3422,3425,3428,3431],{"name":3423,"slug":3424,"type":16},"Camera","camera",{"name":3426,"slug":3427,"type":16},"Hardware","hardware",{"name":3429,"slug":3430,"type":16},"iOS","ios",{"name":3432,"slug":3433,"type":16},"Video","video",488,"https:\u002F\u002Fgithub.com\u002Ffacebook\u002Fmeta-wearables-dat-ios","2026-05-15T06:14:43.555881",{"slug":3438,"name":3438,"fn":3439,"description":3440,"org":3441,"tags":3442,"stars":3434,"repoUrl":3435,"updatedAt":3453},"dat-conventions","develop iOS applications with DAT SDK","Swift patterns, async\u002Fawait, naming conventions, key types for DAT SDK iOS development",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3443,3444,3447,3450],{"name":3429,"slug":3430,"type":16},{"name":3445,"slug":3446,"type":16},"Mobile","mobile",{"name":3448,"slug":3449,"type":16},"SDK","sdk",{"name":3451,"slug":3452,"type":16},"Swift","swift","2026-05-15T06:14:42.334435",{"slug":3455,"name":3455,"fn":3456,"description":3457,"org":3458,"tags":3459,"stars":3434,"repoUrl":3435,"updatedAt":3464},"debugging","debug wearable device software","Common issues, Developer Mode, version compatibility, state machine diagnosis",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3460,3462,3463],{"name":3461,"slug":3455,"type":16},"Debugging",{"name":24,"slug":25,"type":16},{"name":3429,"slug":3430,"type":16},"2026-05-15T06:14:38.626606",{"slug":3466,"name":3466,"fn":3467,"description":3468,"org":3469,"tags":3470,"stars":3434,"repoUrl":3435,"updatedAt":3484},"display-access","manage display capabilities on wearable devices","Display capability setup, display-capable device selection, UI DSL, icons, buttons, images, and video playback",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3471,3474,3477,3480,3483],{"name":3472,"slug":3473,"type":16},"Design","design",{"name":3475,"slug":3476,"type":16},"Images","images",{"name":3478,"slug":3479,"type":16},"Interaction","interaction",{"name":3481,"slug":3482,"type":16},"UI Components","ui-components",{"name":3432,"slug":3433,"type":16},"2026-05-15T06:14:39.844502",{"slug":3486,"name":3486,"fn":3487,"description":3488,"org":3489,"tags":3490,"stars":3434,"repoUrl":3435,"updatedAt":3497},"getting-started","set up Meta wearable SDK integration","SDK setup, Swift Package Manager integration, Info.plist configuration, and first connection to Meta glasses",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3491,3494,3495,3496],{"name":3492,"slug":3493,"type":16},"Configuration","configuration",{"name":3429,"slug":3430,"type":16},{"name":3448,"slug":3449,"type":16},{"name":3451,"slug":3452,"type":16},"2026-05-15T06:14:41.086639",{"slug":3499,"name":3499,"fn":3500,"description":3501,"org":3502,"tags":3503,"stars":3434,"repoUrl":3435,"updatedAt":3511},"mockdevice-testing","test wearable apps with mock devices","MockDeviceKit for testing without physical glasses hardware",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3504,3505,3506,3509],{"name":3429,"slug":3430,"type":16},{"name":3445,"slug":3446,"type":16},{"name":3507,"slug":3508,"type":16},"QA","qa",{"name":434,"slug":3510,"type":16},"testing","2026-05-15T06:14:37.406692",{"slug":3513,"name":3513,"fn":3514,"description":3515,"org":3516,"tags":3517,"stars":3434,"repoUrl":3435,"updatedAt":3523},"permissions-registration","register apps with Meta AI","App registration with Meta AI, camera permission flows",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3518,3519,3520],{"name":3423,"slug":3424,"type":16},{"name":3429,"slug":3430,"type":16},{"name":3521,"slug":3522,"type":16},"Permissions","permissions","2026-05-15T06:14:46.030253",{"slug":3525,"name":3525,"fn":3526,"description":3527,"org":3528,"tags":3529,"stars":3434,"repoUrl":3435,"updatedAt":3533},"sample-app-guide","build wearable apps with camera streaming","Building a complete DAT app with camera streaming and photo capture",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3530,3531,3532],{"name":3423,"slug":3424,"type":16},{"name":3429,"slug":3430,"type":16},{"name":3445,"slug":3446,"type":16},"2026-05-15T06:14:36.185947",{"slug":3535,"name":3535,"fn":3536,"description":3537,"org":3538,"tags":3539,"stars":3434,"repoUrl":3435,"updatedAt":3546},"session-lifecycle","monitor device session lifecycle states","Device session states, pause\u002Fresume, availability monitoring",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3540,3541,3542,3545],{"name":3429,"slug":3430,"type":16},{"name":3445,"slug":3446,"type":16},{"name":3543,"slug":3544,"type":16},"Monitoring","monitoring",{"name":3390,"slug":3391,"type":16},"2026-05-15T06:14:44.790925",27,{"items":3549,"total":1418},[3550,3558],{"slug":4,"name":4,"fn":5,"description":6,"org":3551,"tags":3552,"stars":29,"repoUrl":30,"updatedAt":31},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3553,3554,3555,3556,3557],{"name":24,"slug":25,"type":16},{"name":27,"slug":28,"type":16},{"name":21,"slug":22,"type":16},{"name":14,"slug":15,"type":16},{"name":18,"slug":19,"type":16},{"slug":519,"name":519,"fn":3383,"description":3384,"org":3559,"tags":3560,"stars":29,"repoUrl":30,"updatedAt":3394},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3561,3562,3563,3564,3565],{"name":27,"slug":28,"type":16},{"name":21,"slug":22,"type":16},{"name":3390,"slug":3391,"type":16},{"name":14,"slug":15,"type":16},{"name":18,"slug":19,"type":16}]