https://framer.com/m/VirtulList-G1tWnr.js

Io / UI Basics / VirtualList

VirtualList

VirtualList scrolls a million rows by rendering only the ones in view. Rows come from generated mock data, a static JSON, NDJSON or CSV file, or a paged range API, and search, sort and filtering run in a Web Worker so the page never blocks. Every colour resolves from the project tokens, so the list follows light and dark mode.

Data-driven34 controls1M rows · worker · paged API

Examples

Live specimens for every control the component exposes.

One million rows

Mock Rows is set to 1,000,000. The rows are generated once in a worker and the DOM only ever holds the visible rows plus overscan, so scrolling stays smooth at any size. The Framer canvas caps its preview at 500 rows; the published page scrolls the full million.

Loading rows…
0 rows

Selection

selection is none, single or multiple. Multiple takes Shift for a range and Cmd/Ctrl to toggle a row. Arrow keys, Page Up/Down, Home and End move focus, Enter or Space selects, and the footer counts the selection.

Loading rows…
0 rows

Columns and sorting

columns picks, orders and sizes the columns by key: label, width (0 is flexible) and align. Left empty, every column in the data appears. Click a header to sort ascending, again for descending, a third time to clear.

Loading rows…
0 rows

Filter

filterJSON narrows the rows before search and sort run. Nodes nest with and / or; leaf ops are eq, ne, in, gte, lte and contains. This one keeps Active rows with an amount of 5,000 or more. Invalid JSON is ignored and flagged in the footer.

Loading rows…
0 rows

Density and frame

cellPadding and rowHeight set the density from the theme padding scale; radius, border and shadow set the frame. autoHeight lets rows grow to fit wrapped text instead of truncating to one line.

Loading rows…

cellPadding XS · rowHeight 32 · search and footer off

Loading rows…
0 rows

cellPadding Large · rowHeight 48 · radius Large · shadow Normal

Data sources

source picks where rows come from. Static URL loads one file and parses it in a worker. Paged API speaks a small range protocol, below, so the server only ever sends the block being scrolled to. In both modes the canvas preview shows sample data.

StepRequestBehaviour
Open a queryPOST {apiBase}/querySends sort, search and filter; returns total, schema, queryId.
Fetch a blockGET {apiBase}/query/{queryId}/rowsstart and count return one block of blockSize rows.
Small resultqueryId nullTotal fits inlineLimit: all rows inline, searched in memory.
Snapshot expiredHTTP 410Reopens the same query and keeps the scroll position.
Static fileurl + formatJSON, header-row arrays, NDJSON or CSV, parsed in a worker.
AuthorizationAuthorization headerSent on every request when Auth is set. On 401 the token is resolved again and retried once.
Rate limitedHTTP 429 / 503Backs off 2, 4, 8 … 30 s per block, honouring Retry-After. Two failures show Retry in the footer.
StepRequestBehaviour
Open a queryPOST {apiBase}/querySends sort, search and filter; returns total, schema, queryId.
Fetch a blockGET {apiBase}/query/{queryId}/rowsstart and count return one block of blockSize rows.
Small resultqueryId nullTotal fits inlineLimit: all rows inline, searched in memory.
Snapshot expiredHTTP 410Reopens the same query and keeps the scroll position.
Static fileurl + formatJSON, header-row arrays, NDJSON or CSV, parsed in a worker.
AuthorizationAuthorization headerSent on every request when Auth is set. On 401 the token is resolved again and retried once.
Rate limitedHTTP 429 / 503Backs off 2, 4, 8 … 30 s per block, honouring Retry-After. Two failures show Retry in the footer.

Props

The 34 controls exposed on the instance panel. URL, API and Auth controls appear only when their source is selected.

PropTypeBehaviour
sourceenumMock generates rows, Static URL loads one file, Paged API pages from a server.
mockCountnumberRows generated in Mock mode, up to 2,000,000. Default 100,000; the canvas caps at 500.
urlstringStatic URL mode: the file to load. Parsed in a Web Worker.
formatenumAuto, JSON, NDJSON or CSV. Auto detects the format from the response.
apiBasestringPaged API mode: the base URL for the query and row-block requests.
inlineLimitnumberResults up to this size load in one response and are searched in memory. Default 1,000.
blockSizenumberRows per request in paged mode, 50 to 1,000, capped by the server's maxCount. Default 200.
sendCookiesbooleanSend or omit cookies on URL and API requests.
authSourceenumNone, Static Token, Browser Storage or Function. Adds an Authorization header to every data request; on a 401 the token is resolved again and the request retried once. Hidden in Mock mode.
authSchemestringPrefixed to bare tokens. Default Bearer. Ignored when the token already contains a space; empty sends the raw value.
authTokenstringStatic Token mode: the token itself. Baked into the published site and visible to every visitor, so use only read-only, revocable tokens.
authStorageKeystringBrowser Storage mode: the localStorage (then sessionStorage) key, read fresh on every request. JSON values use access_token, accessToken or token.
authFunctionstringFunction mode: name of a window function returning a token or Promise<string>. Called with { refresh: true } after a 401. Default getAuthToken.
filterJSONstringA filter tree applied before search and sort: and / or nodes; eq, ne, in, gte, lte, contains.
columnsarrayColumns to show, in order: key, label, width (0 is flexible), align. Empty shows every column.
rowHeightnumberExact row height, or the minimum and estimate when autoHeight is on. Default 40.
autoHeightbooleanWrap lets rows grow to fit wrapped text; Fixed keeps one truncated line per row.
overscannumberExtra rows rendered above and below the viewport, 0 to 50. Default 8.
selectionenumNone, Single or Multiple. Multiple adds Shift ranges and Cmd/Ctrl toggles.
showSearchbooleanShows the search box. Search covers the string and date columns.
searchPlaceholderstringPlaceholder text in the search box. Default Search.
showFooterbooleanShows the footer: row count, selection count and status messages.
cellPaddingenumXS, Small, Normal or Large, from the theme padding scale. Default Small.
radiusenumNone, Tiny, Small, Normal or Large corner radius. Default Normal.
borderenumNone, Small, Normal or Large outer border. Default Normal.
shadowenumNone, Small, Normal or Large outer shadow. Default None.
backgroundcolorBody fill. Empty follows the theme.
headerBackgroundcolorHeader row fill. Empty follows the theme.
textColorcolorCell text. Empty follows the theme.
secondaryTextColorcolorHeader labels and footer text. Empty follows the theme.
dividerColorcolorRow, header and footer dividers. Empty follows the theme.
eventIdstringSent as listId with every row click so listeners on the window event can tell lists apart.
onRowClickeventFires when a row is clicked, or activated with Enter or Space, with { listId, index, id, row, selected, selectedIds, trigger }. Also dispatched as the window event virtuallist:rowclick.
onLoadErroreventFires when a data request fails. Also dispatched as the window event virtuallist:loaderror.
PropTypeBehaviour
sourceenumMock generates rows, Static URL loads one file, Paged API pages from a server.
mockCountnumberRows generated in Mock mode, up to 2,000,000. Default 100,000; the canvas caps at 500.
urlstringStatic URL mode: the file to load. Parsed in a Web Worker.
formatenumAuto, JSON, NDJSON or CSV. Auto detects the format from the response.
apiBasestringPaged API mode: the base URL for the query and row-block requests.
inlineLimitnumberResults up to this size load in one response and are searched in memory. Default 1,000.
blockSizenumberRows per request in paged mode, 50 to 1,000, capped by the server's maxCount. Default 200.
sendCookiesbooleanSend or omit cookies on URL and API requests.
authSourceenumNone, Static Token, Browser Storage or Function. Adds an Authorization header to every data request; on a 401 the token is resolved again and the request retried once. Hidden in Mock mode.
authSchemestringPrefixed to bare tokens. Default Bearer. Ignored when the token already contains a space; empty sends the raw value.
authTokenstringStatic Token mode: the token itself. Baked into the published site and visible to every visitor, so use only read-only, revocable tokens.
authStorageKeystringBrowser Storage mode: the localStorage (then sessionStorage) key, read fresh on every request. JSON values use access_token, accessToken or token.
authFunctionstringFunction mode: name of a window function returning a token or Promise<string>. Called with { refresh: true } after a 401. Default getAuthToken.
filterJSONstringA filter tree applied before search and sort: and / or nodes; eq, ne, in, gte, lte, contains.
columnsarrayColumns to show, in order: key, label, width (0 is flexible), align. Empty shows every column.
rowHeightnumberExact row height, or the minimum and estimate when autoHeight is on. Default 40.
autoHeightbooleanWrap lets rows grow to fit wrapped text; Fixed keeps one truncated line per row.
overscannumberExtra rows rendered above and below the viewport, 0 to 50. Default 8.
selectionenumNone, Single or Multiple. Multiple adds Shift ranges and Cmd/Ctrl toggles.
showSearchbooleanShows the search box. Search covers the string and date columns.
searchPlaceholderstringPlaceholder text in the search box. Default Search.
showFooterbooleanShows the footer: row count, selection count and status messages.
cellPaddingenumXS, Small, Normal or Large, from the theme padding scale. Default Small.
radiusenumNone, Tiny, Small, Normal or Large corner radius. Default Normal.
borderenumNone, Small, Normal or Large outer border. Default Normal.
shadowenumNone, Small, Normal or Large outer shadow. Default None.
backgroundcolorBody fill. Empty follows the theme.
headerBackgroundcolorHeader row fill. Empty follows the theme.
textColorcolorCell text. Empty follows the theme.
secondaryTextColorcolorHeader labels and footer text. Empty follows the theme.
dividerColorcolorRow, header and footer dividers. Empty follows the theme.
eventIdstringSent as listId with every row click so listeners on the window event can tell lists apart.
onRowClickeventFires when a row is clicked, or activated with Enter or Space, with { listId, index, id, row, selected, selectedIds, trigger }. Also dispatched as the window event virtuallist:rowclick.
onLoadErroreventFires when a data request fails. Also dispatched as the window event virtuallist:loaderror.

Usage

Guidance carried over from the component definition.

Reach for VirtualList

Tens of thousands to millions of rows that scroll as one list.

The data is a static JSON, NDJSON or CSV file, or a server that pages by range.

Readers need instant search, column sort and a structured filter.

Rows need single or range selection with full keyboard support.

Reach for something else

A few hundred rows, or cells need badge, currency or percentage rendering: use TableX.

Cells need inline editing, grouping or tree rows.

The items are cards or media rather than tabular records.

Io — UI Basics

Every list above is a live VirtualList instance running on generated mock rows, not real data. Colour, type and radius resolve from the project theme tokens.

io

An experiment in what a design system becomes when AI can consume it.

© 2026 io

Contact me

Create a free website with Framer, the website builder loved by startups, designers and agencies.