{"id":28181613,"url":"https://github.com/brandonmcconnell/render-hooks","last_synced_at":"2026-01-25T03:02:48.944Z","repository":{"id":293517771,"uuid":"984324512","full_name":"brandonmcconnell/render-hooks","owner":"brandonmcconnell","description":"Inline render-block-stable React hooks","archived":false,"fork":false,"pushed_at":"2025-05-16T19:14:02.000Z","size":2962,"stargazers_count":412,"open_issues_count":1,"forks_count":7,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-23T10:41:38.282Z","etag":null,"topics":["context","hooks","hooks-api-react","react","reactdom"],"latest_commit_sha":null,"homepage":"https://dtb.one/render-hooks-demos","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/brandonmcconnell.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-05-15T18:40:39.000Z","updated_at":"2025-10-15T04:52:46.000Z","dependencies_parsed_at":"2025-05-15T18:49:53.909Z","dependency_job_id":"602bc461-784e-4124-a6a3-5302f0675d4d","html_url":"https://github.com/brandonmcconnell/render-hooks","commit_stats":null,"previous_names":["brandonmcconnell/render-hooks"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/brandonmcconnell/render-hooks","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brandonmcconnell%2Frender-hooks","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brandonmcconnell%2Frender-hooks/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brandonmcconnell%2Frender-hooks/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brandonmcconnell%2Frender-hooks/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brandonmcconnell","download_url":"https://codeload.github.com/brandonmcconnell/render-hooks/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brandonmcconnell%2Frender-hooks/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28742973,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-25T02:46:29.005Z","status":"ssl_error","status_checked_at":"2026-01-25T02:44:29.968Z","response_time":113,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["context","hooks","hooks-api-react","react","reactdom"],"created_at":"2025-05-16T03:13:22.548Z","updated_at":"2026-01-25T03:02:48.938Z","avatar_url":"https://github.com/brandonmcconnell.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"\u003ch5 align=\"center\"\u003e\u003cimg src=\"./.github/render-hooks-logo_full.png?raw=true\" width=\"100%\" height=\"auto\" alt=\"Anchors for Tailwind CSS\" /\u003e\u003c/h5\u003e\n\n\u003cdiv align=\"center\"\u003e\n\u003cb\u003e\u003ci\u003eRenderHooks (\u003ccode\u003erender-hooks\u003c/code\u003e)\u003c/i\u003e\u003c/b\u003e\u003cbr\u003e\n\u003csmall\u003e\u003ci\u003eUse hooks inline in React/JSX\u003c/i\u003e\u003c/small\u003e\n\u003c/div\u003e\u003cbr\u003e\n\nRenderHooks lets you place hooks right next to the markup that needs them—no wrapper components, no breaking the [Rules of Hooks](https://react.dev/reference/rules/rules-of-hooks), and zero boilerplate, even when you supply your own custom hooks.\n\n---\n\n- [📖 How it works](#-how-it-works)\n- [✨ Features](#-features)\n- [🚀 Install](#-install)\n- [⚡ Quick start](#-quick-start)\n- [🧩 API](#-api)\n- [📚 Examples by hook](#-examples-by-hook)\n  - [`useState` (React ≥ 16.8)](#usestatereact--168)\n  - [`useReducer` (React ≥ 16.8)](#usereducerreact--168)\n  - [`useCallback` (React ≥ 16.8)](#usecallbackreact--168)\n  - [`useContext` (React ≥ 16.8)](#usecontextreact--168)\n  - [`useMemo` (React ≥ 16.8)](#usememoreact--168)\n  - [`useEffect` (React ≥ 16.8)](#useeffectreact--168)\n  - [`useLayoutEffect` (React ≥ 16.8)](#uselayouteffectreact--168)\n  - [`useImperativeHandle` (React ≥ 16.8)](#useimperativehandlereact--168)\n  - [`useRef` (React ≥ 16.8)](#userefreact--168)\n  - [`useInsertionEffect` (React ≥ 18)](#useinsertioneffectreact--18)\n  - [`useId` (React ≥ 18)](#useidreact--18)\n  - [`useSyncExternalStore` (React ≥ 18)](#usesyncexternalstorereact--18)\n  - [`useDeferredValue` (React ≥ 18)](#usedeferredvaluereact--18)\n  - [`useTransition` (React ≥ 18)](#usetransitionreact--18)\n  - [`useActionState` (React ≥ 19, experimental in 18)](#useactionstatereact--19-experimental-in-18)\n  - [`useFormStatus` (React-DOM ≥ 19)](#useformstatusreact-dom--19)\n  - [`use` (awaitable hook, React ≥ 19)](#useawaitable-hook-react--19)\n- [🛠 Custom hooks](#-custom-hooks)\n- [🧱 Nesting hooks](#-nesting-hooks)\n- [🤝 Collaboration](#-collaboration)\n  - [How to contribute](#how-to-contribute)\n\n---\n\n## 📖 How it works\n\n1. At runtime RenderHooks scans the installed `react` and `react-dom`\n   modules and wraps every export whose name starts with **`use`**.\n2. A TypeScript mapped type reproduces *exactly* the same keys from the typings,\n   so autocompletion never lies.\n3. The callback you give to `\u003cRenderHooks\u003e` (commonly aliased, e.g. `\u003c$\u003e`) is executed during that same render\n   pass, keeping the Rules of Hooks intact.\n4. Custom hooks are merged in once—stable reference, fully typed.\n\n---\n\n## ✨ Features\n\n| ✔︎ | Description |\n|----|-------------|\n| **One element** | `\u003c$\u003e` merges every `use*` hook exposed by the consumer's version of **`react` + `react-dom`** into a single helpers object. |\n| **Version-adaptive** | Only the hooks that exist in *your* React build appear. Upgrade React → new hooks show up automatically. |\n| **Custom-hook friendly** | Pass an object of your own hooks once—full IntelliSense inside the render callback. |\n| **100 % type-safe** | No `any`, no `unknown`. Generic signatures flow through the helpers object. |\n| **Tiny runtime** | Just an object merge—`\u003c$\u003e` renders nothing to the DOM. |\n\n---\n\n## 🚀 Install\n\n```bash\nnpm install render-hooks             # or yarn / pnpm / bun\n```\n\nRenderHooks lists **`react`** and **`react-dom`** as peer dependencies, so it\nalways tracks *your* versions.\n\n---\n\n## ⚡ Quick start\n\n```tsx\nimport $ from 'render-hooks';\n\nexport function Counter() {\n  return (\n    \u003c$\u003e\n      {({ useState }) =\u003e {\n        const [n, set] = useState(0);\n        return \u003cbutton onClick={() =\u003e set(n + 1)}\u003eClicked {n}\u003c/button\u003e;\n      }}\n    \u003c/$\u003e\n  );\n}\n```\n\nThe hook runs during the same render, so the Rules of Hooks are upheld.\n\n---\n\n## 🧩 API\n\n| Prop        | Type                                               | Description |\n|-------------|----------------------------------------------------|-------------|\n| `hooks`     | `Record\u003cstring, (...args: never[]) =\u003e unknown\u003e`    | (optional) custom hooks to expose. |\n| `children`  | `(helpers) ⇒ ReactNode`                            | Render callback receiving **all** built-in hooks available in your React version **plus** the custom hooks you supplied. |\n\n---\n\n## 📚 Examples by hook\n\nBelow is a **minimal, practical snippet for every built-in hook**.  \nEach header lists the **minimum React (or React-DOM) version** required—if your\nproject uses an older version, that hook simply won't appear in the helpers\nobject.\n\n\u003e All snippets assume  \n\u003e `import $ from 'render-hooks';`\n\n---\n\n### `useState` (React ≥ 16.8)\n\n```tsx\nexport function UseStateExample() {\n  return (\n    \u003c$\u003e\n      {({ useState }) =\u003e {\n        const [value, set] = useState('');\n        return \u003cinput value={value} onChange={(e) =\u003e set(e.target.value)} /\u003e;\n      }}\n    \u003c/$\u003e\n  );\n}\n```\n\n---\n\n### `useReducer` (React ≥ 16.8)\n\n```tsx\nexport function UseReducerExample() {\n  return (\n    \u003c$\u003e\n      {({ useReducer }) =\u003e {\n        const [count, dispatch] = useReducer(\n          (s: number, a: 'inc' | 'dec') =\u003e (a === 'inc' ? s + 1 : s - 1),\n          0,\n        );\n        return (\n          \u003c\u003e\n            \u003cbutton onClick={() =\u003e dispatch('dec')}\u003e-\u003c/button\u003e\n            \u003cspan\u003e{count}\u003c/span\u003e\n            \u003cbutton onClick={() =\u003e dispatch('inc')}\u003e＋\u003c/button\u003e\n          \u003c/\u003e\n        );\n      }}\n    \u003c/$\u003e\n  );\n}\n```\n\n---\n\n### `useCallback` (React ≥ 16.8)\n\n```tsx\nexport function UseCallbackExample() {\n  return (\n    \u003c$\u003e\n      {({ useState, useCallback }) =\u003e {\n        const [txt, setTxt] = useState('');\n        const onChange = useCallback(\n          (e: React.ChangeEvent\u003cHTMLInputElement\u003e) =\u003e setTxt(e.target.value),\n          [],\n        );\n        return \u003cinput value={txt} onChange={onChange} /\u003e;\n      }}\n    \u003c/$\u003e\n  );\n}\n```\n\n---\n\n### `useContext` (React ≥ 16.8)\n\n```tsx\nconst ThemeCtx = React.createContext\u003c'light' | 'dark'\u003e('light');\n\nexport function UseContextExample() {\n  return (\n    \u003cThemeCtx.Provider value=\"dark\"\u003e\n      \u003c$\u003e\n        {({ useContext }) =\u003e \u003cp\u003eTheme: {useContext(ThemeCtx)}\u003c/p\u003e}\n      \u003c/$\u003e\n    \u003c/ThemeCtx.Provider\u003e\n  );\n}\n```\n\n---\n\n### `useMemo` (React ≥ 16.8)\n\n```tsx\nexport function UseMemoExample() {\n  return (\n    \u003c$\u003e\n      {({ useState, useMemo }) =\u003e {\n        const [n, setN] = useState(25);\n        const fib = useMemo(() =\u003e {\n          const f = (x: number): number =\u003e\n            x \u003c= 1 ? x : f(x - 1) + f(x - 2);\n          return f(n);\n        }, [n]);\n        return (\n          \u003c\u003e\n            \u003cinput\n              type=\"number\"\n              value={n}\n              onChange={(e) =\u003e setN(+e.target.value)}\n            /\u003e\n            \u003cp\u003eFib({n}) = {fib}\u003c/p\u003e\n          \u003c/\u003e\n        );\n      }}\n    \u003c/$\u003e\n  );\n}\n```\n\n---\n\n### `useEffect` (React ≥ 16.8)\n\n```tsx\nexport function UseEffectExample() {\n  return (\n    \u003c$\u003e\n      {({ useState, useEffect }) =\u003e {\n        const [time, setTime] = useState('');\n        useEffect(() =\u003e {\n          const id = setInterval(\n            () =\u003e setTime(new Date().toLocaleTimeString()),\n            1000,\n          );\n          return () =\u003e clearInterval(id);\n        }, []);\n        return \u003cp\u003e{time}\u003c/p\u003e;\n      }}\n    \u003c/$\u003e\n  );\n}\n```\n\n---\n\n### `useLayoutEffect` (React ≥ 16.8)\n\n```tsx\nexport function UseLayoutEffectExample() {\n  return (\n    \u003c$\u003e\n      {({ useRef, useLayoutEffect }) =\u003e {\n        const box = useRef\u003cHTMLDivElement\u003e(null);\n        useLayoutEffect(() =\u003e {\n          box.current!.style.background = '#ffd54f';\n        }, []);\n        return \u003cdiv ref={box}\u003e highlighted after layout \u003c/div\u003e;\n      }}\n    \u003c/$\u003e\n  );\n}\n```\n\n---\n\n### `useImperativeHandle` (React ≥ 16.8)\n\n```tsx\nconst Fancy = React.forwardRef\u003cHTMLInputElement\u003e((_, ref) =\u003e (\n  \u003c$\u003e\n    {({ useRef, useImperativeHandle }) =\u003e {\n      const local = useRef\u003cHTMLInputElement\u003e(null);\n      useImperativeHandle(ref, () =\u003e ({ focus: () =\u003e local.current?.focus() }));\n      return \u003cinput ref={local} placeholder=\"Fancy input\" /\u003e;\n    }}\n  \u003c/$\u003e\n));\n\nexport function UseImperativeHandleExample() {\n  const ref = React.useRef\u003c{ focus: () =\u003e void }\u003e(null);\n  return (\n    \u003c\u003e\n      \u003cFancy ref={ref} /\u003e\n      \u003cbutton onClick={() =\u003e ref.current?.focus()}\u003eFocus\u003c/button\u003e\n    \u003c/\u003e\n  );\n}\n```\n\n---\n\n### `useRef` (React ≥ 16.8)\n\n```tsx\nexport function UseRefExample() {\n  return (\n    \u003c$\u003e\n      {({ useRef }) =\u003e {\n        const input = useRef\u003cHTMLInputElement\u003e(null);\n        return (\n          \u003c\u003e\n            \u003cbutton onClick={() =\u003e input.current?.focus()}\u003efocus\u003c/button\u003e\n            \u003cinput ref={input} /\u003e\n          \u003c/\u003e\n        );\n      }}\n    \u003c/$\u003e\n  );\n}\n```\n\n---\n\n### `useInsertionEffect` (React ≥ 18)\n\n```tsx\nexport function UseInsertionEffectExample() {\n  return (\n    \u003c$\u003e\n      {({ useInsertionEffect }) =\u003e {\n        useInsertionEffect(() =\u003e {\n          const style = document.createElement('style');\n          style.textContent = `.flash{animation:flash 1s steps(2) infinite;}\n            @keyframes flash{to{opacity:.2}}`;\n          document.head.append(style);\n          return () =\u003e style.remove();\n        }, []);\n        return \u003cp className=\"flash\"\u003eflashing text\u003c/p\u003e;\n      }}\n    \u003c/$\u003e\n  );\n}\n```\n\n---\n\n### `useId` (React ≥ 18)\n\n```tsx\nexport function UseIdExample() {\n  return (\n    \u003c$\u003e\n      {({ useId, useState }) =\u003e {\n        const id = useId();\n        const [v, set] = useState('');\n        return (\n          \u003c\u003e\n            \u003clabel htmlFor={id}\u003eName\u003c/label\u003e\n            \u003cinput id={id} value={v} onChange={(e) =\u003e set(e.target.value)} /\u003e\n          \u003c/\u003e\n        );\n      }}\n    \u003c/$\u003e\n  );\n}\n```\n\n---\n\n### `useSyncExternalStore` (React ≥ 18)\n\n```tsx\nexport function UseSyncExternalStoreExample() {\n  return (\n    \u003c$\u003e\n      {({ useSyncExternalStore }) =\u003e {\n        const width = useSyncExternalStore(\n          (cb) =\u003e {\n            window.addEventListener('resize', cb);\n            return () =\u003e window.removeEventListener('resize', cb);\n          },\n          () =\u003e window.innerWidth,\n        );\n        return \u003cp\u003ewidth: {width}px\u003c/p\u003e;\n      }}\n    \u003c/$\u003e\n  );\n}\n```\n\n---\n\n### `useDeferredValue` (React ≥ 18)\n\n```tsx\nexport function UseDeferredValueExample() {\n  return (\n    \u003c$\u003e\n      {({ useState, useDeferredValue }) =\u003e {\n        const [text, setText] = useState('');\n        const deferred = useDeferredValue(text);\n        return (\n          \u003c\u003e\n            \u003cinput value={text} onChange={(e) =\u003e setText(e.target.value)} /\u003e\n            \u003cp\u003edeferred: {deferred}\u003c/p\u003e\n          \u003c/\u003e\n        );\n      }}\n    \u003c/$\u003e\n  );\n}\n```\n\n---\n\n### `useTransition` (React ≥ 18)\n\n```tsx\nexport function UseTransitionExample() {\n  return (\n    \u003c$\u003e\n      {({ useState, useTransition }) =\u003e {\n        const [list, setList] = useState\u003cstring[]\u003e([]);\n        const [pending, start] = useTransition();\n        const filter = (e: React.ChangeEvent\u003cHTMLInputElement\u003e) =\u003e {\n          const q = e.target.value;\n          start(() =\u003e\n            setList(\n              Array.from({ length: 5_000 }, (_, i) =\u003e `Item ${i}`).filter((x) =\u003e\n                x.includes(q),\n              ),\n            ),\n          );\n        };\n        return (\n          \u003c\u003e\n            \u003cinput onChange={filter} placeholder=\"filter 5 k items\" /\u003e\n            {pending \u0026\u0026 \u003cp\u003eupdating…\u003c/p\u003e}\n            \u003cp\u003e{list.length} items\u003c/p\u003e\n          \u003c/\u003e\n        );\n      }}\n    \u003c/$\u003e\n  );\n}\n```\n\n---\n\n### `useActionState` (React ≥ 19, experimental in 18)\n\n```tsx\nexport function UseActionStateExample() {\n  return (\n    \u003c$\u003e\n      {({ useActionState }) =\u003e {\n        const [msg, submit, pending] = useActionState(\n          async (_prev: string, data: FormData) =\u003e {\n            await new Promise((r) =\u003e setTimeout(r, 400));\n            return data.get('text') as string;\n          },\n          '',\n        );\n        return (\n          \u003cform action={submit}\u003e\n            \u003cinput name=\"text\" placeholder=\"Say hi\" /\u003e\n            \u003cbutton disabled={pending}\u003eSend\u003c/button\u003e\n            {msg \u0026\u0026 \u003cp\u003eYou said: {msg}\u003c/p\u003e}\n          \u003c/form\u003e\n        );\n      }}\n    \u003c/$\u003e\n  );\n}\n```\n\n---\n\n### `useFormStatus` (React-DOM ≥ 19)\n\n```tsx\nexport function UseFormStatusExample() {\n  return (\n    \u003c$\u003e\n      {({ useState, useFormStatus }) =\u003e {\n        const [done, setDone] = useState(false);\n        const { pending } = useFormStatus();\n\n        const action = async () =\u003e {\n          await new Promise((r) =\u003e setTimeout(r, 400));\n          setDone(true);\n        };\n\n        return (\n          \u003cform action={action}\u003e\n            \u003cbutton\u003e{pending ? 'Saving…' : 'Save'}\u003c/button\u003e\n            {done \u0026\u0026 \u003cp\u003esaved!\u003c/p\u003e}\n          \u003c/form\u003e\n        );\n      }}\n    \u003c/$\u003e\n  );\n}\n```\n\n---\n\n### `use` (awaitable hook, React ≥ 19)\n\n```tsx\nfunction fetchQuote() {\n  return new Promise\u003cstring\u003e((r) =\u003e\n    setTimeout(() =\u003e r('\"Ship early, ship often.\"'), 800),\n  );\n}\n\nexport function UseAwaitExample() {\n  return (\n    \u003c$\u003e\n      {({ use }) =\u003e \u003cblockquote\u003e{use(fetchQuote())}\u003c/blockquote\u003e}\n    \u003c/$\u003e\n  );\n}\n```\n\n---\n\n## 🛠 Custom hooks\n\nInject any custom hooks once via the `hooks` prop:\n\n```tsx\nimport $ from 'render-hooks';\nimport { useToggle, useDebounce } from './myHooks';\n\nexport function Example() {\n  return (\n    \u003c$ hooks={{ useToggle, useDebounce }}\u003e\n      {({ useToggle, useDebounce }) =\u003e {\n        const [open, toggle] = useToggle(false);\n        const dOpen = useDebounce(open, 250);\n        return (\n          \u003c\u003e\n            \u003cbutton onClick={toggle}\u003etoggle\u003c/button\u003e\n            \u003cp\u003edebounced: {dOpen.toString()}\u003c/p\u003e\n          \u003c/\u003e\n        );\n      }}\n    \u003c/$\u003e\n  );\n}\n```\n\n---\n\n## 🧱 Nesting hooks\n\nYou can nest `RenderHooks` (`$`) as deeply as you need. Each instance provides its own fresh set of hooks, scoped to its render callback. This is particularly useful for managing item-specific state within loops, where you'd otherwise need to create separate components.\n\nHere's an example where RenderHooks is used to manage state for both levels of a nested list directly within the `.map()` callbacks, and a child can affect a parent RenderHook's state:\n\n```tsx\nimport React from 'react'; // Needed for useState, useTransition in this example\nimport $ from 'render-hooks';\n\ntype Category = {\n  id: number;\n  name: string;\n  posts: { id: number; title: string }[];\n};\n\nconst data: Category[] = [\n  {\n    id: 1,\n    name: 'Tech',\n    posts: [{ id: 11, title: 'Next-gen CSS' }],\n  },\n  {\n    id: 2,\n    name: 'Life',\n    posts: [\n      { id: 21, title: 'Minimalism' },\n      { id: 22, title: 'Travel hacks' },\n    ],\n  },\n];\n\nexport function NestedExample() {\n  return (\n    \u003cul\u003e\n      {data.map((cat) =\u003e (\n        /* ───── 1️⃣  Outer RenderHooks for each category row ───── */\n        \u003c$ key={cat.id}\u003e\n          {({ useState, useTransition }) =\u003e {\n            const [expanded, setExpanded] = useState(false);\n            const [likes, setLikes] = useState(0); \n            const [isPending, startTransition] = useTransition();\n\n            return (\n              \u003cli\u003e\n                \u003cbutton onClick={() =\u003e setExpanded(!expanded)}\u003e\n                  {expanded ? '▾' : '▸'} {cat.name} {likes === 0 ? '🖤' : '❤️'.repeat(likes)} ({likes} like{likes === 1 ? '' : 's'})\n                  {isPending \u0026\u0026 ' (updating...)'}\n                \u003c/button\u003e\n\n                {expanded \u0026\u0026 (\n                  \u003cul\u003e\n                    {cat.posts.map((post) =\u003e (\n                      /* ───── 2️⃣  Inner RenderHooks per post row ───── */\n                      \u003c$ key={post.id}\u003e\n                        {({ useState: useItemState }) =\u003e {\n                          const [liked, setItemLiked] = useItemState(false);\n\n                          const toggleLike = () =\u003e {\n                            setItemLiked((prev) =\u003e {\n                              // 🔄 Update outer «likes» using startTransition from the parent RenderHooks\n                              const next = !prev;\n                              startTransition(() =\u003e {\n                                setLikes((c) =\u003e c + (next ? 1 : -1));\n                              });\n                              return next;\n                            });\n                          };\n\n                          return (\n                            \u003cli\u003e\n                              {post.title}{' '}\n                              \u003cbutton onClick={toggleLike}\u003e\n                                {liked ? '❤️ Liked' : '🖤 Like'}\n                              \u003c/button\u003e\n                            \u003c/li\u003e\n                          );\n                        }}\n                      \u003c/$\u003e\n                    ))}\n                  \u003c/ul\u003e\n                )}\n              \u003c/li\u003e\n            );\n          }}\n        \u003c/$\u003e\n      ))}\n    \u003c/ul\u003e\n  );\n}\n```\n\nIn this example:\n- The main `NestedExample` component does not use RenderHooks directly.\n- The **first `.map()`** iterates through `data`. Inside this map, `\u003c$\u003e` is used to give each `category` its own states: `expanded` and `likes`. It also gets `useTransition` to acquire `startTransition`.\n- The **second, inner `.map()`** iterates through `cat.posts`. Inside *this* map, another, nested `\u003c$\u003e` is used to give each `post` its own independent `liked` state.\n- Crucially, when a post's `toggleLike` function is called, it updates its local `liked` state and then calls `startTransition` (obtained from the parent category's RenderHooks scope) to wrap the update to the parent's `likes` state. \n\nThis demonstrates not only nesting for independent state but also how functions and transition control from a parent RenderHooks instance can be utilized by children that also use RenderHooks, facilitating robust cross-scope communication.\n\n\u003e [!IMPORTANT]\n\u003e **Note on `startTransition`**: Using `startTransition` here is important. When an interaction (like clicking \"Like\") in a nested `RenderHooks` instance needs to update state managed by a parent `RenderHooks` instance, React might issue a warning about \"updating one component while rendering another\" if the update is synchronous. Wrapping the parent's state update in `startTransition` signals to React that this update can be deferred, preventing the warning and ensuring smoother UI updates. This is a general React pattern applicable when updates across component boundaries (or deeply nested state updates) might occur.\n\n---\n\n## 🤝 Collaboration\n\nRenderHooks is a community-driven project. Every idea, issue, and pull request helps it grow and improve.\n\nWhether you're fixing a typo or implementing a brand-new feature, **you're warmly welcome here!** ✨\n\n### How to contribute\n\n1. ⭐️ **Star the repo** – it helps others discover the project and shows your support.\n2. 🐛 **Report bugs / request features** – open an issue and describe the problem or idea. Reproduction steps or code snippets are golden.\n3. 📚 **Improve the docs** – spot a typo, unclear wording, or missing example? Submit a quick PR.\n4. 👩‍💻 **Send code changes** – bug fixes, performance tweaks, new examples, or custom hooks… big or small, they're all appreciated. If you're unsure, open a draft PR and we'll figure it out together.\n5. 💬 **Join the conversation** – comment on issues \u0026 PRs, share how you're using RenderHooks, or ask questions. First-time contributors are encouraged to jump in!\n6. 📣 **Share it** - if you love it, please share it! I want to grow this tool into something that makes all of our day-to-day lives a bit easier, so no gate-keeping. \n\nIf this would be your **first open-source contribution**, don't hesitate to ask for guidance—I'll happily walk you through the process.\n\nThank you for making RenderHooks better for everyone! 🙏\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrandonmcconnell%2Frender-hooks","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrandonmcconnell%2Frender-hooks","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrandonmcconnell%2Frender-hooks/lists"}