Bits UI vs Melt UI: Svelte Primitive Libraries Compared

Not every project needs a fully styled component library. Sometimes you want to control every pixel of your design while still getting accessible, well-engineered interactive behavior for free. That’s exactly what headless (or primitive) UI libraries deliver — and in the Svelte ecosystem, two stand out: Bits UI and Melt UI.

This post compares both libraries in depth so you can choose the right foundation for your next project.

What Are Primitive Libraries and Why Do They Matter?

A primitive library provides the behavior of UI components — focus management, keyboard navigation, ARIA attributes, open/close state — without any visual styling. You get a working accessible dialog, popover, or select menu, and you bring your own CSS.

This matters because:

  • Accessibility is hard. Getting keyboard navigation, screen reader support, and focus trapping right for a dialog takes hundreds of lines of careful code. Primitive libraries encode this expertise once so you don’t have to re-implement it for every project.
  • Design freedom is preserved. Unlike styled libraries, primitives don’t impose opinions on color, spacing, or layout. Your design system stays yours.
  • Maintenance burden drops. When WAI-ARIA specs evolve or browser behavior changes, the library handles the update. Your styling stays untouched.

Both Bits UI and Melt UI solve this problem — but with very different API philosophies.

Bits UI: The Component-Based Approach

Bits UI gives you Svelte components that you compose declaratively in your templates. A dialog looks something like this:

<Dialog.Root>
  <Dialog.Trigger>Open</Dialog.Trigger>
  <Dialog.Portal>
    <Dialog.Overlay class="fixed inset-0 bg-black/50" />
    <Dialog.Content class="fixed inset-1/2 ...">
      <Dialog.Title>Confirm action</Dialog.Title>
      <Dialog.Description>Are you sure?</Dialog.Description>
      <Dialog.Close>Cancel</Dialog.Close>
    </Dialog.Content>
  </Dialog.Portal>
</Dialog.Root>

Each sub-component (Root, Trigger, Content) handles its own ARIA role, keyboard interactions, and state. You style them with classes or inline styles — Bits UI doesn’t care which CSS approach you use.

Key strengths of Bits UI:

  • Familiar API. If you’ve used Radix UI in React, the component-based composition pattern will feel natural.
  • Svelte 5 native. Bits UI has been fully rewritten for Svelte 5 with runes, snippets, and the new event handling model.
  • Powers shadcn-svelte. Every shadcn-svelte component is built on top of Bits UI, which means it’s battle-tested at scale across thousands of projects.
  • Type safety. Extensive TypeScript definitions for every component and prop.

Melt UI: The Builder/Action-Based Approach

Melt UI takes a different path. Instead of giving you components, it gives you builders — functions that return props objects and Svelte actions you spread onto your own HTML elements.

<script>
  import { createDialog } from '@melt-ui/svelte';
  const { trigger, overlay, content, title, close } = createDialog();
</script>

<button {...$trigger} use:trigger.action>Open</button>
<div {...$overlay} use:overlay.action class="fixed inset-0 bg-black/50" />
<div {...$content} use:content.action class="fixed inset-1/2 ...">
  <h2 {...$title} use:title.action>Confirm action</h2>
  <button {...$close} use:close.action>Cancel</button>
</div>

You write the HTML. Melt UI decorates it with the right attributes and behavior via Svelte actions.

Key strengths of Melt UI:

  • Maximum HTML control. You choose every element — <div>, <section>, <button>, whatever your semantics require.
  • No component abstraction layer. There’s no wrapper component between you and the DOM, which can make debugging more transparent.
  • Granular reactivity. Builders expose stores for every piece of state, making it easy to react to open/close, selected values, and other internal state changes.
  • Strong accessibility. Like Bits UI, Melt UI implements WAI-ARIA patterns and handles keyboard interactions.

Accessibility Comparison

Both libraries take accessibility seriously, and both implement WAI-ARIA design patterns. Focus management, keyboard navigation, aria-expanded, aria-controls, role attributes — all handled automatically by both.

The difference is in how that accessibility is delivered:

  • Bits UI encapsulates ARIA attributes inside component implementations. You can’t accidentally forget to spread the right props because the component handles them internally.
  • Melt UI requires you to spread props and apply actions onto your elements. If you forget the use:trigger.action or {...$trigger} spread, you lose accessibility. The trade-off for more control is more responsibility.

The takeaway: Bits UI’s component model makes it slightly harder to introduce accessibility regressions through user error. Melt UI gives you more power but requires more discipline.

Svelte 5 Compatibility

This is an important consideration in 2026, as the ecosystem has largely moved to Svelte 5.

Bits UI was rewritten from the ground up for Svelte 5. It uses runes for reactivity, snippets for render delegation, and the new event handler syntax. It’s a first-class Svelte 5 citizen.

Melt UI was originally designed for Svelte 4’s store-based reactivity model. The builder pattern — returning stores and actions — maps naturally to Svelte 4’s $store syntax. While it works with Svelte 5 through compatibility mode, the API doesn’t feel native to the runes-first world.

The takeaway: If you’re starting a new Svelte 5 project, Bits UI’s API will feel more idiomatic. If you’re maintaining a Svelte 4 project, Melt UI remains an excellent choice.

When to Pick Bits UI

Choose Bits UI when you:

  • Are building a new Svelte 5 project and want a runes-native API
  • Prefer component-based composition and declarative templates
  • Plan to use shadcn-svelte (it’s built on Bits UI, so you’re already using it)
  • Want a slightly safer accessibility model that’s harder to misuse
  • Value the cross-pollination with the Radix UI design philosophy

When to Pick Melt UI

Choose Melt UI when you:

  • Want total control over your HTML elements and DOM structure
  • Prefer a functions-and-stores API over component wrappers
  • Are working in a Svelte 4 codebase where the builder pattern feels natural
  • Need fine-grained access to internal component state via stores
  • Like the idea of progressive enhancement — plain HTML first, behavior layered on

How They Pair with Styled Libraries

Here’s something worth noting: Bits UI and Melt UI aren’t just standalone tools — they serve as foundations for higher-level styled libraries.

shadcn-svelte is the most prominent example. Every component in shadcn-svelte is a styled wrapper around a Bits UI primitive. When you use shadcn-svelte’s Dialog component, you’re using Bits UI under the hood, with Tailwind CSS classes applied on top.

This means you can start with shadcn-svelte for rapid development and drop down to Bits UI when you need custom behavior — all without switching libraries.

Melt UI can similarly serve as a foundation for custom styled component systems. If your team is building a bespoke design system and prefers the builder pattern, Melt UI is an excellent base layer.

Verdict

Both Bits UI and Melt UI are excellent at what they do. The choice comes down to API preference and project context:

  • Bits UI is the better default for new Svelte 5 projects. Its component API is clean, its Svelte 5 support is native, and its role as the foundation of shadcn-svelte means it has enormous real-world usage.
  • Melt UI remains a compelling choice for developers who prefer builder patterns and want the most granular control over their markup.

Explore both in detail: Bits UI | Melt UI | Compare side by side.