{"id":22819530,"url":"https://github.com/humanspeak/svelte-keyed","last_synced_at":"2026-02-20T22:41:06.689Z","repository":{"id":266859722,"uuid":"886401254","full_name":"humanspeak/svelte-keyed","owner":"humanspeak","description":"A powerful writable derived store for Svelte that enables deep object and array manipulation with TypeScript support","archived":false,"fork":false,"pushed_at":"2025-04-21T04:17:56.000Z","size":1021,"stargazers_count":0,"open_issues_count":12,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-25T21:34:57.194Z","etag":null,"topics":["array-store","derived","nested-store","object-store","reactive","rxjs","state-management","store","svelte","svelte5","sveltekit","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/humanspeak.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":["humanspeak"]}},"created_at":"2024-11-10T22:25:01.000Z","updated_at":"2025-01-28T00:55:51.000Z","dependencies_parsed_at":null,"dependency_job_id":"27e963d6-8eed-4581-8ed7-2f3fc667bdf2","html_url":"https://github.com/humanspeak/svelte-keyed","commit_stats":null,"previous_names":["humanspeak/svelte-keyed"],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/humanspeak/svelte-keyed","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/humanspeak%2Fsvelte-keyed","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/humanspeak%2Fsvelte-keyed/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/humanspeak%2Fsvelte-keyed/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/humanspeak%2Fsvelte-keyed/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/humanspeak","download_url":"https://codeload.github.com/humanspeak/svelte-keyed/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/humanspeak%2Fsvelte-keyed/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29667095,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-20T19:49:36.704Z","status":"ssl_error","status_checked_at":"2026-02-20T19:44:05.372Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["array-store","derived","nested-store","object-store","reactive","rxjs","state-management","store","svelte","svelte5","sveltekit","typescript"],"created_at":"2024-12-12T15:12:46.508Z","updated_at":"2026-02-20T22:41:06.665Z","avatar_url":"https://github.com/humanspeak.png","language":"TypeScript","funding_links":["https://github.com/sponsors/humanspeak"],"categories":[],"sub_categories":[],"readme":"# @humanspeak/svelte-keyed\n\n![svelte-keyed-banner](https://user-images.githubusercontent.com/42545742/145455110-0d90603a-5fb3-453a-a9ea-7c4e3b443913.png)\n\n\u003e A powerful TypeScript-first derived store for Svelte 5 that enables deep object and array manipulation with full reactivity.\n\n[![npm version](http://img.shields.io/npm/v/@humanspeak/svelte-keyed.svg)](https://www.npmjs.com/package/humanspeak/svelte-keyed)\n![build](https://img.shields.io/github/actions/workflow/status/humanspeak/svelte-keyed/npm-publish.yml)\n[![coverage](https://coveralls.io/repos/github/humanspeak/svelte-keyed/badge.svg?branch=main)](https://coveralls.io/github/humanspeak/svelte-keyed?branch=main)\n[![Downloads](https://img.shields.io/npm/dm/@humanspeak/svelte-keyed.svg)](https://www.npmjs.com/package/@humanspeak/svelte-keyed)\n[![License](https://img.shields.io/npm/l/@humanspeak/svelte-keyed.svg)](https://github.com/humanspeak/svelte-keyed/blob/main/LICENSE)\n[![CodeQL](https://github.com/humanspeak/svelte-keyed/actions/workflows/codeql.yml/badge.svg)](https://github.com/humanspeak/svelte-keyed/actions/workflows/codeql.yml)\n[![Install size](https://packagephobia.com/badge?p=@humanspeak/svelte-keyed)](https://packagephobia.com/result?p=@humanspeak/svelte-keyed)\n[![Code Style: Trunk](https://img.shields.io/badge/code%20style-trunk-blue.svg)](https://trunk.io)\n\n## Features\n\n- 🎯 **Type-Safe**: Full TypeScript support with automatic type inference\n- 🔄 **Reactive**: Deep object and array manipulation with automatic updates\n- 🎨 **Svelte 5 Ready**: Built for the latest Svelte features\n- 🪶 **Lightweight**: Zero dependencies, tiny bundle size\n- 🔒 **Null-Safe**: Built-in handling for nullable types\n- 🎮 **Easy API**: Simple dot notation for deep object access\n\n## Quick Start\n\n```js\nconst user = writable({ name: { first: 'Rich', last: 'Harris' } })\nconst firstName = keyed(user, 'name.first')\n\n$firstName = 'Bryan'\n\nconsole.log($user) // { name: { first: 'Bryan', last: 'Harris' } };\n```\n\n## Installation\n\n```bash\n\nnpm i -D svelte-keyed\n\n```\n\nSince Svelte automatically bundles all required dependencies, you only need to install this package as a dev dependency with the `-D` flag.\n\n## Why svelte-keyed?\n\nWhile Svelte's built-in stores are powerful, they don't provide an elegant way to work with nested properties. svelte-keyed solves this by:\n\n- Enabling direct manipulation of nested properties\n- Maintaining full TypeScript support\n- Providing a clean API for complex state management\n- Supporting both object and array access patterns\n\n## API\n\n`keyed` takes a writable object store and a **keypath**, and returns a writable store whose _changes are reflected on the original store_.\n\nProperties are accessed with dot notation, and arrays can be indexed with bracket notation.\n\n```js\nconst email = keyed(settings, 'profiles[0].email')\n```\n\n### Nullable parents\n\nIf the parent store is nullable, then the child store will also be nullable.\n\n```ts\ntype User = {\n    name: {\n        first: string\n        last: string\n    }\n    relations: {\n        partner?: User\n    }\n}\n\nconst maybeUser = writable\u003cUser | undefined\u003e(undefined)\n// Writable\u003cstring | undefined\u003e\nconst firstName = keyed(maybeUser, 'name.first')\n```\n\n### Nullable properties\n\nNullable properties are accessed with [optional chaining](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining) behaviour.\n\n```ts\nconst user = writable(initUser)\n// Writable\u003cName | undefined\u003e\nconst partnerName = keyed(user, 'relations.partner.name')\n```\n\n### TypeScript\n\n`keyed` infers the return type of the keyed store from the keypath.\n\n```ts\nconst user = writable(initUser)\n// Writable\u003cstring\u003e\nconst firstName = keyed(user, 'name.first')\n```\n\n`keyed` will also try to guess all possible keypaths up to a depth limit of 3.\n\n```ts\nkeyed(user, '...');\n            ┌───────────────────────────────┐\n            │ • name                        │\n            │ • name.first                  │\n            │ • name.last                   │\n            │ • relations                   │\n            │ • relations.partner           │\n            │ • relations.partner.name      │\n            └───────────────────────────────┘\n```\n\n_This limit is due to a TypeScript limitation where structured types must be generated statically. Increasing the depth limit slows down type compilation._\n\nType hints will not be provided for keypaths with a depth greater than 3 but this does not affect the return type.\n\n```ts\nconst user = writable(user)\n// Writable\u003cstring | undefined\u003e\nconst firstName = keyed(user, 'relations.partner.name.first')\n```\n\n## Motivations\n\nWe usually read and write properties of an object store with [auto-subscriptions](https://svelte.dev/tutorial/auto-subscriptions).\n\n```svelte\n\u003cinput bind:value={$name.first} /\u003e\n```\n\nHowever, auto-subscriptions are isolated to a Svelte component. `svelte-keyed` aims to solve several common limitations listed below.\n\n### Context stores\n\nOften, we want to set a property or element of a store into component context, then allow child components to read / write to the property.\n\n```svelte\n\u003c!-- Settings.svelte --\u003e\n\u003cscript\u003e\n    setContext('profileSettings', keyed(settings, 'profile'))\n\u003c/script\u003e\n\n\u003cGeneralSettings /\u003e\n\u003cProfileSettings /\u003e\n```\n\n```svelte\n\u003c!-- ProfileSettings.svelte --\u003e\n\u003cscript\u003e\n    const profileSettings = getContext('profileSettings')\n\u003c/script\u003e\n\n\u003cinput type=\"text\" bind:value={$profileSettings.username} /\u003e\n```\n\n### Helper functions\n\nOne 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.\n\n```svelte\n\u003c!-- Settings.svelte --\u003e\n\u003cscript\u003e\n    const stats = writable({ userClicks: 0, userTaps: 0 })\n    const clicks = keyed(stats, 'userClicks')\n\u003c/script\u003e\n\n\u003cdiv use:trackClicks={clicks} /\u003e\n\u003cinput use:trackClicks={clicks} /\u003e\n```\n\n```js\nexport const trackClicks = (node, clicks) =\u003e {\n    const listen = () =\u003e {\n        clicks.update(($clicks) =\u003e $clicks + 1)\n    }\n    node.addEventListener('click', listen)\n    return {\n        destroy() {\n            node.removeEventListener('click', listen)\n        }\n    }\n}\n```\n\n## License\n\nMIT © [Humanspeak, Inc.](LICENSE)\n\n## Credits\n\nMade with ♥ by [Humanspeak](https://humanspeak.com)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhumanspeak%2Fsvelte-keyed","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhumanspeak%2Fsvelte-keyed","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhumanspeak%2Fsvelte-keyed/lists"}