
Svelte Components
- 307 installs
- 92 repo stars
- Updated April 29, 2026
- spences10/svelte-skills-kit
svelte-components is a Svelte agent skill that guides developers through reusable component patterns, headless libraries, forms, and custom elements when building Svelte or SvelteKit product UIs.
About
svelte-components is a Claude Code skill from spences10/svelte-skills-kit for authoring reusable Svelte UI with correct props, events, accessibility, and library integration. The skill documents Bits UI, Ark UI, and Melt UI setup, web component compilation via customElement, and advanced form patterns such as the HTML form attribute for inputs outside a form wrapper. Three reference guides cover component libraries, web components, and form handling, with guidance verified against Svelte 5 official docs as of 2026-05-14. Reach for svelte-components when implementing product UI slices, extension panels, or lightweight mobile shells where you need consistent Svelte component architecture instead of ad hoc markup.
- Props slots and events
- Accessible markup defaults
- Composable UI structure
- Style scoping patterns
- Test-friendly component APIs
Svelte Components by the numbers
- 307 all-time installs (skills.sh)
- +9 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #750 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/spences10/svelte-skills-kit --skill svelte-componentsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 307 |
|---|---|
| repo stars | ★ 92 |
| Last updated | April 29, 2026 |
| Repository | spences10/svelte-skills-kit ↗ |
How do you build accessible Svelte component libraries?
Author reusable Svelte components with correct props, slots, events, and accessibility patterns while implementing product UI slices in apps, extensions, or lightweight mobile shells.
Who is it for?
Developers shipping Svelte 5 or SvelteKit interfaces who want opinionated patterns for component libraries, forms, and custom elements.
Skip if: Teams building React, Vue, or plain HTML apps without Svelte in the stack.
When should I use this skill?
The task involves Svelte component architecture, Bits UI or Ark UI integration, custom elements, or non-trivial form markup.
What you get
Svelte component files, headless library integrations, custom element configs, and form-pattern reference implementations.
- Svelte component files
- Library integration patterns
- Custom element configuration
By the numbers
- Covers 3 headless component libraries: Bits UI, Ark UI, and Melt UI
- Bundles 3 reference guides for libraries, web components, and forms
- Last verified against Svelte 5 official docs on 2026-05-14
Files
Svelte Components
Quick Start
Component libraries: Bits UI (headless) | Ark UI | Melt UI (primitives)
Form trick: Use form attribute when form can't wrap inputs:
<form id="my-form" action="/submit"><!-- outside table --></form>
<table>
<tr>
<td><input form="my-form" name="email" /></td>
<td><button form="my-form">Submit</button></td>
</tr>
</table>Web Components
// svelte.config.js
export default {
compilerOptions: {
customElement: true,
},
};<!-- MyButton.svelte -->
<svelte:options customElement="my-button" />
<button><slot /></button>Reference Files
- component-libraries.md - Bits
UI, Ark UI setup
- web-components.md - Building custom
elements
- form-patterns.md - Advanced form
handling
Notes
- Bits UI 1.0: flexible, unstyled, accessible components for Svelte
- Form
defaultValueattribute enables easy form resets - Use snippets to wrap rich HTML in custom select options
- Last verified: 2025-01-14
<!-- PROGRESSIVE DISCLOSURE GUIDELINES:
- Keep this file ~50 lines total (max ~150 lines)
- Use 1-2 code blocks only (recommend 1)
- Keep description <200 chars for Level 1 efficiency
- Move detailed docs to references/ for Level 3 loading
- This is Level 2 - quick reference ONLY, not a manual
-->
Component Libraries
Bits UI
Headless, unstyled, accessible components for Svelte.
pnpm add bits-ui<script>
import { Button } from 'bits-ui';
</script>
<Button.Root class="my-button">Click me</Button.Root>Key features:
- Fully unstyled - bring your own CSS
- Accessible by default (ARIA, keyboard nav)
- Composable compound components
Docs: bits-ui.com
---
Ark UI
Full-featured component library with Svelte support.
pnpm add @ark-ui/svelte<script>
import { Dialog } from '@ark-ui/svelte';
</script>
<Dialog.Root>
<Dialog.Trigger>Open</Dialog.Trigger>
<Dialog.Backdrop />
<Dialog.Positioner>
<Dialog.Content>
<Dialog.Title>Title</Dialog.Title>
<Dialog.Description>Description</Dialog.Description>
<Dialog.CloseTrigger>Close</Dialog.CloseTrigger>
</Dialog.Content>
</Dialog.Positioner>
</Dialog.Root>Docs: ark-ui.com
---
Melt UI
Low-level primitives (builders) for maximum flexibility.
pnpm add @melt-ui/svelte<script>
import { createDialog } from '@melt-ui/svelte';
const {
elements: { trigger, portalled, overlay, content, title, close },
states: { open },
} = createDialog();
</script>
<button use:melt={$trigger}>Open</button>
{#if $open}
<div use:melt={$portalled}>
<div use:melt={$overlay} />
<div use:melt={$content}>
<h2 use:melt={$title}>Title</h2>
<button use:melt={$close}>Close</button>
</div>
</div>
{/if}Key difference: Melt uses builders (functions) instead of components.
Docs: melt-ui.com
---
Which to Choose?
| Library | Style | Approach | Best For |
|---|---|---|---|
| Bits UI | Unstyled | Components | Quick accessible UI |
| Ark UI | Unstyled | Components | Feature-rich apps |
| Melt UI | Unstyled | Builders | Maximum control |
All three work with Svelte 5 runes.
Form Patterns
Form Attribute Trick
When you can't nest a form (e.g., inside tables), use the form attribute:
<form id="add-item" action="?/add" method="POST"></form>
<table>
<tbody>
{#each items as item}
<tr>
<td>{item.name}</td>
<td>{item.price}</td>
</tr>
{/each}
<tr>
<td><input form="add-item" name="name" required /></td>
<td
><input
form="add-item"
name="price"
type="number"
required
/></td
>
<td><button form="add-item">Add</button></td>
</tr>
</tbody>
</table>Benefits:
- Form can be anywhere in the document
- Submit with Enter works
- FormData collection works
- Accessible by default
Default Values and Reset
Forms support defaultValue for easy resets:
<script>
let name = $state('');
</script>
<form onreset={() => (name = '')}>
<input bind:value={name} defaultValue="" />
<button type="submit">Save</button>
<button type="reset">Reset</button>
</form>Progressive Enhancement
<script>
import { enhance } from '$app/forms';
let submitting = $state(false);
</script>
<form
method="POST"
use:enhance={() => {
submitting = true;
return async ({ update }) => {
await update();
submitting = false;
};
}}
>
<input name="email" type="email" required />
<button disabled={submitting}>
{submitting ? 'Saving...' : 'Save'}
</button>
</form>Form Validation with Valibot
// +page.server.ts
import * as v from 'valibot';
import { fail } from '@sveltejs/kit';
const ContactSchema = v.object({
email: v.pipe(v.string(), v.email()),
message: v.pipe(v.string(), v.minLength(10)),
});
export const actions = {
default: async ({ request }) => {
const formData = await request.formData();
const data = Object.fromEntries(formData);
const result = v.safeParse(ContactSchema, data);
if (!result.success) {
return fail(400, {
data,
errors: v.flatten(result.issues),
});
}
// Process valid data
await saveContact(result.output);
},
};<!-- +page.svelte -->
<script>
let { form } = $props();
</script>
<form method="POST">
<label>
Email
<input
name="email"
type="email"
value={form?.data?.email ?? ''}
/>
{#if form?.errors?.nested?.email}
<span class="error">{form.errors.nested.email[0]}</span>
{/if}
</label>
<label>
Message
<textarea name="message">{form?.data?.message ?? ''}</textarea>
{#if form?.errors?.nested?.message}
<span class="error">{form.errors.nested.message[0]}</span>
{/if}
</label>
<button>Send</button>
</form>Multiple Forms on One Page
<form action="?/subscribe" method="POST">
<input name="email" type="email" />
<button>Subscribe</button>
</form>
<form action="?/contact" method="POST">
<input name="message" />
<button>Send</button>
</form>// +page.server.ts
export const actions = {
subscribe: async ({ request }) => {
// Handle subscription
},
contact: async ({ request }) => {
// Handle contact
},
};Web Components with Svelte
Basic Setup
// svelte.config.js
export default {
compilerOptions: {
customElement: true, // Enable for entire project
},
};Or per-component:
<svelte:options customElement="my-element" />
<script>
let { name = 'World' } = $props();
</script>
<p>Hello {name}!</p>Gotchas
1. Self-Closing Tags
Svelte 5 requires closing tags. This affects custom elements:
<!-- WRONG -->
<my-element />
<!-- RIGHT -->
<my-element></my-element>2. Nested HTML in Options
<option> with nested HTML causes compiler errors:
<!-- WRONG - compiler error -->
<select>
<option><div>Rich content</div></option>
</select>
<!-- WORKAROUND - use snippets -->
{#snippet optionContent()}
<div>Rich content</div>
{/snippet}
<select>
<option>{@render optionContent()}</option>
</select>3. Shadow DOM Styling
Styles are scoped to shadow DOM by default:
<svelte:options customElement="styled-button" />
<button>
<slot />
</button>
<style>
/* Only affects this component's shadow DOM */
button {
background: blue;
}
</style>Exposing Props as Attributes
<svelte:options
customElement={{
tag: 'my-counter',
props: {
count: { reflect: true, type: 'Number' },
},
}}
/>
<script>
let { count = 0 } = $props();
</script>
<button onclick={() => count++}>{count}</button>Events
Dispatch custom events:
<svelte:options customElement="event-button" />
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
</script>
<button onclick={() => dispatch('clicked', { time: Date.now() })}>
Click me
</button><!-- Usage -->
<event-button></event-button>
<script>
document
.querySelector('event-button')
.addEventListener('clicked', (e) => console.log(e.detail));
</script>Library Distribution
For library authors:
// package.json
{
"svelte": "./dist/index.js",
"exports": {
".": {
"svelte": "./dist/index.js"
}
},
"keywords": ["svelte"],
"peerDependencies": {
"svelte": "^5.0.0"
}
}Important: Always include svelte in keywords and peerDependencies.
Related skills
How it compares
Pick svelte-components over generic frontend skills when the codebase is Svelte-specific and you need library integration patterns rather than framework-agnostic CSS advice.
FAQ
Which component libraries does svelte-components cover?
svelte-components documents three headless Svelte libraries: Bits UI, Ark UI, and Melt UI. The skill links to reference guides for setup, composition, and accessible defaults when wiring primitives into product UI.
Does svelte-components support Svelte custom elements?
svelte-components shows enabling customElement in svelte.config.js and defining elements with svelte:options customElement. Native HTML slot usage is documented for packaging components as reusable web components.