{"id":39766681,"url":"https://github.com/beefchimi/vurtis","last_synced_at":"2026-01-18T11:45:33.195Z","repository":{"id":230763017,"uuid":"780056007","full_name":"beefchimi/vurtis","owner":"beefchimi","description":"Fluid grid virtualization for React","archived":false,"fork":false,"pushed_at":"2024-05-30T19:09:17.000Z","size":238,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-09T18:50:30.873Z","etag":null,"topics":["burt","curt","furt","gurt","hurt","jurt","turt","vurt","yurt","zurt"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/beefchimi.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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}},"created_at":"2024-03-31T15:22:06.000Z","updated_at":"2024-05-30T19:08:55.000Z","dependencies_parsed_at":"2024-03-31T19:29:54.012Z","dependency_job_id":"cf1a2e53-a49f-4582-abd6-a87a452809db","html_url":"https://github.com/beefchimi/vurtis","commit_stats":null,"previous_names":["beefchimi/vurtis"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/beefchimi/vurtis","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beefchimi%2Fvurtis","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beefchimi%2Fvurtis/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beefchimi%2Fvurtis/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beefchimi%2Fvurtis/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/beefchimi","download_url":"https://codeload.github.com/beefchimi/vurtis/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beefchimi%2Fvurtis/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28535177,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-18T10:13:46.436Z","status":"ssl_error","status_checked_at":"2026-01-18T10:13:11.045Z","response_time":98,"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":["burt","curt","furt","gurt","hurt","jurt","turt","vurt","yurt","zurt"],"created_at":"2026-01-18T11:45:33.080Z","updated_at":"2026-01-18T11:45:33.155Z","avatar_url":"https://github.com/beefchimi.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Vurtis\n\n[![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://opensource.org/licenses/ISC)\n\n\u003e Welcome to `Vurtis` aka `Virtual Curtis` aka `another React virutalization package`.\n\nThis package was created to satisfy a very specific use-case for virtualization in `React`. If you have a fluid grid of uniform height items, this is the package for you! Otherwise, you probably want `@tanstack/react-virtual`.\n\n[Check out the StackBlitz demo](https://stackblitz.com/edit/vurtis)\n\n## Install\n\n```sh\nnpm install vurtis\n```\n\n## Usage\n\nThe following example is your most common use case for a “virtualized fluid grid with uniform height items”.\n\nThis method requires passing the returned `top/left/width` values to each `\u003cli /\u003e` in order to absolutely position them within the parent `\u003cul /\u003e`.\n\n```tsx\nimport {useVurtis} from 'vurtis';\n\nimport {useBreakpoint} from '../local-project/hooks';\nimport {someDataSet} from '../local-project/data';\n\nexport function MyComponent() {\n  const {desktop} = useBreakpoint();\n\n  const itemMinWidth = desktop ? 260 : 160;\n  const gapSize = desktop ? 16 : 10;\n\n  const {listRef, listHeight, virtualItems, updateItemHeight} = useVurtis({\n    count: someDataSet.length,\n    minWidth: itemMinWidth,\n    gap: gapSize,\n  });\n\n  const itemsMarkup = virtualItems.map(\n    ({order, top, left, width, height}, index) =\u003e {\n      const {id, name} = someDataSet[order] ?? {};\n\n      // NOTES:\n      // 1. While `updateItemHeight` could be passed to the `ref` of\n      //    every `item`... we recommend checking against `index` and\n      //    only passing it to the first item. This is to help avoid\n      //    redundant DOM measurements (since all items are equal height).\n      // 2. While `height` is available from the `item` data,\n      //    it is not passed to `style`. This is because we want\n      //    our items to compute their `height` naturally.\n\n      return (\n        \u003cli\n          key={`Item-${id}`}\n          ref={index === 0 ? updateItemHeight : undefined}\n          style={{top, left, width}}\n        \u003e\n          \u003cspan\u003e{name}\u003c/span\u003e\n          \u003cspan\u003e{order}\u003c/span\u003e\n          \u003cspan\u003e{index}\u003c/span\u003e\n        \u003c/li\u003e\n      );\n    },\n  );\n\n  return (\n    \u003cdiv className=\"MyComponent\"\u003e\n      \u003cul ref={listRef} style={{height: listHeight}}\u003e\n        {itemsMarkup}\n      \u003c/ul\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\nThe following minimum CSS styles are required for this to work:\n\n```css\n.MyComponent {\n  /*\n   * This wrapper - or any other parent of the \u003cul /\u003e - cannot be\n   * positioned (have a stacking context) or else offsetTop cannot\n   * be captured within useVurtis().\n  */\n}\n\nul {\n  position: relative;\n}\n\nul li {\n  position: absolute;\n}\n```\n\n### Alternate usage\n\nIt might be that you cannot use absolute positioning for your virtualized grid.\n\nWe can allow our list to use a CSS grid layout while leveraging the \"space before/after\" methods to update the list’s `padding top/bottom` as we scroll.\n\n```tsx\nexport function AlternateSolution() {\n  const {desktop} = useBreakpoint();\n\n  const itemMinWidth = desktop ? 260 : 160;\n  const gapSize = desktop ? 16 : 10;\n\n  const {\n    listRef,\n    virtualItems,\n    updateItemHeight,\n    getSpaceBefore,\n    getSpaceAfter,\n  } = useVurtis({\n    count: someDataSet.length,\n    minWidth: itemMinWidth,\n    gap: gapSize,\n  });\n\n  const itemsMarkup = virtualItems.map(({order}, index) =\u003e {\n    const {id, name} = someDataSet[order] ?? {};\n\n    return (\n      \u003cli key={`Item-${id}`} ref={index === 0 ? updateItemHeight : undefined}\u003e\n        \u003cspan\u003e{name}\u003c/span\u003e\n        \u003cspan\u003e{order}\u003c/span\u003e\n        \u003cspan\u003e{index}\u003c/span\u003e\n      \u003c/li\u003e\n    );\n  });\n\n  return (\n    \u003cdiv className=\"MyComponent\"\u003e\n      \u003cul\n        ref={listRef}\n        style={{\n          paddingTop: getSpaceBefore(),\n          paddingBottom: getSpaceAfter(),\n        }}\n      \u003e\n        {itemsMarkup}\n      \u003c/ul\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\nThe above solution assumes the following CSS:\n\n```css\nul {\n  position: relative;\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));\n  grid-template-rows: auto;\n  align-content: start;\n  align-items: start;\n  gap: 10px;\n\n  @media (min-width: 1280px) {\n    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));\n    gap: 16px;\n  }\n}\n```\n\nNOTE: For this to work, you will need the `minWidth / gap` values passed to `useVurtis` to be in sync with your CSS. It is recommended you share these “style tokens” so that they can never deviate.\n\n## Debounce / throttle helper\n\nPerhaps you require some side-effects for when a value returned by `useVurtis()` changes. At the moment, nothing internal to `useVurtis()` is debounced/throttled, since all the relevant measurements need to be computed quickly in order to give a smooth UX. Throwing some expensive side-effects on top of that could cause a lot of re-rendering issues for your UI. If this is the cause, you may want to leverage the `useVurttle()` hook.\n\n```tsx\nexport function MyComponent() {\n  const {listRef, listWidth, listHeight, virtualItems, updateItemHeight} =\n    useVurtis({\n      count: 20,\n      minWidth: 100,\n      gap: 10,\n    });\n\n  // Will flip back-and-forth between `true/false` during resize operations.\n  // When `true`, you can refrain from additional computations - such as animations.\n  // Passing the 2nd `debounce` argument will further limit `pending` changes.\n  const pending = useVurttle(listWidth, true);\n\n  useEffect(() =\u003e {\n    console.log('Some side-effect goes here...', pending);\n  }, [pending]);\n\n  const itemsMarkup = virtualItems.map(\n    ({order, top, left, width, height}, index) =\u003e {\n      return (\n        \u003cli\n          key={`Item-${order}`}\n          ref={index === 0 ? updateItemHeight : undefined}\n          style={{top, left, width}}\n        \u003e\n          \u003cspan\u003e{order}\u003c/span\u003e\n        \u003c/li\u003e\n      );\n    },\n  );\n\n  return (\n    \u003cdiv\u003e\n      \u003cp\u003ePending: {pending.toString()}\u003c/p\u003e\n\n      \u003cul ref={listRef} style={{height: listHeight}}\u003e\n        {itemsMarkup}\n      \u003c/ul\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\n## Notes\n\nAs mentioned above, this package is for a very specific virtualization pattern. As such, there are a number of missing features / optimizations that you may otherwise expect to have. Some of these things _could be added in the future..._ but I make no guarantee.\n\n**Missing features:**\n\n1. Support for variable height items.\n2. Support for horizontal scrolling lists.\n3. Support for non-window containers.\n4. Debounced window listeners (scroll/resize).\n5. Recommended solutions for animation.\n6. Fully SSR / RSC compatible.\n7. Tests.\n8. etc...\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbeefchimi%2Fvurtis","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbeefchimi%2Fvurtis","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbeefchimi%2Fvurtis/lists"}