Keyboard-friendly, accessible and highly customizable Svelte components. View the docs
Every component is a named export from the package root, and every one also has a direct
subpath import (svelte-widgets/Toc.svelte) so bundlers can skip the rest.
| Component | What it does | Docs |
|---|---|---|
MultiSelect |
Keyboard-friendly multi/single select with grouping, async loading and deep style hooks | docs |
CommandMenu |
Command palette with fuzzy search, hotkeys, recents and async actions | docs |
PageSearch |
Pagefind-backed site search built on CommandMenu |
docs |
Popover |
Floating surface that positions, dismisses and traps focus for you | docs |
ActionMenu |
Action list opened from a trigger or right-click, with complete menu keyboard semantics | docs |
ConfirmDialog |
Promise-based dialog queue, so two racing prompts can't share one answer | docs |
Dialog |
Native modal with composable sections, close reasons and nested-dialog handling | docs |
DraggablePane |
Floating panel you can drag by its header, resize and reset to its anchor | docs |
NumberRangeInput |
Paired number and range inputs bound to one value, with optional schema defaults | docs |
SettingsGroup |
Collapsible group for organizing related settings sections | docs |
SettingsSearch |
Settings-row filter that expands matching groups and restores their prior state | docs |
SettingsSection |
Titled settings region with change tracking, resets, descriptions and shared-grid layout | docs |
Sheet |
Dialog-based modal edge panel with side placement and shared dismissal policies | docs |
Tabs |
Controlled ARIA tabs with automatic or manual keyboard activation | docs |
Accordion |
Single or multi-open disclosure group with snippet-rendered content | docs |
FindBar |
In-DOM find-in-page bar that highlights, counts and steps through matches | docs |
CodeEditor |
Virtualized editable code surface with injectable highlighting and persistence | docs |
DiffView |
Virtualized side-by-side and unified diffs with an injectable backend | docs |
Toast |
Notification queue with priorities, dedupe and pause-on-hover | docs |
Nav |
Navigation bar with dropdowns, pinning and active-route styling | docs |
Toc |
Sticky table of contents that finds and tracks its own headings | docs |
Masonry |
Column-balancing masonry grid with SSR support and virtualization | docs |
Footer |
Centered row of icon links, sized and themed with --footer-* |
docs |
ActionButton |
Async action button with pending, success and error feedback | docs |
CopyButton |
Copy-to-clipboard button with success and error feedback | docs |
ButtonGroup |
Segmented control over a set of options, single or multi select | docs |
FullscreenButton |
Fullscreen toggle scoped to one wrapper, so viewers don't fight over the flag | docs |
ThemeToggle |
Light/dark/system theme cycler with persistence and cross-tab synchronization | docs |
Toggle |
Accessible switch with a bindable checked |
docs |
CodeExample |
Collapsible source viewer used by the live examples | docs |
FileDetails |
Collapsible <details> viewer for a set of files |
docs |
PrevNext |
Previous/next links for sequential pages | docs |
SubpageGrid |
Card grid linking to child pages | docs |
Icon |
Inline SVG icon from the bundled set | docs |
GitHubCorner |
The classic corner ribbon link | docs |
CircleSpinner |
Minimal loading spinner | docs |
ContributorList |
Avatar row of GitHub contributors, grayscale until hover | docs |
LiteYouTubeEmbed |
YouTube poster that only loads the player iframe once clicked | docs |
Wiggle |
Spring-animated shake wrapper | docs |
Fifteen attachments work on any element: fourteen come from svelte-widgets/attachments, while heading_anchors has its own subpath. dismiss_on_outside_press is the lower-level multi-surface primitive behind click_outside.
<script>
import { CommandMenu, MultiSelect, Popover, Tabs, Toc } from 'svelte-widgets'
</script>- No run-time deps: every component needs only Svelte as a peer dependency
- Keyboard friendly: every interactive component is fully operable without a mouse
- Bindable: component state is exposed through
$bindableprops, so you can both read it and drive it from the outside - Themeable: CSS variables with sensible defaults on every element, plus prop bags to spread arbitrary attributes onto internals
- SSR-safe: nothing touches
windoworlocalStoragebefore mount - Typed: props, snippets and events are inferred from the data you pass
| Statements | Branches | Lines |
|---|---|---|
npm install --dev svelte-widgetsThis package was called svelte-multiselect up to v11 (#432). Swap it out:
npm uninstall svelte-multiselect && npm install -D svelte-widgetsThen rewrite the imports. Matching on the opening quote (all three kinds) keeps prose and GitHub URLs untouched, and covers every subpath along with the bare import. It skips .md deliberately: in markdown a backtick-quoted mention is usually prose, not an import.
find src -type f \( -name '*.svelte' -o -name '*.ts' -o -name '*.js' \) -exec perl -pi -e "s{(['\"\`])svelte-multiselect}{\$1svelte-widgets}g" {} +Three things the rewrite cannot do for you: CmdPalette is now CommandMenu and
PagefindPalette is now PageSearch (#428), and click_outside changed shape (it dismisses on
pointerdown, and exclude/include merged into one inside option) (#431). See the
changelog for the details.
Coming from svelte-toc or svelte-bricks instead? Those are now Toc and Masonry
here (#432), so the same swap applies with import { Toc } from 'svelte-widgets' and
import { Masonry } from 'svelte-widgets'.
Components have direct .svelte entry points, and headless/build-time APIs have focused subpaths:
import {
auto_update_position, // coalesce floating-position updates and clean up listeners
click_outside, // dismiss a surface when a press lands outside it
draggable,
float, // park an element next to an anchor and keep it there
focus_trap, // keep Tab inside a surface, hand focus back when it closes
highlight_matches,
hotkey, // declarative keybindings, `mod` maps to Cmd or Ctrl
register_escape_layer, // add a handler to the shared LIFO Escape stack
sortable,
tooltip,
} from 'svelte-widgets/attachments'
import { compute_position, fuzzy_match, get_label } from 'svelte-widgets/utils'
import { heading_anchors } from 'svelte-widgets/heading-anchors'| Subpath | API |
|---|---|
/attachments |
Element attachments and dismissal primitives |
/clipboard |
Clipboard feedback state |
/code-editor |
Backend-agnostic editing, diff rendering and primitives |
/code-editor/editor.css |
Shared syntax-token and diff-view styles |
/dialogs |
Queued choice, confirmation and prompt requests |
/file-drop |
Directory expansion and accept filtering |
/find-in-page |
Reactive find-in-page cursor behind FindBar |
/fullscreen |
Shared fullscreen state |
/heading-anchors |
Heading ID preprocessor, slugger and anchor attachment |
/icons |
Dynamic icon registry |
/katex |
KaTeX before/after preprocessor pair |
/live-examples |
mdsvex live-example transform, Vite plugin and highlighter |
/live-examples/create-highlighter |
Lightweight custom grammar highlighter factory |
/print |
Element printing |
/storage |
Non-throwing localStorage, persisted choices and MRU lists |
/text-search |
Text ranges, highlighting and search-jump helpers |
/theme |
Headless light/dark/system state |
/toast-queue |
Toast reducer and reactive store |
/utils |
Positioning, fuzzy matching, hotkeys and general helpers |
/vite-config |
This repository's Vite Plus configuration helper |
CodeEditor and DiffView take host-supplied EditorBackend and DiffBackend implementations, either through their backend props or once per app with set_editor_backend() and set_diff_backend(). Import svelte-widgets/code-editor/editor.css alongside them for the token palette and shared line metrics. The editor takes a host-owned model={create_editor_model({ uri, text })} whose rope, UTF-16 selection, transactions, dirty checkpoint, and bounded history remain usable at 100 MB / 1,000,000 lines. Saving is an optional callback, so file reads, persistence, conflicts and draft policy remain in the host. The editable DOM temporarily remains a full-document textarea and is therefore still subject to browser textarea and scroll-height limits. Both backend contracts are runtime-agnostic and can call a native process, worker, WASM module or server route.
Run the opt-in, hardware-sensitive editor stress target locally with RUN_LARGE_EDITOR_TESTS=1 npx vp test --run tests/vitest/code-editor-model.test.ts; normal CI deliberately skips it.
For $β¦$ and $$β¦$$ math in mdsvex, wrap mdsvex with katex_preprocess() and run heading_ids() last:
import { mdsvex } from 'mdsvex'
import { heading_ids } from 'svelte-widgets/heading-anchors'
import { katex_preprocess } from 'svelte-widgets/katex'
const katex = katex_preprocess()
export default {
preprocess: [katex.before, mdsvex({ extensions: [`.md`] }), katex.after, heading_ids()],
}Import katex/dist/katex.min.css once in the app so the generated markup is styled.
Popover and ActionMenu use the browser Popover API for top-layer rendering, light dismissal and Escape handling, while float supplies placement. Explicit custom dismissal policies still use click_outside. Dialog-like popovers can add focus_trap; action menus use Arrow/Home/End navigation and close on Tab so browser focus continues in page order.
<script lang="ts">
import { ActionMenu, Popover } from 'svelte-widgets'
const actions = [{ label: `Reload`, action: () => location.reload() }]
</script>
<Popover placement="bottom" align="start">
{#snippet trigger(props)}
<button {...props}>Options</button>
{/snippet}
<p>Anything you like in here.</p>
</Popover>
<ActionMenu {actions}>
{#snippet trigger(props)}
<button {...props}>Page actions</button>
{/snippet}
</ActionMenu>
<ActionMenu {actions}>
<div>Right-click anywhere in this region</div>
</ActionMenu>See src/lib/live-examples/readme.md for optional live-example helpers.
Here are some steps to get you started if you'd like to contribute to this project!