Docs Philosophy
Compared with Bits UI
Different architecture, different trade-offs. Bits UI offers accessible primitives through a component model; Svelte Lean offers a platform-first DOM protocol with selective behavior. The right answer may be Bits UI.
On this page
Two models
In a component model, an interaction is a set of components the application imports and composes: a root, a trigger, content. The component owns its state, its handlers and its relationships, and exposes props and events. That model is expressive, familiar, and lets a library manage a widget entirely in JavaScript.
In the Svelte Lean model, an interaction is markup on a protocol. The browser owns what it can own (a dialog, a popover, a disclosure, the form controls); where it cannot, one behavior is registered per page and driven by attributes, with the state in the DOM and one shared listener per event type. Nothing is imported for the interaction, and the markup is the whole representation.
The two models answer different questions. A component API asks what the component should do; the protocol asks what the browser already does and adds only the remainder.
Measured
The same UI written with each library, built by Vite in production mode with the same Svelte version. The library column is every package module except Svelte's own runtime, which both sides ship. The case files and the script are in benchmarks/compare.
| UI | Svelte Lean JS (brotli) | Bits UI JS (brotli) | Svelte Lean characters | Bits UI characters | Svelte Lean lines | Bits UI lines |
|---|---|---|---|---|---|---|
| Dialog | 0 B | 12378 B | 330 | 340 | 18 | 16 |
| Menu | 1908 B | 27114 B | 414 | 376 | 22 | 13 |
| Tabs | 1488 B | 5437 B | 836 | 339 | 54 | 13 |
A thousand tabs mounted into an empty page in headless Chrome, from before mount() to the second animation frame, and the JavaScript heap after a forced garbage
collection:
| Library | Mount, median | Range | Heap, median |
|---|---|---|---|
| Svelte Lean | 35.1 ms | 33.6 ms to 36.2 ms | 2.3 MB |
| Bits UI | 220.8 ms | 209.9 ms to 233.6 ms | 48.9 MB |
Bits UI 2.19.3, Svelte 5.57.1, Vite 7.3.6, chrome 153.0.8010.53, Apple M2, 2026-09-27. Nine fresh pages per mount case.
What the numbers do not say
- The dialog costs Svelte Lean nothing because the browser ships the behavior. Bits UI also ships behavior the native element does not have, such as configurable focus management and scroll lock, and it works the same in browsers without invoker commands.
- The markup is not shorter. A dialog is about the same length; a menu is a little longer and tabs are much longer, because roles, ids and relationships are written in the HTML, where Bits UI generates them. Characters exclude whitespace; lines are counted after Prettier at 64 columns.
- One machine, one browser, client-side mounting. Server rendering and hydration are not part of the mount case. Nothing here measures accessibility; both libraries document their own.
Choosing
- Choose a component model when your team wants a typed component surface for every interaction, when the widgets you need go beyond what Svelte Lean ships today (its runtime side is four behaviors: tabs, menu, listbox and combobox), or when you want one library to own the whole widget in JavaScript regardless of browser support for native features.
- Choose Svelte Lean when you want native primitives to work before hydration and without client JavaScript, when you want the markup to be the single representation the server, CSS and assistive technology see, when the listener and bundle invariants matter to your pages, and when you are prepared to write the protocol attributes and author the ids yourself.
Use Bits UI if its component model fits your application better. Svelte Lean does not need to replace every library; it needs to be a sound architectural choice for the developers who value its model, and the trade-offs page lists what that choice costs.
Coexistence
The two can share an application. A native dialog or popover written on the protocol has no
runtime and no provider, so it does not conflict with a component-based widget on the same page;
the tabs and menu behaviors attach to the document and only respond to roots marked with data-slean. What cannot be shared is a single primitive's implementation: a widget
is either a component or protocol markup.