Modern UI component library for Svelte 5
Tailwind CSS 4 · OKLCH Color Tokens · Fully Typed · 60+ Components · 16 Hooks
Live Demo · Getting Started · Changelog
bun add svelora mode-watcher
# npm install svelora mode-watcher
# pnpm add svelora mode-watcher
# yarn add svelora mode-watcher<script lang="ts">
import 'svelora/theme.css'
import { ModeWatcher } from 'mode-watcher'
</script>
<ModeWatcher />/* layout.css */
@import 'svelora/theme.css';<script lang="ts">
import { Avatar, Button, ThemeModeButton, toast } from 'svelora'
import { ModeWatcher } from 'mode-watcher'
</script>
<ModeWatcher />
<Button variant="soft" color="primary" leadingIcon="lucide:edit">Edit</Button>
<ThemeModeButton />
<Avatar src="/photo.jpg" alt="Jane" size="lg" />- Svelte 5 — Runes, snippets, latest reactivity
- Tailwind CSS 4 — Utility-first with Tailwind Variants
- Fully Typed — Strict TypeScript, exported prop types
- Accessible — Built on Bits UI & Vaul Svelte
- 200,000+ Icons — Iconify integration
- Themeable — OKLCH color tokens, light/dark mode, global config
- Hooks — 16 reactive hooks for common UI patterns
Reactive hooks built on Svelte 5 runes and actions.
<script>
import { useMediaQuery, useClipboard, useEventListener } from 'svelora'
const isMobile = useMediaQuery('(max-width: 640px)')
const clipboard = useClipboard()
let width = $state(0)
useEventListener(() => window, 'resize', () => {
width = window.innerWidth
})
</script>
{#if isMobile.matches}
<MobileLayout />
{/if}
<Button onclick={() => clipboard.copy('Hello!')}>
{clipboard.copied ? 'Copied!' : 'Copy'}
</Button>
<p>{width}px</p>| Hook | Type | Description |
|---|---|---|
useMediaQuery |
Runes | Reactive CSS media query tracking |
useClipboard |
Runes | Copy text with auto-reset state |
useFormField |
Context | Access FormField context from child components |
useClickOutside |
Action | Detect clicks outside an element |
useInfiniteScroll |
Runes + Action | Auto-load on scroll with loading state |
useEscapeKeydown |
Action | Listen for Escape key |
useDebounce |
Runes | Debounce with pending, cancel, flush |
useDebouncedState |
Runes | Bindable debounced state helper |
useEventListener |
Runes | Reactive event listeners with cleanup |
useResizeObserver |
Runes | Observe element size changes |
useElementSize |
Runes | Read reactive element width and height |
useIntersectionObserver |
Runes | Track viewport intersection state |
useScrollLock |
Runes | Lock and restore document scrolling |
useFocusTrap |
Runes | Keep focus inside a container |
useLocalStorage |
Runes | SSR-safe localStorage binding |
useThrottle |
Runes | Throttle reactive updates |
useTimeout |
Runes | Schedule cancellable one-shot timers |
useInterval |
Runes | Schedule cancellable repeating timers |
<!-- Per-instance -->
<Button ui={{ base: 'rounded-full shadow-lg' }}>Custom</Button>// Global defaults
import { defineConfig } from 'svelora'
defineConfig({
button: { defaultVariants: { variant: 'outline' } },
icons: { loading: 'lucide:loader' }
})/* Custom colors */
:root {
--color-primary: oklch(0.55 0.25 270);
}Release checklist: RELEASE_CHECKLIST.md
bun run release:verifyOne command publish:
bun run publicThe publish command generates CHANGELOG.md automatically from unreleased git commits.
It groups commit messages into Added, Changed, and Fixed, writes the next version section, resets ## [Unreleased] back to the template automatically, commits local changes if needed, publishes to npm, tags the release, and pushes everything.
Refresh the template manually if needed:
bun run changelog:templateOther version bumps:
bun run public:minor
# bun run public:majorFull verification before publish:
bun run public:fullDry run:
bun run public:drybun run public:minor:dry
# bun run public:major:drynpm login
npm publish --access publicIf you want to include the full test suite before publishing:
bun run release:verify:full