@ariefsn/svelte-use
v1.0.0
Published
Collection of reactive utility composables for Svelte 5 inspired by VueUse.
Downloads
143
Maintainers
Readme
Features
| | |
| --------------------- | ----------------------------------------------------- |
| ⚡ Svelte 5 Runes | Built for $state, $derived, $effect — no stores |
| 🌲 Tree-shakable | Import only what you use |
| 🔒 Fully typed | First-class TypeScript, no any |
| 🌐 SSR safe | All browser APIs are guarded |
| 📦 Zero deps | No runtime dependencies |
| 🧹 Auto cleanup | Listeners removed on component destroy |
Installation
npm install @ariefsn/svelte-use
# or
pnpm add @ariefsn/svelte-use
# or
bun add @ariefsn/svelte-useRequires Svelte 5 as a peer dependency.
Utilities
Animation
| Composable | Description |
| --------------- | ----------------------------------------------- |
| useAnimate | Reactive Web Animations API wrapper |
| useParallax | Parallax effect based on pointer or device tilt |
| useTransition | Animated numeric transitions with easing |
Async
| Composable | Description |
| -------------- | --------------------------------------- |
| useFetch | Reactive fetch with loading/error state |
| useWebSocket | Reactive WebSocket with auto-reconnect |
Time
| Composable | Description |
| ---------------- | -------------------------------------------- |
| useInterval | Reactive interval counter |
| useIntervalFn | Run a callback on an interval |
| useNow | Reactive current Date |
| useTimeout | Reactive timeout flag |
| useTimeoutFn | Run a callback after a delay |
| useTimeoutPoll | Poll a callback with timeout-based intervals |
| useTimestamp | Reactive current timestamp (ms) |
State
| Composable | Description |
| -------------- | ------------------------------------------- |
| useToggle | Reactive boolean toggle |
| useCounter | Reactive counter with inc/dec/reset |
| usePrevious | Track previous value of any reactive getter |
| useSorted | Reactive sorted copy of an array |
| useCycleList | Cycle through a list reactively |
| useCountdown | Countdown timer with start/stop/reset |
| useTimeAgo | Human-readable relative time string |
Reactivity
| Composable | Description |
| ------------- | ---------------------------- |
| useDebounce | Debounce any reactive getter |
Browser – Keyboard & Scroll
| Composable | Description |
| ---------------- | --------------------------------------------------------- |
| useMagicKeys | Reactive keyboard state via Proxy — single keys or combos |
| useKeyModifier | Track Ctrl/Shift/Alt/Meta state |
| useScroll | Scroll position, direction, edge arrival |
| useScrollLock | Lock/unlock body scroll |
Browser – Pointer & Drag
| Composable | Description |
| ----------------- | -------------------------------------------------- |
| useMouse | Viewport-relative pointer position |
| useMousePressed | Detect mouse button press state |
| useDraggable | Full-featured draggable with axis, bounds, handles |
Browser – Observers
| Composable | Description |
| ------------------------- | ---------------------------------------------- |
| useElementSize | Reactive element dimensions via ResizeObserver |
| useIntersectionObserver | Visibility detection via IntersectionObserver |
| useResizeObserver | Raw ResizeObserver with callback |
| useMutationObserver | DOM mutation observation |
Browser – Sensors
| Composable | Description |
| ---------------------- | ------------------------------------------------------ |
| useIdle | Detect user idle state |
| useNetwork | Network Information API (downlink, RTT, effectiveType) |
| useGeolocation | Reactive geolocation via watchPosition |
| useBreakpoints | Reactive responsive breakpoints |
| useBrowserLocation | Reactive browser location (URL, hash, search) |
| useNavigatorLanguage | Reactive navigator language |
| useOnline | Reactive online/offline status |
| usePageLeave | Detect when user leaves the page |
Browser – Storage
| Composable | Description |
| ------------------- | --------------------------------------- |
| useLocalStorage | Reactive localStorage with SSR safety |
| useIndexedDB | Reactive IndexedDB with CRUD & querying |
| useBase64 | Reactive Base64 encode/decode |
| useObjectUrl | Reactive object URL from Blob/File |
| useSessionStorage | Reactive sessionStorage with SSR safety |
Browser – Interaction
| Composable | Description |
| ----------------- | ----------------------------------------- |
| useClickOutside | Detect clicks outside an element |
| useDropZone | Drag-and-drop zone with file/data support |
| useElementHover | Detect hover state of an element |
| useFocus | Reactive focus state of an element |
Performance
| Composable | Description |
| --------------- | ---------------------------------- |
| useFps | Reactive frames-per-second counter |
| useThrottleFn | Throttle any function |
| useDebounceFn | Debounce any function |
Virtualization
| Composable | Description |
| ---------------- | -------------------------------- |
| useVirtualList | Efficient virtual list rendering |
Web APIs
| Composable | Description |
| ---------------------- | ----------------------------------- |
| useClipboard | Reactive clipboard read/write |
| useBattery | Reactive Battery Status API |
| useSpeechRecognition | Reactive Web Speech Recognition API |
Composable Reference
useToggle
Reactive boolean toggle.
import { useToggle } from '@ariefsn/svelte-use';
const { value, toggle, set } = useToggle();
// value → false
toggle(); // value → true
set(false); // value → false| Return | Type | Description |
| -------- | ---------------------- | ------------------------- |
| value | boolean (reactive) | Current boolean state |
| toggle | () => void | Flips the value |
| set | (v: boolean) => void | Sets the value explicitly |
useCounter
Reactive counter with increment, decrement, and reset.
import { useCounter } from '@ariefsn/svelte-use';
const { value, inc, dec, reset } = useCounter(0);
inc(); // value → 1
inc(5); // value → 6
dec(3); // value → 3
reset(); // value → 0| Return | Type | Description |
| ------- | -------------------------- | -------------------------------- |
| value | number (reactive) | Current counter value |
| inc | (delta?: number) => void | Increment by delta (default 1) |
| dec | (delta?: number) => void | Decrement by delta (default 1) |
| reset | () => void | Reset to initial value |
usePrevious
Tracks the previous value of any reactive getter. Returns undefined until the tracked value changes for the first time.
import { usePrevious } from '@ariefsn/svelte-use';
let count = $state(0);
const prev = usePrevious(() => count);
// prev() → undefined
count = 1;
// prev() → 0
count = 2;
// prev() → 1| Parameter | Type | Description |
| --------- | --------- | ----------------------------------- |
| getter | () => T | Reactive getter function to observe |
Returns a () => T | undefined getter.
useDebounce
Delays a reactive value until the source stops changing for the specified duration.
import { useDebounce } from '@ariefsn/svelte-use';
let query = $state('');
const debounced = useDebounce(() => query, 500);
// debounced() updates only after 500 ms of inactivity| Parameter | Type | Default | Description |
| --------- | --------- | ------- | --------------------------- |
| getter | () => T | — | Reactive getter to debounce |
| delay | number | 300 | Delay in milliseconds |
Returns a () => T getter.
useLocalStorage
Reactive localStorage with SSR safety. Values are serialised with JSON.stringify / JSON.parse. Falls back to initial in non-browser environments or on parse errors.
import { useLocalStorage } from '@ariefsn/svelte-use';
const theme = useLocalStorage<'light' | 'dark'>('theme', 'light');
theme.set('dark'); // persists to localStorage
theme.value; // 'dark'| Parameter | Type | Description |
| --------- | -------- | ------------------------------------- |
| key | string | localStorage key |
| initial | T | Fallback when key is absent or in SSR |
| Return | Type | Description |
| ------- | ---------------- | ---------------------------- |
| value | T (reactive) | Current stored value |
| set | (v: T) => void | Update and persist the value |
useIndexedDB
Reactive IndexedDB utility with full CRUD, querying, and filtering. SSR-safe — all operations are no-ops on the server. Values survive page refreshes and browser restarts.
import { useIndexedDB } from '@ariefsn/svelte-use';
interface Note {
id?: number;
text: string;
done: boolean;
}
const db = useIndexedDB<Note>('my-app', 'notes');
// CRUD
await db.add({ text: 'Buy milk', done: false }); // returns generated key
await db.get(1); // Note | undefined
await db.getAll(); // Note[]
await db.update({ id: 1, text: 'Buy milk', done: true });
await db.remove(1);
await db.clear(); // delete all records
// Reactive state (updated automatically after every mutation)
db.items; // Note[] — all records
db.loading; // boolean
db.error; // Error | null
// Filtering
const pending = await db.query((n) => !n.done); // Note[]Options
| Parameter | Type | Default | Description |
| ----------------------- | --------- | ------- | ------------------------------------- |
| dbName | string | — | IndexedDB database name |
| storeName | string | — | Object store name |
| options.version | number | 1 | Schema version (increment to migrate) |
| options.keyPath | string | 'id' | Primary key field name |
| options.autoIncrement | boolean | true | Auto-generate numeric keys |
Returns
| Property / Method | Type | Description |
| ----------------- | ----------------------------------- | ------------------------------------------------------------ |
| items | T[] (reactive) | All stored records; refreshed after every mutation |
| loading | boolean (reactive) | true while an async operation is in flight |
| error | Error \| null (reactive) | Last error, or null |
| add(record) | Promise<IDBValidKey \| undefined> | Insert record; returns generated key |
| get(key) | Promise<T \| undefined> | Fetch single record by primary key |
| getAll() | Promise<T[]> | Fetch all records and sync items |
| update(record) | Promise<void> | Replace record (must include key field) |
| remove(key) | Promise<void> | Delete record by primary key |
| query(filter) | Promise<T[]> | Return records matching a predicate (doesn't modify items) |
| clear() | Promise<void> | Delete all records |
useSorted
Returns a reactive sorted copy of an array. Never mutates the source.
import { useSorted } from '@ariefsn/svelte-use';
let items = $state([3, 1, 4, 1, 5]);
const sorted = useSorted(() => items);
// sorted() → [1, 1, 3, 4, 5]
const desc = useSorted(
() => items,
(a, b) => b - a
);useCycleList
Cycle through a list reactively. Wraps around at both ends.
import { useCycleList } from '@ariefsn/svelte-use';
const cycle = useCycleList(['light', 'dark', 'system']);
cycle.state(); // → 'light'
cycle.next(); // → 'dark'
cycle.prev(); // → 'light'
cycle.setIndex(2);useCountdown
Countdown timer with start/stop/reset.
import { useCountdown } from '@ariefsn/svelte-use';
const timer = useCountdown(60); // 60s at 1s intervals
timer.start();
timer.count(); // → 60, 59, …, 0
timer.isActive(); // → true
timer.stop();
timer.reset();useTimeAgo
Reactive human-readable relative time string.
import { useTimeAgo } from '@ariefsn/svelte-use';
const ago = useTimeAgo(() => new Date('2024-01-01'));
ago(); // → "1 year ago"useMagicKeys
Track any key or combination via a Proxy.
import { useMagicKeys } from '@ariefsn/svelte-use';
const keys = useMagicKeys();
keys['ctrl+s'](); // → true while Ctrl+S is held
keys['shift'](); // → true while Shift is helduseKeyModifier
Track a specific modifier key state.
import { useKeyModifier } from '@ariefsn/svelte-use';
const ctrl = useKeyModifier('ctrl'); // 'ctrl' | 'shift' | 'alt' | 'meta'
ctrl(); // → true while Ctrl is helduseScroll
Scroll position, direction flags, edge detection for any scrollable element or window.
import { useScroll } from '@ariefsn/svelte-use';
const scroll = useScroll(); // window
const elScroll = useScroll(() => myEl, { offset: { bottom: 20 } });
scroll.y(); // → number
scroll.isScrolling(); // → boolean
scroll.arrivedState.bottom(); // → boolean
scroll.directions.down(); // → boolean
scroll.scrollTo({ top: 0 });useMouse
Tracks viewport-relative pointer position.
import { useMouse } from '@ariefsn/svelte-use';
const mouse = useMouse();
mouse.x(); // → number
mouse.y(); // → number
mouse.sourceType(); // → 'mouse' | 'touch' | nulluseMousePressed
Detects whether any mouse button is held.
import { useMousePressed } from '@ariefsn/svelte-use';
const pressed = useMousePressed();
pressed(); // → booleanuseDraggable
Full-featured draggable with axis constraints, bounds, handles, and callbacks.
import { useDraggable } from '@ariefsn/svelte-use';
const drag = useDraggable(() => el, {
axis: 'x',
initialValue: { x: 100, y: 100 },
onEnd: (pos) => console.log(pos)
});
drag.x(); // → number
drag.isDragging(); // → boolean
drag.style(); // → "transform: translate(100px, 0px);"useElementSize
Reactively tracks element dimensions via ResizeObserver.
import { useElementSize } from '@ariefsn/svelte-use';
const size = useElementSize(() => el);
size.width(); // → number
size.height(); // → numberuseIntersectionObserver
Viewport visibility detection.
import { useIntersectionObserver } from '@ariefsn/svelte-use';
const { isIntersecting, stop } = useIntersectionObserver(() => el, { threshold: 0.5 });
isIntersecting(); // → booleanuseResizeObserver
Raw ResizeObserver wrapper with automatic cleanup.
import { useResizeObserver } from '@ariefsn/svelte-use';
const { stop } = useResizeObserver(
() => el,
(entry) => {
console.log(entry.contentRect.width);
}
);useMutationObserver
Observes DOM mutations on any node.
import { useMutationObserver } from '@ariefsn/svelte-use';
const { stop } = useMutationObserver(
() => el,
(mutations) => {
for (const m of mutations) console.log(m.type);
},
{ childList: true, subtree: true }
);useIdle
Detect user inactivity.
import { useIdle } from '@ariefsn/svelte-use';
const { isIdle, reset } = useIdle(5000); // idle after 5s
isIdle(); // → booleanuseNetwork
Reactive Network Information API.
import { useNetwork } from '@ariefsn/svelte-use';
const net = useNetwork();
net.effectiveType(); // → '4g' | '3g' | undefined
net.downlink(); // → Mbps | undefined
net.saveData(); // → boolean | undefineduseGeolocation
Reactive geolocation via watchPosition.
import { useGeolocation } from '@ariefsn/svelte-use';
const geo = useGeolocation({ enableHighAccuracy: true });
geo.coords()?.latitude; // → number | null
geo.error(); // → GeolocationPositionError | null
geo.isSupported(); // → booleanuseFetch
Reactive fetch wrapper with loading/error state.
import { useFetch } from '@ariefsn/svelte-use';
const { data, loading, error, execute } = useFetch<User[]>('/api/users');
// data() → User[] | null
// loading() → boolean
// error() → Error | nulluseWebSocket
Reactive WebSocket with auto-reconnect.
import { useWebSocket } from '@ariefsn/svelte-use';
const ws = useWebSocket('wss://echo.example.com');
ws.send('hello');
ws.data(); // → last received message
ws.status(); // → 'OPEN' | 'CLOSED' | 'CONNECTING'useAnimate
Reactive Web Animations API wrapper.
import { useAnimate } from '@ariefsn/svelte-use';
const { animate, stop } = useAnimate(() => el);
animate([{ opacity: 0 }, { opacity: 1 }], { duration: 300 });useParallax
Parallax effect based on pointer position or device tilt.
import { useParallax } from '@ariefsn/svelte-use';
const { tilt, roll } = useParallax(() => containerEl);
// tilt() → number (−0.5 to 0.5)
// roll() → number (−0.5 to 0.5)useTransition
Animated numeric transitions with easing.
import { useTransition } from '@ariefsn/svelte-use';
let target = $state(0);
const animated = useTransition(() => target, { duration: 500 });
// animated() smoothly interpolates to targetuseInterval
Reactive interval counter.
import { useInterval } from '@ariefsn/svelte-use';
const { counter, pause, resume } = useInterval(1000);
counter(); // → increments every seconduseNow
Reactive current Date.
import { useNow } from '@ariefsn/svelte-use';
const now = useNow();
now(); // → Date (updated every second by default)useTimestamp
Reactive current timestamp in milliseconds.
import { useTimestamp } from '@ariefsn/svelte-use';
const ts = useTimestamp();
ts(); // → number (ms since epoch)useBreakpoints
Reactive responsive breakpoints.
import { useBreakpoints } from '@ariefsn/svelte-use';
const bp = useBreakpoints({ sm: 640, md: 768, lg: 1024 });
bp.lg(); // → true if viewport ≥ 1024px
bp.between('sm', 'lg')(); // → booleanuseClipboard
Reactive clipboard read/write.
import { useClipboard } from '@ariefsn/svelte-use';
const { text, copy, copied } = useClipboard();
copy('Hello!');
copied(); // → true for 1.5s after copyuseBattery
Reactive Battery Status API.
import { useBattery } from '@ariefsn/svelte-use';
const battery = useBattery();
battery.level(); // → 0–1
battery.charging(); // → booleanuseVirtualList
Efficient virtual list rendering for large datasets.
import { useVirtualList } from '@ariefsn/svelte-use';
const { list, containerProps, wrapperProps } = useVirtualList(items, { itemHeight: 40 });
// list() → only the visible slice of itemsDeveloping
bun install
bun run devTo run tests:
bun run testBuilding & Publishing
# build the library
bun run build
# publish to npm
npm publish --access publicLinks
- 📦 npm: https://www.npmjs.com/package/@ariefsn/svelte-use
- 🐙 GitHub: https://github.com/ariefsn/svelte-use
