{"id":19408569,"url":"https://github.com/morglod/react-virtual-overflow","last_synced_at":"2025-04-24T09:31:56.006Z","repository":{"id":248020304,"uuid":"827567633","full_name":"Morglod/react-virtual-overflow","owner":"Morglod","description":"React components for efficiently rendering large lists without headache","archived":false,"fork":false,"pushed_at":"2024-07-28T20:35:54.000Z","size":449,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-16T03:56:14.768Z","etag":null,"topics":["list","overflow","performance","react","virtual-scroll","virtualization","virtualized","virtualized-list","virtualized-scroll"],"latest_commit_sha":null,"homepage":"https://morglod.github.io/react-virtual-overflow/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Morglod.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2024-07-11T23:09:19.000Z","updated_at":"2024-09-10T05:59:24.000Z","dependencies_parsed_at":"2024-07-19T18:02:52.777Z","dependency_job_id":null,"html_url":"https://github.com/Morglod/react-virtual-overflow","commit_stats":null,"previous_names":["morglod/react-virtual-overflow"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Morglod%2Freact-virtual-overflow","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Morglod%2Freact-virtual-overflow/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Morglod%2Freact-virtual-overflow/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Morglod%2Freact-virtual-overflow/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Morglod","download_url":"https://codeload.github.com/Morglod/react-virtual-overflow/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250600722,"owners_count":21457018,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["list","overflow","performance","react","virtual-scroll","virtualization","virtualized","virtualized-list","virtualized-scroll"],"created_at":"2024-11-10T12:06:35.577Z","updated_at":"2025-04-24T09:31:55.373Z","avatar_url":"https://github.com/Morglod.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![NPM Version](https://badge.fury.io/js/react-virtual-overflow.svg?style=flat)](https://www.npmjs.com/package/react-virtual-overflow)\n[![GitHub stars](https://img.shields.io/github/stars/Morglod/react-virtual-overflow.svg?style=social\u0026label=Star)](https://gitHub.com/Morglod/react-virtual-overflow/)\n\n# react-virtual-overflow\n\nSimilar to [react-virtualized](https://github.com/bvaughn/react-virtualized), but:\n\n-   No deps\n-   No fighting with containers\n-   No magical divs will wrap your list with position: absolute and height: 0\n-   No scroll syncing problems\n-   No AutoWindow over AutoSize with VerticalSpecialList\n-   Dead simple infinity loader\n-   Full rendering controll\n-   It just works\n-   ~0.5kb gzipped\n\nCurrently only fixed item sizes supported, but will add dynamic sizing later.\n\nComponents \u0026 hooks in this library will automatically find all containers with overflows and render only visible items.  \nSo you could stack and wrap your list in anyway you want, everything will work.\n\nYou also could use some parts of this library for example to calculate only visible on screen rect of element.\n\nThis library track only scroll/resize/orientationchange events (on capture phase), so if you resize some wrapper with overflow through styles, you should use hooks and call recalculation manually.\n\n![](./important.jpg)\n\n```\nnpm i react-virtual-overflow\n```\n\n[demo app code](src/examples/demo.tsx)  \n[working demo](https://morglod.github.io/react-virtual-overflow/)\n\n## Simple example\n\n```tsx\nimport { VirtualListY } from \"react-virtual-overflow/lib/fixed-list-y\";\n\nfunction MyApp() {\n    const items = Array.from({ length: 300 }).map((_, i) =\u003e `item ${i}`);\n\n    const itemHeight = 40;\n\n    const renderItem = (item) =\u003e (\n        \u003cdiv style={{ height: '40px' }}\u003e{item}\u003c/div\u003e\n    );\n\n    return (\n        \u003cdiv style={{ overflowY: 'scroll', height: '300px', background: 'lightgreen' }}\u003e\n            {/* !this component will not add container with overflow! */}\n            {/* the only overflow here is element above */}\n            \u003cVirtualListY\n                items={items}\n                itemHeight={itemHeight}\n                itemKey={x =\u003e x}\n                renderItem={renderItem}\n            /\u003e\n        \u003c/div\u003e\n    );\n}\n```\n\n## Advanced example\n\nAdvanced example with hook\n\n```tsx\nimport { useVirtualOverflowY } from \"react-virtual-overflow\";\n\nfunction MyApp() {\n    const items = Array.from({ length: 300 }).map((_, i) =\u003e `item ${i}`);\n\n    const containerRef = useRef\u003cHTMLDivElement\u003e(undefined!);\n\n    const itemHeight = 40;\n\n    const { renderedItems } = useVirtualOverflowY({\n        containerRef,\n        itemsLengthY: items.length,\n        itemHeight,\n        renderItem: (itemIndex, offsetTop) =\u003e {\n            const item = items[itemIndex];\n            \n            return (\n                \u003cdiv style={{ position: \"absolute\", top: `${offsetTop}px` }} key={item}\u003e\n                    {item}\n                \u003c/div\u003e\n            )\n        },\n    }, []);\n\n    return (\n        \u003cdiv style={{ overflowY: \"scroll\", height: \"300px\" }}\u003e\n            \u003cdiv ref={containerRef} style={{ height: `${itemHeight * items.length}px` }}\u003e\n                {renderedItems}\n            \u003c/div\u003e\n        \u003c/div\u003e\n    );\n}\n```\n\n## Available hooks \u0026 components\n\n\u003cdetails\u003e\n\u003csummary\u003e\n\u003cb\u003eVertical list component\u003c/b\u003e\n\u003c/summary\u003e\n\n\u003cbr\u003e\n\nThis component is used to render vertical list\n\n```tsx\nimport { VirtualListY } from \"react-virtual-overflow/lib/fixed-list-y\";\n\ntype VirtualListYProps\u003cItemT\u003e = {\n    items: ItemT[],\n    itemHeight: number,\n    // used to calculate react key when rendering\n    itemKey: (item: ItemT, itemIndex: number) =\u003e string,\n    overscanItemsCount?: number,\n    renderItem: (item: ItemT, itemIndex: number, contentTopOffset: number) =\u003e React.ReactNode,\n    calcVisibleRect?: VirtualOverflowCalcVisibleRectFn\n};\n\nfunction MyApp() {\n    const items = Array.from({ length: 300 }).map((_, i) =\u003e `item ${i}`);\n\n    const itemHeight = 40;\n\n    const renderItem = (item) =\u003e (\n        \u003cdiv style={{ height: '40px' }}\u003e{item}\u003c/div\u003e\n    );\n\n    return (\n        \u003cdiv style={{ overflowY: 'scroll', height: '300px', background: 'lightgreen' }}\u003e\n            \u003cVirtualListY\n                items={items}\n                itemHeight={itemHeight}\n                itemKey={x =\u003e x}\n                renderItem={renderItem}\n            /\u003e\n        \u003c/div\u003e\n    );\n}\n```\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n\u003csummary\u003e\n\u003cb\u003eHorizontal list component\u003c/b\u003e\n\u003c/summary\u003e\n\n\u003cbr\u003e\n\nThis component is used to render horizontal list\n\n```tsx\nimport { VirtualListX } from \"react-virtual-overflow/lib/fixed-list-x\";\n\ntype VirtualListXProps\u003cItemT\u003e = {\n    items: ItemT[],\n    itemWidth: number,\n    itemKey: (item: ItemT, itemIndex: number) =\u003e string,\n    overscanItemsCount?: number,\n    renderItem: (item: ItemT, itemIndex: number, contentTopOffset: number) =\u003e React.ReactNode,\n    calcVisibleRect?: VirtualOverflowCalcVisibleRectFn\n};\n\nfunction MyApp() {\n    const items = Array.from({ length: 300 }).map((_, i) =\u003e `item ${i}`);\n\n    const itemWidth = 40;\n\n    const renderItem = (item) =\u003e (\n        \u003cdiv style={{ width: '40px' }}\u003e{item}\u003c/div\u003e\n    );\n\n    return (\n        \u003cdiv style={{ overflowX: 'scroll', height: '300px', background: 'lightgreen' }}\u003e\n            \u003cVirtualListX\n                items={items}\n                itemWidth={itemWidth}\n                itemKey={x =\u003e x}\n                renderItem={renderItem}\n            /\u003e\n        \u003c/div\u003e\n    );\n}\n```\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n\u003csummary\u003e\n\u003cb\u003eGrid component\u003c/b\u003e\n\u003c/summary\u003e\n\n\u003cbr\u003e\n\nThis component is used to render grid\n\n```tsx\nimport { VirtualGrid } from \"react-virtual-overflow/lib/fixed-grid\";\n\ntype VirtualGridProps\u003cItemT\u003e = {\n    // rows\n    items: ItemT[][],\n    columnsNum: number,\n    itemWidth: number,\n    itemHeight: number,\n    itemKey: (item: ItemT, itemIndexX: number, itemIndexY: number) =\u003e string,\n    overscanItemsCount?: number,\n    renderItem: (item: ItemT, itemIndexX: number, leftOffsetPx: number, itemIndexY: number, topOffsetPx: number) =\u003e React.ReactNode,\n    calcVisibleRect?: VirtualOverflowCalcVisibleRectFn\n};\n\nfunction GridExample() {\n    const items = itemsGrid;\n\n    return (\n        \u003cdiv style={{ overflowY: 'scroll', height: '300px', background: 'lightgreen' }}\u003e\n            \u003cVirtualGrid\n                items={items}\n                columnsNum={300}\n                itemWidth={40}\n                itemHeight={80}\n                itemKey={x =\u003e x}\n                overscanItemsCount={3}\n                renderItem={item =\u003e \u003cdiv style={{ width: '40px', height: '80px' }}\u003e{item}\u003c/div\u003e}\n            /\u003e\n        \u003c/div\u003e\n    );\n}\n```\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n\u003csummary\u003e\n\u003cb\u003eVertical list hook\u003c/b\u003e\n\u003c/summary\u003e\n\n\u003cbr\u003e\n\n`useVirtualOverflowY` hook that computes and renders vertical list\n\nIt accepts this params:\n\n```ts\ntype UseVirtualOverflowParamsY = {\n    // reference to container with elements (not scroll)\n    containerRef: React.MutableRefObject\u003cHTMLElement\u003e;\n\n    // total num of items\n    itemsLengthY: number;\n\n    // how to render each item\n    renderItem: (itemIndex: number, contentTopOffsetPx: number) =\u003e React.ReactNode;\n\n    // height of one item in pixels\n    itemHeight: number;\n\n    // how much items should be rendered beyond visible border\n    // default=3\n    overscanItemsCount?: number;\n\n    // function to calculate visible rect (check utils for other options)\n    calcVisibleRect?: CalcVisibleRectFn;\n};\n```\n\nAnd returns:\n\n```ts\n{\n    renderedItems: React.Node[],\n\n    // method that will force update calculations\n    updateViewRect: () =\u003e void,\n\n    itemSlice: {\n        topStartIndex: number;\n        lengthY: number;\n        leftStartIndex: number;\n        lengthX: number;\n    }\n}\n```\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n\u003csummary\u003e\n\u003cb\u003eHorizontal list hook\u003c/b\u003e\n\u003c/summary\u003e\n\n\u003cbr\u003e\n\n`useVirtualOverflowX` hook that computes and renders horizontal list\n\nIt accepts this params:\n\n```ts\ntype UseVirtualOverflowParamsX = {\n    // reference to container with elements (not scroll)\n    containerRef: React.MutableRefObject\u003cHTMLElement\u003e;\n\n    // total num of items\n    itemsLengthX: number;\n\n    // how to render each item\n    renderItem: (itemIndex: number, contentLeftOffsetPx: number) =\u003e React.ReactNode;\n\n    // width of one item in pixels\n    itemWidth: number;\n\n    // how much items should be rendered beyond visible border\n    // default=3\n    overscanItemsCount?: number;\n\n    // function to calculate visible rect (check utils for other options)\n    calcVisibleRect?: CalcVisibleRectFn;\n};\n```\n\nAnd returns:\n\n```ts\n{\n    renderedItems: React.Node[],\n\n    // method that will force update calculations\n    updateViewRect: () =\u003e void,\n\n    itemSlice: {\n        topStartIndex: number;\n        lengthY: number;\n        leftStartIndex: number;\n        lengthX: number;\n    }\n}\n```\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n\u003csummary\u003e\n\u003cb\u003eGrid hook\u003c/b\u003e\n\u003c/summary\u003e\n\n\u003cbr\u003e\n\n`useVirtualOverflowGrid` hook that computes and renders grid\n\nIt accepts this params:\n\n```ts\ntype UseVirtualOverflowParamsGrid = {\n    // reference to container with elements (not scroll)\n    containerRef: React.MutableRefObject\u003cHTMLElement\u003e;\n\n    // total num of items horizontal\n    itemsLengthX: number;\n\n    // total num of items vertical\n    itemsLengthY: number;\n\n    // how to render each item\n    renderItem: (itemIndexX: number, leftOffsetPx: number, itemIndexY: number, topOffsetPx: number) =\u003e React.ReactNode;\n\n    // width of one item in pixels\n    itemWidth: number;\n\n    // height of one item in pixels\n    itemHeight: number;\n\n    // how much items should be rendered beyond visible border\n    // default=3\n    overscanItemsCount?: number;\n\n    // function to calculate visible rect (check utils for other options)\n    calcVisibleRect?: CalcVisibleRectFn;\n};\n```\n\nAnd returns:\n\n```ts\n{\n    renderedItems: React.Node[],\n\n    // method that will force update calculations\n    updateViewRect: () =\u003e void,\n\n    itemSlice: {\n        topStartIndex: number;\n        lengthY: number;\n        leftStartIndex: number;\n        lengthX: number;\n    }\n}\n```\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n\u003csummary\u003e\n\u003cb\u003euseCalcVirtualOverflow - universal hook for fixed list/grid\u003c/b\u003e\n\u003c/summary\u003e\n\n\u003cbr\u003e\n\n`useCalcVirtualOverflow` hook that computes visible rect at calculates slice of items that should be rendered\n\nIt could be used if you want to render items manually, and you need only slice calculated\n\nIt accepts this params:\n\n```ts\ntype UseVirtualOverflowParams = {\n    containerRef: React.MutableRefObject\u003cHTMLElement\u003e,\n    itemsLengthX?: number,\n    itemsLengthY?: number,\n    /** if undefined, then horizontal calculation will be skipped */\n    itemWidth?: number,\n    /** if undefined, then vertical calculation will be skipped */\n    itemHeight?: number,\n    /** default=3 */\n    overscanItemsCount?: number,\n    calcVisibleRect?: VirtualOverflowCalcVisibleRectFn,\n};\n```\n\nAnd returns:\n\n```ts\n{\n    itemSlice: {\n        topStartIndex: number;\n        lengthY: number;\n        leftStartIndex: number;\n        lengthX: number;\n    };\n    updateViewRect: () =\u003e void;\n}\n```\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n\u003csummary\u003e\n\u003cb\u003eCalculate visible on screen rect\u003c/b\u003e\n\u003c/summary\u003e\n\n\u003cbr\u003e\n\n`virtualOverflowCalcVisibleRect` method will calculate on screen visible rect of some element\n\nIt accepts this params:\n\n```ts\nfunction virtualOverflowCalcVisibleRect(element: HTMLElement): {\n    top: number;\n    left: number;\n    bottom: number;\n    right: number;\n    contentOffsetTop: number;\n    contentOffsetLeft: number;\n    contentVisibleHeight: number;\n    contentVisibleWidth: number;\n};\n```\n\n\u003c/details\u003e\n\n\n\n\u003cdetails\u003e\n\u003csummary\u003e\n\u003cb\u003eSlice calculation from visible rect\u003c/b\u003e\n\u003c/summary\u003e\n\n\u003cbr\u003e\n\n`virtualOverflowCalcItems` method will calculate slice of items from visible rect\n\nYou can pass here horizontal and vertical values from \"calcVisibleRect\" method.\n\nThis method is axis-agnostic, so you just first calculate vertical data by passing vertical coords of rect, and then (if you need) horizontal.\n\n```ts\nfunction virtualOverflowCalcItems(\n    contentOffsetStartPx: number,\n    contentVisibleSizePx: number,\n    itemSize: number,\n    overscanItemsCount: number,\n    itemsLength: number\n);\n\n// returns\n{\n    // index of starting item that should be rendered (including overscan)\n    itemStart: number,\n    // total count of items (including start \u0026 end overscan)\n    itemLen: number\n};\n\n// Example for vertical slice calculation:\nconst visibleRect = calcVisibleRect(containerRef.current);\nconst verticalSlice = virtualOverflowCalcItems(\n    visibleRect.contentOffsetTop,\n    visibleRect.contentVisibleHeight,\n    itemHeight,\n    overscanItemsCount,\n    itemsLengthY\n);\n```\n\n\u003c/details\u003e\n\n\n\n\u003cdetails\u003e\n\u003csummary\u003e\n\u003cb\u003eInfinity loader\u003c/b\u003e\n\u003c/summary\u003e\n\n\u003cbr\u003e\n\nAll hooks (`useCalcVirtualOverflow`, `useVirtualOverflowY`, `useVirtualOverflowX`, `useVirtualOverflowGrid`) returns `itemSlice` which you can use to trigger infinity loading.\n\nFor example:\n\n```tsx\nconst [items, setItems] = useState([] as any[]);\n\n// here we get current rendered itemSlice\nconst { renderedItems, itemSlice } = useVirtualOverflowY({\n    itemsLengthY: items.length,\n    // ...\n});\n\n// here we check if we render bottom range of items\nuseEffect(() =\u003e {\n    if (itemSlice.topStartIndex + itemSlice.lengthY \u003e= items.length - 4) {\n        // load more\n        setItems((prev) =\u003e [...prev, ...newItems]);\n    }\n}, [itemSlice.topStartIndex, itemSlice.lengthY]);\n```\n\n\u003c/details\u003e\n\n\n### utils\n\nAll methods here are inside `virtualOverflowUtils` namespace in `react-virtual-overflow/utils`. I will not write namespace here below for readability purposes.\n\nUsually react app is static, so you dont need to calc all parents rect (except if it has floating parents, than use default method).  \nSo for this case better use `calcVisibleRectOverflowed`.\n\nThis method will find only parents with overflow style set and calculate clipping only with them. It may boost performance for some cases.\n\nAlso if you know all containers with scroll (which you can find with `findScrollContainerTopStack`) you can calculate directly with `calcVisibleRectWithStack`.\n\n```tsx\nimport { virtualOverflowUtils } from \"react-virtual-overflow/lib/utils\";\n\n// in component\nconst [parentsWithOverflow, setParentsWithOverflow] = useState([] as any[]);\n\nuseLayoutEffect(() =\u003e {\n    // we find all elements with overflow once\n    const stack = findScrollContainerTopStack(containerRef.current);\n    setParentsWithOverflow(stack);\n}, []);\n\nconst { renderedItems } = useVirtualOverflowY({\n    containerRef,\n    itemsLengthY,\n    itemHeight,\n    calcVisibleRect: (el: HTMLElement) =\u003e {\n        // calculate only by found overflows\n        return virtualOverflowUtils.calcVisibleRectWithStack(el, parentsWithOverflow);\n    },\n    renderItem,\n},\n    // add overflow stack to deps\n    [parentsWithOverflow]\n);\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmorglod%2Freact-virtual-overflow","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmorglod%2Freact-virtual-overflow","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmorglod%2Freact-virtual-overflow/lists"}