Melt UI
Melt UI: headless, accessible UI component builders for Svelte using the builder pattern with full styling freedom
You are an expert in building interfaces with Melt UI.
## Key Points
- Use the `melt` action (`use:melt={$builder}`) rather than manually spreading attributes; it handles both attribute spreading and event listeners in one step.
- Leverage the preprocessor (`@melt-ui/pp`) for a cleaner template syntax that avoids excessive `use:melt` directives in complex component trees.
- Keep builder creation in `<script>` and destructure `elements` and `states` separately for clarity and IDE autocompletion.
- Forgetting to subscribe to builder stores with `$` in templates: `use:melt={$trigger}` not `use:melt={trigger}`. Without the `$`, Svelte won't reactively update attributes.
## Quick Example
```bash
npm install @melt-ui/svelte
```
```bash
npm install -D @melt-ui/pp
```skilldb get ui-components-services-skills/Melt UIFull skill: 185 linesMelt UI — UI Components
You are an expert in building interfaces with Melt UI.
Core Philosophy
Overview
Melt UI is a headless UI component library designed specifically for Svelte. It provides accessible, unstyled component "builders" that return reactive attributes and event handlers you spread onto your own HTML elements. This builder pattern gives you complete control over markup and styling while Melt UI handles keyboard navigation, ARIA attributes, focus management, and state logic. It supports Svelte 4 and integrates naturally with SvelteKit.
Setup & Configuration
Installation
npm install @melt-ui/svelte
Optionally install the preprocessor for a cleaner API:
npm install -D @melt-ui/pp
Add the preprocessor to svelte.config.js:
import { preprocessMeltUI, sequence } from '@melt-ui/pp'
const config = {
preprocess: sequence([
vitePreprocess(),
preprocessMeltUI(),
]),
}
export default config
Core Patterns
Builder pattern (standard API)
Every component in Melt UI starts with a create* function that returns builders. Each builder provides an action and reactive attributes to spread onto elements:
<script>
import { createDialog, melt } from '@melt-ui/svelte'
const {
elements: { trigger, overlay, content, title, description, close },
states: { open },
} = createDialog()
</script>
<button use:melt={$trigger}>Open Dialog</button>
{#if $open}
<div use:melt={$overlay} class="fixed inset-0 bg-black/50" />
<div use:melt={$content} class="fixed inset-1/2 -translate-x-1/2 -translate-y-1/2 bg-white p-6 rounded-lg">
<h2 use:melt={$title}>Dialog Title</h2>
<p use:melt={$description}>Dialog description text.</p>
<button use:melt={$close}>Close</button>
</div>
{/if}
Select component
<script>
import { createSelect, melt } from '@melt-ui/svelte'
const {
elements: { trigger, menu, option, label },
states: { selectedLabel, open },
} = createSelect({
forceVisible: true,
positioning: { placement: 'bottom', fitViewport: true },
})
const options = [
{ value: 'react', label: 'React' },
{ value: 'svelte', label: 'Svelte' },
{ value: 'vue', label: 'Vue' },
]
</script>
<div>
<label use:melt={$label}>Framework</label>
<button use:melt={$trigger}>
{$selectedLabel || 'Select a framework'}
</button>
{#if $open}
<ul use:melt={$menu}>
{#each options as item}
<li use:melt={$option({ value: item.value, label: item.label })}>
{item.label}
</li>
{/each}
</ul>
{/if}
</div>
Tooltip
<script>
import { createTooltip, melt } from '@melt-ui/svelte'
const {
elements: { trigger, content, arrow },
states: { open },
} = createTooltip({
positioning: { placement: 'top' },
openDelay: 200,
closeDelay: 0,
})
</script>
<button use:melt={$trigger}>Hover me</button>
{#if $open}
<div use:melt={$content} class="bg-gray-900 text-white px-3 py-1.5 rounded text-sm">
<div use:melt={$arrow} />
Tooltip content
</div>
{/if}
Controlled state
Builders accept an optional initial value and return writable stores so you can control state externally:
<script>
import { createSwitch, melt } from '@melt-ui/svelte'
const {
elements: { root, input },
states: { checked },
} = createSwitch({ defaultChecked: true })
// Programmatic control
function reset() {
checked.set(false)
}
</script>
<button use:melt={$root} class="w-11 h-6 rounded-full" class:bg-blue-600={$checked} class:bg-gray-300={!$checked}>
<span class="block w-5 h-5 rounded-full bg-white transition-transform" class:translate-x-5={$checked} />
</button>
<input use:melt={$input} />
Best Practices
- Use the
meltaction (use:melt={$builder}) rather than manually spreading attributes; it handles both attribute spreading and event listeners in one step. - Leverage the preprocessor (
@melt-ui/pp) for a cleaner template syntax that avoids excessiveuse:meltdirectives in complex component trees. - Keep builder creation in
<script>and destructureelementsandstatesseparately for clarity and IDE autocompletion.
Common Pitfalls
- Forgetting to subscribe to builder stores with
$in templates:use:melt={$trigger}notuse:melt={trigger}. Without the$, Svelte won't reactively update attributes. - Using
{#if $open}for content that needs exit transitions: Svelte removes the element immediately. Use theforceVisible: trueoption and handle visibility with CSS or Svelte transitions instead.
Anti-Patterns
Using the service without understanding its pricing model. Cloud services bill differently — per request, per GB, per seat. Deploying without modeling expected costs leads to surprise invoices.
Hardcoding configuration instead of using environment variables. API keys, endpoints, and feature flags change between environments. Hardcoded values break deployments and leak secrets.
Ignoring the service's rate limits and quotas. Every external API has throughput limits. Failing to implement backoff, queuing, or caching results in dropped requests under load.
Treating the service as always available. External services go down. Without circuit breakers, fallbacks, or graceful degradation, a third-party outage becomes your outage.
Coupling your architecture to a single provider's API. Building directly against provider-specific interfaces makes migration painful. Wrap external services in thin adapter layers.
Install this skill directly: skilldb add ui-components-services-skills
Related Skills
Ark UI
"Ark UI: state machine-driven components, headless primitives, Select, Combobox, DatePicker, Toast, styling with any CSS framework"
Daisyui
"daisyUI: Tailwind CSS component library, themes, responsive utilities, component classes, customization, semantic color names"
Headless UI
"Headless UI: unstyled accessible components for Tailwind, Dialog, Combobox, Listbox, Menu, Transition, render props, React/Vue"
Kobalte
Kobalte: accessible, unstyled UI component library for SolidJS with WAI-ARIA compliance and composable compound component APIs
Park UI
Park UI: pre-designed styled components built on Ark UI primitives, available for React, Vue, and Solid with Panda CSS or Tailwind CSS styling
Radix UI
"Radix UI: accessible primitives, Dialog, Dropdown, Popover, Tabs, Toast, composition pattern, styling with className, controlled/uncontrolled"