sveltelean
Versionv0.2.0 GitHub

Sizes per entry point

Production output of each package, measured by its own yarn size. The budget column shows the value the script fails on, where one exists; rows without a budget are recorded for comparison (the esbuild figures keep helpers behind folded development gates and are not the quoted number). Method and environment: Methodology.

@svelte-lean/primitives

SubjectEntry or toolrawgzipbrotliBudgetStatusNotes
core-onlyvite1590 B841 B730 Bnone (recorded for comparison)––
core-onlyesbuild2461 B1247 B1094 Bnone (recorded for comparison)––
tabsvite3639 B1693 B1521 B1700 B brotliwithin budgetno diagnostics · registration kept
tabsesbuild4875 B2249 B2021 Bnone (recorded for comparison)–diagnostics: __SLEAN_DEBUG__, Svelte Lean Runtime, WeakSet
menuvite4765 B2162 B1932 B2100 B brotliwithin budgetno diagnostics · registration kept
menuesbuild5830 B2661 B2399 Bnone (recorded for comparison)–diagnostics: __SLEAN_DEBUG__, Svelte Lean Runtime, WeakSet
listboxvite4268 B2030 B1838 B2000 B brotliwithin budgetno diagnostics · registration kept
listboxesbuild5470 B2568 B2320 Bnone (recorded for comparison)–diagnostics: __SLEAN_DEBUG__, Svelte Lean Runtime, WeakSet
comboboxvite6855 B2849 B2564 B2900 B brotliwithin budgetno diagnostics · registration kept
comboboxesbuild8288 B3460 B3143 Bnone (recorded for comparison)–diagnostics: __SLEAN_DEBUG__, Svelte Lean Runtime, WeakSet
selectvite9526 B3731 B3362 B3700 B brotliwithin budgetno diagnostics · registration kept
selectesbuild10912 B4351 B3925 Bnone (recorded for comparison)–diagnostics: __SLEAN_DEBUG__, Svelte Lean Runtime, WeakSet
calendarvite10627 B4268 B3925 B4300 B brotliwithin budgetno diagnostics · registration kept
calendaresbuild11806 B4815 B4418 Bnone (recorded for comparison)–diagnostics: __SLEAN_DEBUG__, Svelte Lean Runtime, WeakSet
date-pickervite13726 B5364 B4912 B5400 B brotliwithin budgetno diagnostics · registration kept
date-pickeresbuild15377 B6098 B5594 Bnone (recorded for comparison)–diagnostics: __SLEAN_DEBUG__, Svelte Lean Runtime, WeakSet
tooltipvite3165 B1473 B1297 B1400 B brotliwithin budgetno diagnostics · registration kept
tooltipesbuild4341 B2011 B1787 Bnone (recorded for comparison)–diagnostics: __SLEAN_DEBUG__, Svelte Lean Runtime, WeakSet
toastvite3403 B1547 B1396 B1600 B brotliwithin budgetno diagnostics · registration kept
toastesbuild4701 B2124 B1906 Bnone (recorded for comparison)–diagnostics: __SLEAN_DEBUG__, Svelte Lean Runtime, WeakSet
treevite4817 B2139 B1919 B2100 B brotliwithin budgetno diagnostics · registration kept
treeesbuild5927 B2633 B2380 Bnone (recorded for comparison)–diagnostics: __SLEAN_DEBUG__, Svelte Lean Runtime, WeakSet
range-slidervite2919 B1426 B1291 B1400 B brotliwithin budgetno diagnostics · registration kept
range-slideresbuild3954 B1926 B1744 Bnone (recorded for comparison)–diagnostics: __SLEAN_DEBUG__, Svelte Lean Runtime, WeakSet
number-fieldvite2519 B1228 B1082 B1200 B brotliwithin budgetno diagnostics · registration kept
number-fieldesbuild3581 B1714 B1526 Bnone (recorded for comparison)–diagnostics: __SLEAN_DEBUG__, Svelte Lean Runtime, WeakSet
togglevite1964 B1005 B888 B1000 B brotliwithin budgetno diagnostics · registration kept
toggleesbuild2993 B1486 B1329 Bnone (recorded for comparison)–diagnostics: __SLEAN_DEBUG__, Svelte Lean Runtime, WeakSet
toggle-groupvite3734 B1756 B1569 B1800 B brotliwithin budgetno diagnostics · registration kept
toggle-groupesbuild4944 B2296 B2063 Bnone (recorded for comparison)–diagnostics: __SLEAN_DEBUG__, Svelte Lean Runtime, WeakSet
toolbarvite3459 B1671 B1494 B1700 B brotliwithin budgetno diagnostics · registration kept
toolbaresbuild4563 B2167 B1940 Bnone (recorded for comparison)–diagnostics: __SLEAN_DEBUG__, Svelte Lean Runtime, WeakSet
splittervite4355 B1881 B1683 B1900 B brotliwithin budgetno diagnostics · registration kept
splitteresbuild5499 B2428 B2178 Bnone (recorded for comparison)–diagnostics: __SLEAN_DEBUG__, Svelte Lean Runtime, WeakSet
context-menuvite3286 B1574 B1409 B1600 B brotliwithin budgetno diagnostics · registration kept
context-menuesbuild4323 B2056 B1856 Bnone (recorded for comparison)–diagnostics: __SLEAN_DEBUG__, Svelte Lean Runtime, WeakSet
file-dropvite2627 B1244 B1107 B1300 B brotliwithin budgetno diagnostics · registration kept
file-dropesbuild3706 B1742 B1562 Bnone (recorded for comparison)–diagnostics: __SLEAN_DEBUG__, Svelte Lean Runtime, WeakSet
hover-cardvite3312 B1530 B1353 B1500 B brotliwithin budgetno diagnostics · registration kept
hover-cardesbuild4464 B2052 B1828 Bnone (recorded for comparison)–diagnostics: __SLEAN_DEBUG__, Svelte Lean Runtime, WeakSet
menubarvite3464 B1623 B1457 B1700 B brotliwithin budgetno diagnostics · registration kept
menubaresbuild4495 B2097 B1886 Bnone (recorded for comparison)–diagnostics: __SLEAN_DEBUG__, Svelte Lean Runtime, WeakSet
native-onlyvite11810 B3207 B2681 Bnone (recorded for comparison)–no runtime

Method: ESM, target es2022, gzip level 9, brotli quality 11 (node:zlib). Tier 1 and Tier 2 entries are dist/<name>/register.js with @svelte-lean/core from the workspace. "vite" builds the public specifier with Vite in production mode; the brotli budget applies to it and it must list no diagnostics and keep the registration. "esbuild" bundles the same file with esbuild under the production condition; it keeps development helpers core references from folded DEV gates and is recorded for comparison. "core-only" is a registration with an empty handler. "native-only" imports the root entry and a Tier 0 subpath and must contain no runtime. Command: yarn workspace @svelte-lean/primitives size. Date: not recorded in the file; sizes are deterministic for the toolchain versions listed in docs/compatibility.md.

@svelte-lean/core

SubjectEntry or toolrawgzipbrotliBudgetStatusNotes
productionesbuild4429 B2113 B1927 B2500 B gzipwithin budgetdiagnostics: __SLEAN_DEBUG__, Svelte Lean Runtime, validate, WeakSet, is not a routed event type
defaultesbuild4632 B2197 B1990 B2500 B gzipwithin budgetdiagnostics: console.warn, __SLEAN_DEBUG__, Svelte Lean Runtime, validate, WeakSet, is not a routed event type
consumervite1936 B994 B882 B2500 B gzipwithin budgetno diagnostics

Method: ESM, target es2022, gzip level 9, brotli quality 11 (node:zlib). "production" and "default" are dist/index.js with every export, esbuild bundle + minify, under the production export condition and the runtime-detected env module respectively. "consumer" is one Tier 1 behavior using parts and emit, built with Vite in production mode; it must list no diagnostics. Command: yarn workspace @svelte-lean/core size. Date: not recorded in the file; sizes are deterministic for the toolchain versions listed in docs/compatibility.md.

@svelte-lean/table

SubjectEntry or toolrawgzipbrotliBudgetStatusNotes
rendererdist/index.js64638 B20031 B17886 B22000 B gzipwithin budget–
headlessdist/core.js5338 B2475 B2280 B2700 B gzipwithin budget–
enginedist/table.svelte.js8416 B3494 B3204 B3600 B gzipwithin budget–
groupingdist/grouping.js4675 B2148 B1963 B2400 B gzipwithin budget–
pivotdist/pivot.js948 B526 B470 B600 B gzipwithin budget–
editingdist/editing.js1367 B707 B617 B800 B gzipwithin budget–
csvdist/export.js1220 B741 B646 B850 B gzipwithin budget–
clipboarddist/clipboard.js1721 B895 B817 B1000 B gzipwithin budget–
serverdist/server.js561 B369 B310 B425 B gzipwithin budget–
statedist/state.js1793 B796 B711 B900 B gzipwithin budget–
stylesdist/style.css30608 B6665 B5704 B7300 B gzipwithin budget–

Method: Each entry bundled independently with esbuild (bundle + minify, ESM); shared code is counted per entry here and de-duplicated by consumer bundlers. Svelte peer runtime excluded. gzip level 9 / brotli quality 11 (node:zlib). Budgets are gzip. Command: yarn workspace @svelte-lean/table size. Date: not recorded in the file; sizes are deterministic for the toolchain versions listed in docs/compatibility.md.

@svelte-lean/styles

SubjectEntry or toolrawgzipbrotliBudgetStatusNotes
accordion.css–3585 B816 B660 Bnone (recorded for comparison)–minified before compression
alert-dialog.css–2814 B559 B457 Bnone (recorded for comparison)–minified before compression
alert.css–5201 B798 B665 Bnone (recorded for comparison)–minified before compression
autocomplete.css–3065 B594 B482 Bnone (recorded for comparison)–minified before compression
avatar.css–2726 B545 B437 Bnone (recorded for comparison)–minified before compression
badge.css–2086 B488 B387 Bnone (recorded for comparison)–minified before compression
base.css–1789 B360 B290 Bnone (recorded for comparison)–minified before compression
breadcrumb.css–3023 B647 B532 Bnone (recorded for comparison)–minified before compression
button-group.css–3438 B503 B404 Bnone (recorded for comparison)–minified before compression
button.css–6825 B1013 B863 Bnone (recorded for comparison)–minified before compression
calendar.css–9719 B1312 B1121 Bnone (recorded for comparison)–minified before compression
card.css–4365 B857 B686 Bnone (recorded for comparison)–minified before compression
carousel.css–6672 B978 B841 Bnone (recorded for comparison)–minified before compression
checkbox.css–3496 B764 B615 Bnone (recorded for comparison)–minified before compression
color-field.css–3741 B602 B488 Bnone (recorded for comparison)–minified before compression
combobox.css–1206 B178 B144 Bnone (recorded for comparison)–minified before compression
context-menu.css–1101 B246 B219 Bnone (recorded for comparison)–minified before compression
control.css–16971 B1960 B1698 Bnone (recorded for comparison)–minified before compression
date-field.css–2929 B654 B550 Bnone (recorded for comparison)–minified before compression
date-picker.css–4889 B812 B687 Bnone (recorded for comparison)–minified before compression
descriptions.css–3261 B554 B451 Bnone (recorded for comparison)–minified before compression
dialog.css–4571 B898 B728 Bnone (recorded for comparison)–minified before compression
disclosure.css–2433 B638 B509 Bnone (recorded for comparison)–minified before compression
drawer.css–6867 B1152 B970 Bnone (recorded for comparison)–minified before compression
empty.css–3302 B663 B553 Bnone (recorded for comparison)–minified before compression
field.css–2468 B455 B368 Bnone (recorded for comparison)–minified before compression
file-drop.css–3109 B659 B548 Bnone (recorded for comparison)–minified before compression
file-field.css–3735 B702 B598 Bnone (recorded for comparison)–minified before compression
hover-card.css–3000 B621 B516 Bnone (recorded for comparison)–minified before compression
index.css–1854 B371 B309 Bnone (recorded for comparison)–minified before compression
input.css–4163 B680 B560 Bnone (recorded for comparison)–minified before compression
kbd.css–1259 B373 B297 Bnone (recorded for comparison)–minified before compression
listbox.css–5165 B917 B752 Bnone (recorded for comparison)–minified before compression
menu.css–5619 B993 B834 Bnone (recorded for comparison)–minified before compression
menubar.css–2485 B599 B498 Bnone (recorded for comparison)–minified before compression
meter.css–3572 B486 B389 Bnone (recorded for comparison)–minified before compression
number-field.css–6000 B987 B837 Bnone (recorded for comparison)–minified before compression
otp-field.css–4663 B818 B696 Bnone (recorded for comparison)–minified before compression
pagination.css–4981 B891 B755 Bnone (recorded for comparison)–minified before compression
popover.css–4215 B559 B465 Bnone (recorded for comparison)–minified before compression
progress.css–2964 B601 B514 Bnone (recorded for comparison)–minified before compression
radio-group.css–4505 B834 B688 Bnone (recorded for comparison)–minified before compression
range-slider.css–5305 B801 B689 Bnone (recorded for comparison)–minified before compression
rating.css–4803 B926 B746 Bnone (recorded for comparison)–minified before compression
scroll-area.css–4006 B561 B457 Bnone (recorded for comparison)–minified before compression
segmented.css–4292 B848 B691 Bnone (recorded for comparison)–minified before compression
select.css–13417 B1685 B1456 Bnone (recorded for comparison)–minified before compression
separator.css–1135 B285 B219 Bnone (recorded for comparison)–minified before compression
skeleton.css–2687 B546 B459 Bnone (recorded for comparison)–minified before compression
slider.css–5197 B777 B624 Bnone (recorded for comparison)–minified before compression
spinner.css–2392 B510 B419 Bnone (recorded for comparison)–minified before compression
splitter.css–4454 B779 B657 Bnone (recorded for comparison)–minified before compression
steps.css–5826 B1047 B882 Bnone (recorded for comparison)–minified before compression
switch.css–3708 B771 B626 Bnone (recorded for comparison)–minified before compression
tabs.css–4147 B835 B678 Bnone (recorded for comparison)–minified before compression
tag.css–3062 B731 B592 Bnone (recorded for comparison)–minified before compression
timeline.css–4296 B750 B622 Bnone (recorded for comparison)–minified before compression
toast.css–4737 B952 B794 Bnone (recorded for comparison)–minified before compression
toggle-group.css–4030 B787 B646 Bnone (recorded for comparison)–minified before compression
toggle.css–4072 B731 B603 Bnone (recorded for comparison)–minified before compression
tokens.css–18068 B2204 B1895 Bnone (recorded for comparison)–minified before compression
toolbar.css–2370 B481 B399 Bnone (recorded for comparison)–minified before compression
tooltip.css–2604 B543 B443 Bnone (recorded for comparison)–minified before compression
tree.css–4363 B856 B719 Bnone (recorded for comparison)–minified before compression
index.css (resolved)–280760 B20765 B16880 B21600 B gzipwithin budgetminified before compression

Method: bytes = shipped source; minified = comments and redundant whitespace removed by scripts/check.mjs minify (a bundler minifier may be slightly smaller); gzip level 9 and brotli quality 11 of the minified text. standalone = the file with the files it @imports inlined. "index.css (resolved)" inlines every @import once, depth first, and carries the budget. Command: yarn workspace @svelte-lean/styles size. Date: not recorded in the file; sizes are deterministic for the toolchain versions listed in docs/compatibility.md.

What a consumer ships

Five small applications consume the packages the way an installed consumer would and are built in production mode. A plugin isolates every @svelte-lean/* module into one chunk and Svelte's runtime into another, so the Svelte Lean bytes of a page can be read without Svelte's in them. Assertions are the expect.json checks of each fixture (module graph, forbidden strings, manifest).

FixtureUsesSvelte Lean chunk, brotligziprawSvelte runtime, brotliCSS, brotliSvelte Lean modulesAssertions
combobox-only–2564 B2849 B6855 B8710 B3695 B1448 passed
date-picker-only–5446 B5935 B15676 B12207 B4777 B2153 passed
listbox-only–1838 B2030 B4268 B8710 B2704 B1446 passed
manual-registrationtabs markup, register module imported by hand1521 B1693 B3639 B8710 B2652 B1342 passed
native-onlybutton, dialog, popover markup and styles0 B0 B0 B8710 B3565 B031 passed
select-only–3362 B3731 B9526 B10119 B4546 B1448 passed
table-basic<DataTable /> and its stylesheet16829 B18900 B52658 B15916 B5402 B846 passed
tabs-and-menutabs and menu markup2351 B2611 B6209 B8710 B3944 B1743 passed
tabs-onlytabs markup, discovered by the plugin1521 B1693 B3639 B8710 B2652 B1343 passed

Method: Production `vite build` per fixture with Vite defaults (esbuild minify, modulepreload polyfill). Sizes are the emitted JavaScript and CSS files: raw bytes, gzip level 9, brotli quality 11 (node:zlib). "svelteLean" is the svelte-lean chunk that the fixture plugin isolates for every @svelte-lean/* module and "svelteRuntime" the svelte-runtime chunk for Svelte's runtime (both include chunk import/export bindings); 0 B means no such chunk was emitted. "modules.svelteLean" lists rendered @svelte-lean/* modules with their pre-minification rendered length. Environment: node v22.23.3,vite 7.3.6,svelte 5.57.1,@sveltejs/vite-plugin-svelte 6.2.4. Date: not recorded in the file. Command: yarn test:fixtures.

Listeners

The listener claim has no number in a file: it is an exact assertion in the browser suite, that the per-type listener counts for a thousand tabs roots equal those for one root, on two independent accounts. What this page can show is the same two accounts taken live. The specimen below is a real tabs root driven by the shared behavior; the panel under it reads the runtime's own account and the independent count of every addEventListener call on this page. Mount a hundred roots, then compare the readings.

Tabs roots on this page: 1
Root 1, overview panel.
Every root answers to the arrow keys, Home and End; the behavior writes aria-selected, tabindex and hidden to the markup and holds no state of its own.
Runtime
Read from the page after it loads; requires JavaScript.

The proof page mounts one, a hundred or a thousand roots on demand and records a reading for each count. The gate itself is listeners.spec.ts; command: yarn workspace @svelte-lean/playground test:e2e --project=chromium tests/primitives/listeners.spec.ts. Two related counts are recorded in files:

SubjectValueMethodFile
DOM nodes with a thousand tabs roots8045document.getElementsByTagName("*").length after loadapps/playground/results/bench.json
JavaScript event listeners on the page after five mount and unmount cycles28Performance.getMetrics JSEventListeners at baseline and after the last cycleapps/playground/results/stress.json

Timings

Startup and interaction entries, each the median of five fresh page loads with every sample recorded. Minimum and maximum are shown so the spread is visible: five samples on one machine give a median, not a distribution, and a run on another machine gives another number. Startup and interaction are never combined into one score.

SubjectMedianMinMaxp95SamplesMethod
table.1000-rows.mount283.8 ms267.3 ms342.8 ms342.8 ms5component init to the second animation frame after onMount, client render of the whole table (ssr=false); median of 5 loads
table.1000-rows.render231.4 ms205.8 ms378.1 ms378.1 ms5unmount + mount of the DataTable via {#key}, tick() then two animation frames; median of 5
table.1000-rows.sort114.1 ms110.7 ms117.7 ms117.7 ms5header button click (ascending by revenue) to the second animation frame; median of 5
table.10000-rows.mount3907.1 ms3105.6 ms5984.2 ms5984.2 ms5component init to the second animation frame after onMount, client render of the whole table (ssr=false); median of 5 loads
table.10000-rows.render3806.9 ms2946.1 ms4554.2 ms4554.2 ms5unmount + mount of the DataTable via {#key}, tick() then two animation frames; median of 5
table.10000-rows.sort1928.4 ms1385.3 ms2142.4 ms2142.4 ms5header button click (ascending by revenue) to the second animation frame; median of 5
tabs.1000-roots.dom-content-loaded73.8 ms23 ms84.4 ms84.4 ms5PerformanceNavigationTiming.domContentLoadedEventEnd of a fresh page.goto; median of 5 loads
tabs.1000-roots.load74.2 ms23 ms84.9 ms84.9 ms5PerformanceNavigationTiming.loadEventEnd of a fresh page.goto; median of 5 loads
tabs.1000-roots.time-to-interactive98.8 ms66.1 ms119.3 ms119.3 ms5performance.now() in onMount of the client-rendered page: 1000 roots in the document and the behavior registered; median of 5 loads
tabs.1000-roots.first-interaction1.6 ms1.3 ms2.5 ms2.5 ms5performance.now() around trigger.click() on root 500 right after load (synchronous routing + DOM writes); median of 5 loads
tabs.1000-roots.repeat-interaction0.7 ms0.7 ms1.1 ms1.1 ms5performance.now() around a second trigger.click() on root 900; median of 5 loads
tabs.1000-roots.dom-nodes80458045804580455document.getElementsByTagName("*").length after load

Environment: chromium 153.0.8010.53, darwin 27.0.0 arm64, Apple M2, 8 cores, 16 GiB, Node v22.23.3, production build, headless. Date: 2026-09-27T20:46:17.407Z. Command: yarn bench:publish.

Memory after stress cycles

A thousand tabs roots mounted and unmounted five times, a menu opened and closed two hundred times, and one combobox root opened with ArrowDown and closed with Escape two hundred times (a Tier 2 session per cycle; the number of scoped document pointerdown listeners installed is recorded next to the JSEventListeners count): heap after forced garbage collection, DOM nodes and listeners, against a baseline taken after a warm-up cycle. The tolerance for growth is stated in each entry's method. This project runs in the nightly workflow, not on every pull request.

SubjectMedianMinMaxp95SamplesMethod
stress.tabs.mount-unmount.heap-fresh2136940 bytes2136940 bytes2136940 bytesnot measured1JSHeapUsedSize after two forced GCs on /primitives/tabs/many?n=1 right after load
stress.tabs.mount-unmount.heap-baseline2665560 bytes2665560 bytes2665560 bytesnot measured1JSHeapUsedSize after two forced GCs, n=1, after one warm-up cycle 1 → 1000 → 1
stress.tabs.mount-unmount.heap-peak11787304 bytes11650340 bytes11864060 bytes11864060 bytes5JSHeapUsedSize after two forced GCs with 1000 roots mounted and interacted with, one sample per cycle
stress.tabs.mount-unmount.heap-per-cycle8889316 bytes8828724 bytes8984780 bytes8984780 bytes5heap-peak minus the heap after the previous cleanup: what 1000 roots cost, one sample per cycle
stress.tabs.mount-unmount.heap-after-cleanup2974744 bytes2846752 bytes3035596 bytes3035596 bytes5JSHeapUsedSize after two forced GCs back at n=1, one sample per cycle
stress.tabs.mount-unmount.heap-growth380208 bytes380208 bytes380208 bytesnot measured1heap after 5 cycles minus baseline; tolerance max(2 MiB, 15 % of baseline) = 2097152 B
stress.tabs.mount-unmount.nodes-baseline310310310not measured1Performance.getMetrics Nodes at n=1 after the warm-up cycle
stress.tabs.mount-unmount.nodes-after-cleanup3103103103105Performance.getMetrics Nodes back at n=1, one sample per cycle; allowance 100 above baseline
stress.tabs.mount-unmount.js-event-listeners282828282Performance.getMetrics JSEventListeners at baseline and after the last cycle
stress.menu.open-close.heap-baseline2084788 bytes2084788 bytes2084788 bytesnot measured1JSHeapUsedSize after two forced GCs on /primitives/menu after one open/close cycle
stress.menu.open-close.heap-after2138688 bytes2138688 bytes2138688 bytesnot measured1JSHeapUsedSize after two forced GCs after 200 open/close cycles
stress.menu.open-close.heap-growth53900 bytes53900 bytes53900 bytesnot measured1heap after minus baseline; tolerance max(2 MiB, 15 % of baseline) = 2097152 B
stress.menu.open-close.nodes2672672672672Performance.getMetrics Nodes at baseline and after the cycles
stress.menu.open-close.cycles-focused-inside200200200not measured1cycles (of 200) in which the first item had focus when Escape was sent
stress.combobox.open-close.heap-baseline2379528 bytes2379528 bytes2379528 bytesnot measured1JSHeapUsedSize after two forced GCs on /primitives/combobox after one open/close cycle
stress.combobox.open-close.heap-after2542684 bytes2542684 bytes2542684 bytesnot measured1JSHeapUsedSize after two forced GCs after 200 open/close cycles (ArrowDown, Escape) of one Tier 2 root
stress.combobox.open-close.heap-growth163156 bytes163156 bytes163156 bytesnot measured1heap after minus baseline; tolerance max(2 MiB, 15 % of baseline) = 2097152 B
stress.combobox.open-close.nodes10061006100610062Performance.getMetrics Nodes at baseline and after the cycles
stress.combobox.open-close.js-event-listeners373737372Performance.getMetrics JSEventListeners at baseline and after the cycles; a retained session listener would raise it
stress.combobox.open-close.session-listeners-installed200200200not measured1document pointerdown installations recorded by the wrapped addEventListener during 200 cycles: one scoped listener per open session
stress.combobox.open-close.cycles-active-while-open200200200not measured1cycles (of 200) in which an option was active while the popup was open

Environment: chromium 153.0.8010.53, darwin 27.0.0 arm64, Apple M2, 8 cores, 16 GiB, Node v22.23.3, production build, headless. Date: 2026-09-27T20:46:35.800Z. Command: yarn bench:publish.

Table with 1000 rows

The specimen the timings above are taken from, rendered on demand with every row in the document: no pagination and no virtualization, deliberately. The two readings under the table are taken in your browser when the specimen mounts and when you re-render it; they are readings, not published numbers, and change with the machine.

The table mounts when you ask for it and requires JavaScript; the prerendered HTML does not carry its seven thousand cells.

Sort by clicking a header; the benchmark's sort entry times a header click to the second animation frame. The published values, taken on the playground's page, are in the timings table above.
Rows.svelte
<script lang="ts">
	import { DataTable, type Column } from '@svelte-lean/table';
	import '@svelte-lean/table/style.css';
	import { rows, type Row } from './rows'; // one thousand generated rows

	const columns: Column<Row>[] = [
		{ id: 'name', header: 'Name' },
		{ id: 'email', header: 'Email' },
		{ id: 'team', header: 'Team' },
		{ id: 'region', header: 'Region' },
		{ id: 'status', header: 'Status' },
		{ id: 'revenue', header: 'Revenue', align: 'end' },
		{ id: 'joined', header: 'Joined' }
	];
</script>

<!-- Every row is in the document: no pagination, no virtualization. -->
<DataTable
	data={rows}
	{columns}
	getRowId={(row) => row.id}
	label="One thousand rows"
	pagination={false}
	stickyHeader
	height="24rem"
/>

Source