inokawa/virtua: a zero-config virtual list and grid for five frameworks
A zero-config, fast and small virtual list and grid component for React, Vue, Solid, Svelte and Angular.
At a glance
- What is it?
- virtua renders only the rows a user can see, ships separate entry points for React, Vue, Solid, Svelte and Angular, and asks for almost no configuration. The trade-off is a set of browser requirements the README states plainly.
- Who is it for?
- Adopt virtua if you already render long lists in React, Vue, Solid, Svelte or Angular and want dynamic row heights without hand-written measurement code. Do not adopt it if you must support browsers without ResizeObserver, or if you set horizontal: true on a browser without CSS inset-inline-start, since the README states that one cannot be polyfilled and points to virtua@<=0.50 instead.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 7, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem virtua solves: rendering a thousand rows without rendering a thousand rows
A list of 10,000 rows in a scrollable div means 10,000 DOM nodes. Layout and paint cost grows with the node count even when the viewport shows twenty of them. Virtualization fixes this by mounting only the visible window plus a small overscan buffer, and replacing the rest with spacer space so the scrollbar keeps its proportions.
The hard part is not the arithmetic. It is that real rows are not all the same height. A chat message, a table cell with wrapped text, an image that has not loaded yet: each one changes the measured size after mount, and a naive implementation either jumps or drifts. virtua's stated goal is to handle those cases without asking the developer to declare sizes up front. The README lists dynamic size measurement, scroll position adjustment during reverse scrolling and imperative scrolling, and iOS support as things the library deals with rather than the application.
The audience is front-end engineers who already have a long list and do not want to write measurement code for it. The README's React example passes a style with a random height per row and nothing else, which is the whole configuration surface for the basic case.
How virtua works: one core, five framework entry points
The package.json exports map is the clearest description of the architecture. The root export (".") is the React build, and there are separate subpath exports for "./vue", "./solid", "./svelte" and "./angular". There is also "./unstable_core", typed as lib/core/index.d.ts, which is where the shared logic lives. The name says unstable, so treat it as an internal seam rather than a public API.
The build script confirms this: it runs vite build once per mode (react, vue, solid, solid-ssr, core, svelte, angular) and then runs ngc against tsconfig.angular.build.json plus a script that copies Angular type declarations. Each framework gets its own compiled output rather than a runtime adapter over one generic renderer.
On the component side the README documents four React components with different contracts. VList is the drop-in replacement for a scrollable div. Virtualizer is the lower-level primitive for custom markup, and it takes a startMargin prop, shown as 40 in the README, so the virtualizer knows how much content sits above it inside the same scroll container. WindowVirtualizer virtualizes against the document scroll rather than a nested element. VGrid handles tabular data with a columns array of key and width objects. The Vue, Solid, Svelte and Angular builds mirror this naming under their own subpaths.
One design decision worth noting: the README says VGrid requires CSS subgrid. That is a real constraint, not a detail, and it rules out older Safari versions for the grid component specifically.
Installing virtua and rendering a first dynamic-height list
Installation is a single npm command. The README gives this exact form:
npm install virtuaFor React, the README states react >= 16.14 is required, and adds that if you use ESM with webpack 5 you should use react >= 18 to avoid the "Can't resolve react/jsx-runtime" error. That second point is a build-tool interaction, not a virtua bug, but it will surface the moment you try to bundle.
The smallest working example passes children and a fixed height on the container:
import { VList } from "virtua";
export const App = () => {
return (
<VList style={{ height: 800 }}>
{Array.from({ length: 1000 }).map((_, i) => (
<div key={i} style={{ height: 50 }}>
{i}
</div>
))}
</VList>
);
};You should see a scrollable area 800 pixels tall containing numbered rows, with the DOM holding only the visible slice plus overscan. If you inspect the element tree, the row count will be far below 1000.
When the markup needs to sit inside a container that already scrolls, or when there is a header above the list, the README switches to Virtualizer with startMargin:
import { Virtualizer } from "virtua";
export const App = () => {
return (
<div style={{ overflowY: "auto", overflowAnchor: "none", height: 800 }}>
<div style={{ height: 40 }}>header</div>
<Virtualizer startMargin={40}>
{Array.from({ length: 1000 }).map((_, i) => (
<div key={i} style={{ height: 50 }}>{i}</div>
))}
</Virtualizer>
</div>
);
};The overflowAnchor: "none" in that snippet is worth copying verbatim. It disables the browser's scroll anchoring, which otherwise fights the virtualizer when content above the viewport changes size.
The browser requirements the README states, and why they are the real constraint
virtua does not ship polyfills, and the README is explicit about what it assumes. ResizeObserver is always required, with @juggle/resize-observer named as an example polyfill. Scroll methods on elements are always required, with element-scroll-polyfill named. CSS scroll-behavior is required only if you call scrollToIndex with smooth: true, with scroll-behavior-polyfill named.
The fourth item is the one that should decide an adoption question. CSS inset-inline-start is required only if you set horizontal: true, and the README states it cannot be polyfilled. The fallback the README offers is to use virtua@<=0.50 instead. So a horizontal list on an old browser is not a configuration problem, it is a version problem, and the fix is to stay on an older major line.
The fifth item applies only to VGrid: CSS subgrid is required. A team that needs a virtualized table on a browser without subgrid support cannot use VGrid at all, though VList still works.
None of this is hidden. The README puts the polyfill list directly under the install command, which is the right place for it. The practical consequence is that virtua is a poor fit for products with a hard legacy-browser floor, and a good fit for anything targeting current evergreen browsers.
Where virtua is the wrong tool
The clearest limitation is the one above: horizontal virtualization on browsers without CSS inset-inline-start is unsupported in current versions, and the README says it cannot be polyfilled. If your product matrix includes those browsers and your list scrolls sideways, the current release is not an option.
A second limitation is the unstable_core export. It is present in the exports map and typed, so it is reachable, but the name is the project telling you not to build on it. If you are writing a framework binding that virtua does not ship, you are working against an interface the maintainers have reserved the right to change.
A third case is scale mismatch. Virtualization adds measurement, scroll listeners and a spacer layer. For a list of fifty items that all fit in a normal scroll container, that machinery buys nothing and makes debugging harder, because the DOM no longer matches the data.
Finally, the README does not document rollback or a migration path between minor versions. There is no changelog excerpt in the repository, only the release list. A team that pins virtua and later needs to move a version has no documented downgrade procedure to follow.
Alternatives and how they differ in approach
The README links a comparison section, so the project positions itself against other virtualization libraries rather than pretending the space is empty. The difference in approach is visible in the package layout.
Most virtual list libraries in this space are single-framework. react-window and react-virtualized are React-only, and their APIs are built around explicit itemSize or a measurement callback. virtua's root export is React, but Vue, Solid, Svelte and Angular live in the same package under subpath exports, sharing a core build. If your codebase spans two of those frameworks, one dependency and one mental model covers both.
The second difference is configuration. Libraries in the react-window lineage generally ask you to supply a size estimator or accept fixed heights. virtua's README frames measurement as the library's job, and the React example passes randomly sized rows with no size prop anywhere. That is the trade the project is making: less control at the call site, more work inside the component.
The third difference is the primitive layer. Virtualizer and WindowVirtualizer expose the scroll container decision to the caller, so a list can be virtualized against a nested div or against the document. Libraries that only ship a self-contained list component make that choice for you.
Maintenance, licence and upgrade cost
The repository is not archived. The last push was on 2026-09-23, and the most recent releases listed are 0.52.7 on 2026-09-22, 0.52.6 on 2026-09-22 and 0.52.5 on 2026-09-21. Three patch releases inside two days suggests a fast patch cadence, and the version numbers staying in the 0.52.x line means the project has not declared a 1.0 API freeze.
That cadence cuts both ways. Fixes arrive quickly, and so do version bumps you may need to track. A renovate.json sits at the repository root, which is consistent with automated dependency updates on the maintainer's side. On your side, the practical cost is re-running your test suite against each patch, particularly if you depend on Virtualizer's startMargin behaviour or on scrollToIndex with smooth: true.
The licence is MIT, which permits commercial use, modification and redistribution provided the copyright notice and permission notice are retained. That is the standard permissive arrangement and it is compatible with proprietary applications. This is a description of the licence text, not legal advice; have your own counsel review anything that matters to your organisation.
The package.json marks sideEffects as false and ships only the lib directory, so bundlers can tree-shake unused components. The README states components start from roughly 3kB gzipped, which is the project's own figure rather than a measurement.
Editorial conclusion
Adopt virtua if you already render long lists in React, Vue, Solid, Svelte or Angular and want dynamic row heights without hand-written measurement code. Do not adopt it if you must support browsers without ResizeObserver, or if you set horizontal: true on a browser without CSS inset-inline-start, since the README states that one cannot be polyfilled and points to virtua@<=0.50 instead. Before committing, check the src/ directory for the components your framework needs and confirm that the version you install exports them.
Frequently asked questions
What is virtua in the inokawa/virtua project?
It is a virtual list and grid component for React, Vue, Solid, Svelte and Angular. The README describes it as zero-config, and it renders only the visible slice of a long list rather than every row.
How do I install virtua in a React project?
Run npm install virtua, then import VList from "virtua". The README states react >= 16.14 is required, and react >= 18 if you use ESM with webpack 5.
Does virtua work with Vue, Solid, Svelte and Angular?
Yes. The package.json exports map defines separate subpath exports for "./vue", "./solid", "./svelte" and "./angular", each with its own compiled output and type declarations.
What browser APIs does virtua require?
ResizeObserver and scroll methods on elements are always required, and CSS scroll-behavior is required only when calling scrollToIndex with smooth: true. CSS inset-inline-start is required for horizontal lists and the README states it cannot be polyfilled.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/inokawa-virtua)