A powerful TypeScript-first derived store for Svelte 5 that enables deep object and array manipulation with full reactivity.
- ๐ฏ Type-Safe: Full TypeScript support with automatic type inference
- ๐ Reactive: Deep object and array manipulation with automatic updates
- ๐จ Svelte 5 Ready: Built for the latest Svelte features
- ๐ชถ Lightweight: Zero dependencies, tiny bundle size
- ๐ Null-Safe: Built-in handling for nullable types
- ๐ฎ Easy API: Simple dot notation for deep object access
const user = writable({ name: { first: 'Rich', last: 'Harris' } })
const firstName = keyed(user, 'name.first')
$firstName = 'Bryan'
console.log($user) // { name: { first: 'Bryan', last: 'Harris' } };
npm i -D svelte-keyed
Since Svelte automatically bundles all required dependencies, you only need to install this package as a dev dependency with the -D
flag.
While Svelte's built-in stores are powerful, they don't provide an elegant way to work with nested properties. svelte-keyed solves this by:
- Enabling direct manipulation of nested properties
- Maintaining full TypeScript support
- Providing a clean API for complex state management
- Supporting both object and array access patterns
keyed
takes a writable object store and a keypath, and returns a writable store whose changes are reflected on the original store.
Properties are accessed with dot notation, and arrays can be indexed with bracket notation.
const email = keyed(settings, 'profiles[0].email')
If the parent store is nullable, then the child store will also be nullable.
type User = {
name: {
first: string
last: string
}
relations: {
partner?: User
}
}
const maybeUser = writable<User | undefined>(undefined)
// Writable<string | undefined>
const firstName = keyed(maybeUser, 'name.first')
Nullable properties are accessed with optional chaining behaviour.
const user = writable(initUser)
// Writable<Name | undefined>
const partnerName = keyed(user, 'relations.partner.name')
keyed
infers the return type of the keyed store from the keypath.
const user = writable(initUser)
// Writable<string>
const firstName = keyed(user, 'name.first')
keyed
will also try to guess all possible keypaths up to a depth limit of 3.
keyed(user, '...');
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โข name โ
โ โข name.first โ
โ โข name.last โ
โ โข relations โ
โ โข relations.partner โ
โ โข relations.partner.name โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
This limit is due to a TypeScript limitation where structured types must be generated statically. Increasing the depth limit slows down type compilation.
Type hints will not be provided for keypaths with a depth greater than 3 but this does not affect the return type.
const user = writable(user)
// Writable<string | undefined>
const firstName = keyed(user, 'relations.partner.name.first')
We usually read and write properties of an object store with auto-subscriptions.
<input bind:value={$name.first} />
However, auto-subscriptions are isolated to a Svelte component. svelte-keyed
aims to solve several common limitations listed below.
Often, we want to set a property or element of a store into component context, then allow child components to read / write to the property.
<!-- Settings.svelte -->
<script>
setContext('profileSettings', keyed(settings, 'profile'))
</script>
<GeneralSettings />
<ProfileSettings />
<!-- ProfileSettings.svelte -->
<script>
const profileSettings = getContext('profileSettings')
</script>
<input type="text" bind:value={$profileSettings.username} />
One important method to reduce clutter on your component is to extract functionality into external helper functions. svelte-keyed
allows you to create derived Writable
stores that can be passed into or returned from helper functions.
<!-- Settings.svelte -->
<script>
const stats = writable({ userClicks: 0, userTaps: 0 })
const clicks = keyed(stats, 'userClicks')
</script>
<div use:trackClicks={clicks} />
<input use:trackClicks={clicks} />
export const trackClicks = (node, clicks) => {
const listen = () => {
clicks.update(($clicks) => $clicks + 1)
}
node.addEventListener('click', listen)
return {
destroy() {
node.removeEventListener('click', listen)
}
}
}
MIT ยฉ Humanspeak, Inc.
Made with โฅ by Humanspeak