{"id":27634893,"url":"https://github.com/aleclarson/valtio-kit","last_synced_at":"2025-04-23T19:39:05.270Z","repository":{"id":271303450,"uuid":"912955246","full_name":"aleclarson/valtio-kit","owner":"aleclarson","description":"React state management library. Write plain old JavaScript. Compile it into a reactive masterpiece. Subscribe to immutable snapshots.","archived":false,"fork":false,"pushed_at":"2025-04-10T20:09:09.000Z","size":364,"stargazers_count":9,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-16T02:59:47.099Z","etag":null,"topics":["reactjs","valtio","vite-plugin"],"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/aleclarson.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"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}},"created_at":"2025-01-06T18:27:52.000Z","updated_at":"2025-04-10T20:09:11.000Z","dependencies_parsed_at":"2025-01-09T22:38:08.233Z","dependency_job_id":"542e1872-c882-4068-8cb2-6257af219314","html_url":"https://github.com/aleclarson/valtio-kit","commit_stats":null,"previous_names":["aleclarson/vite-react-state","aleclarson/valtio-kit"],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aleclarson%2Fvaltio-kit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aleclarson%2Fvaltio-kit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aleclarson%2Fvaltio-kit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aleclarson%2Fvaltio-kit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aleclarson","download_url":"https://codeload.github.com/aleclarson/valtio-kit/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250500898,"owners_count":21440907,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["reactjs","valtio","vite-plugin"],"created_at":"2025-04-23T19:39:04.396Z","updated_at":"2025-04-23T19:39:05.250Z","avatar_url":"https://github.com/aleclarson.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# valtio-kit\n\n“Valtio Kit” is a smart way to handle the data in your React apps, especially if you're tired of complicated state management. As a plugin for Vite, it uses the power of Valtio to let you write state logic with minimal boilerplate in plain JavaScript or TypeScript. This means less complex code for you, and a faster experience for your users because your React components can now subscribe to the exact data they need.\n\n```\npnpm add valtio-kit\n```\n\n- [StackBlitz playground](https://stackblitz.com/edit/valtio-kit?file=src%2FCounter.state.ts\u0026terminal=dev)\n\n## Usage\n\n1. Add the Vite plugin to your `vite.config.ts` file.\n\n```ts\nimport { valtioKit } from 'valtio-kit/vite'\n\nexport default defineConfig({\n  plugins: [\n    // These are the default options.\n    valtioKit({\n      include: /\\.state\\.[jt]s$/,\n      exclude: /\\/node_modules\\//,\n      globals: false,\n    }),\n  ],\n})\n```\n\n2. Create a module with a `.state.ts` or `.state.js` extension.\n\n3. (Optional) Enable the “globals API” to skip importing the various runtime functions provided by this package.\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003ci\u003eEnabling the globals API\u003c/i\u003e\u003c/summary\u003e\n\n- Keep your “state module” in a dedicated `src/state` folder. Then add a `tsconfig.json` with the following compiler option:\n\n```json\n\"compilerOptions\": {\n  \"types\": [\"valtio-kit/globals\"]\n}\n```\n\n- If you don't add a `tsconfig.json` file, you need to use a triple-slash directive instead:\n\n```ts\n/// \u003creference types=\"valtio-kit/globals\" /\u003e\n```\n\n- Finally, set `globals: true` in your `vite.config.ts` file:\n\n```ts\nexport default defineConfig({\n  plugins: [valtioKit({ globals: true })],\n})\n```\n\n\u003c/details\u003e\n\n4. Call `createClass` to define a reactive class. For example, here's a simple counter (please note that there's much, much more you can do with `createClass`):\n\n```ts\nimport { createClass } from 'valtio-kit'\n\nexport const Counter = createClass((initialCount = 0) =\u003e {\n  let count = initialCount\n\n  return {\n    count,\n    increment(amount = 1) {\n      count += amount\n    },\n    decrement(amount = 1) {\n      count -= amount\n    },\n  }\n})\n```\n\n\u003e [!NOTE]\n\u003e The function passed to `createClass` is known as the **factory function**, which initializes a **reactive instance** by returning an object literal. The function returned by `createClass` is known as a **reactive class**.\n\n5. Initialize a reactive instance with the `useInstance` hook. Before using its data to render your component, you should first pass it to Valtio's `useSnapshot` hook.\n\n```tsx\nimport { useInstance, useSnapshot } from 'valtio-kit/react'\nimport { Counter } from './Counter.state'\n\nexport function App() {\n  // Create a counter with an initial count of 100. Any persistent effects set up\n  // by the instance will be cleaned up when the component unmounts.\n  const counter = useInstance(Counter, 100)\n\n  // Subscribe to the counter's data. Only the data you use will trigger re-renders.\n  const { count, increment, decrement } = useSnapshot(counter)\n\n  return (\n    \u003cdiv\u003e\n      Count: {count}\n      \u003cbutton onClick={increment}\u003e+\u003c/button\u003e\n      \u003cbutton onClick={decrement}\u003e-\u003c/button\u003e\n    \u003c/div\u003e\n  )\n}\n```\n\nThe `useInstance` hook _creates_ a reactive instance, which your React components can subscribe to using the `useSnapshot` hook.\n\n#### Global state\n\nIn some cases, you may prefer to initialize a reactive instance outside of a React component. For example, you may want to initialize a global state object that can be accessed by any component.\n\n```ts\nimport { Counter } from './Counter.state'\n\n// Initialize a global counter with an initial count of 1.\nexport const counter = new Counter(1)\n```\n\nIf your global instance sets up any persistent effects (i.e. `computed`, `watch`, `on`, etc.), you need to clean up the effects when Vite HMR is triggered.\n\n```ts\nimport.meta.hot?.dispose(() =\u003e counter.release())\n```\n\n### Terminology\n\nThis package borrows terminology from [Valtio](https://github.com/pmndrs/valtio). For example, a **snapshot** is an immutable copy of a reactive instance, which can intelligently rerender your React components if an accessed property changes. You **subscribe** to a reactive instance (or its property) to be notified when it changes. In Valtio, a reactive instance is referred to as a **proxy**.\n\n## Rules\n\nThere are a few rules to keep in mind inside a `createClass` factory function:\n\n- You must return an object literal.\n- Root-level `let` and `var` declarations are _deeply_ reactive by default.\n- If you re-assign a factory parameter, it becomes _deeply_ reactive. Notably, this behavior does not apply to _properties_ of object/array parameters (unless you also re-assign the parameter itself).\n- When you return a non-`const` variable as a property, a one-way binding is implicitly created, so assigning to the variable will re-render any components that use the property.\n- Certain objects are _deeply_ reactive when assigned to root-level variables. This is even true when assignment occurs inside a nested function. Supported object types include:\n  - plain objects\n  - arrays\n  - `new Map()`\n  - `new Set()`\n- The factory function can have arguments. Any kind and any number of arguments are supported.\n- Passing a reactive instance into a factory function is not currently supported.\n\n### Further Reading\n\nCheck out the `docs/` folder for more information.\n\n- **Basics**\n  - [Variables](/docs/variables.md)\n  - [Parameters](/docs/parameters.md)\n  - [Objects](/docs/objects.md)\n  - [React Integration](/docs/react.md)\n  - [Computed Bindings](/docs/computed.md)\n- **Advanced**\n  - [Gotchas](/docs/gotchas.md)\n  - [Debugging](/docs/debug.md)\n  - [Class Extensions](/docs/class-extension.md)\n\nIt's also recommended to read the API reference below.\n\n## API\n\nThe valtio-kit API is intentionally minimal. Syntax sugar is generally avoided in favor of plain JavaScript. There exists utilities for readonly reactive values, reactive functions, managed event listeners, and snapshots.\n\n### `computed`\n\n`computed` is a function that subscribes to reactive values and returns a new reactive value.\n\n\u003e [!NOTE]\n\u003e Computed values _cannot_ be declared just anywhere. You can only call `computed` with the following syntax and it must be declared at the **root level** of a `createClass` factory function:\n\u003e\n\u003e ```ts\n\u003e // This is a readonly, computed variable.\n\u003e const xyz = computed(() =\u003e …)\n\u003e\n\u003e // This is a readonly, computed property.\n\u003e const foo = { bar: computed(() =\u003e …) }\n\u003e\n\u003e // This is a computed property assignment.\n\u003e foo.bar = computed(() =\u003e …)\n\u003e ```\n\n```ts\nconst TacoExample = createClass(() =\u003e {\n  let day = 'Monday'\n  const taco = { type: 'beef' }\n  const isTacoTuesday = computed(\n    () =\u003e day === 'Tuesday' \u0026\u0026 taco.type === 'beef'\n  )\n\n  return {\n    isTacoTuesday,\n    setDay(newValue: string) {\n      day = newValue\n    },\n    setTacoType(type: string) {\n      taco.type = type\n    },\n  }\n})\n\nconst example = new TacoExample()\nexample.isTacoTuesday // =\u003e false\nexample.setDay('Tuesday')\nexample.isTacoTuesday // =\u003e true\nexample.setTacoType('chicken')\nexample.isTacoTuesday // =\u003e false\n```\n\n### `watch`\n\n`watch` is a persistent effect that reruns when its reactive dependencies change.\n\n```ts\nconst CatExample = createClass(() =\u003e {\n  // Any of this data can be watched.\n  const cat = { name: 'Fluffy' }\n  let numLives = 9\n\n  watch(() =\u003e {\n    console.log(\n      `The cat named ${cat.name} has ${numLives} lives remaining. Meow!`\n    )\n  })\n\n  return {\n    renameCat(name: string) {\n      cat.name = name\n    },\n    fallFromTree() {\n      numLives--\n    },\n    eatFish() {\n      numLives++\n    },\n  }\n})\n\nconst cat = new CatExample()\n// Logs \"The cat named Fluffy has 9 lives remaining. Meow!\"\n\ncat.fallFromTree()\n// Logs \"The cat named Fluffy has 8 lives remaining. Meow!\"\n\ncat.renameCat('Whiskers')\n// Logs \"The cat named Whiskers has 8 lives remaining. Meow!\"\n\ncat.eatFish()\n// Logs \"The cat named Whiskers has 9 lives remaining. Meow!\"\n```\n\n### `on`\n\n`on` is a function that attaches an event listener to any `EventTarget`.\n\n```ts\nconst ResizeExample = createClass(() =\u003e {\n  let ratio = window.innerWidth / window.innerHeight\n  on(window, 'resize', () =\u003e {\n    ratio = window.innerWidth / window.innerHeight\n  })\n  return {\n    ratio,\n  }\n})\n\nconst example = new ResizeExample()\nexample.ratio // Updates when the window is resized.\n```\n\n### `onMount`\n\n`onMount` is a function that runs a callback when a reactive instance is mounted. The callback must return a cleanup function, which gets called when the reactive instance is unmounted.\n\n```ts\nconst StyleSheetExample = createClass(() =\u003e {\n  const style = document.createElement('style')\n  onMount(() =\u003e {\n    document.head.appendChild(style)\n    return () =\u003e {\n      document.head.removeChild(style)\n    }\n  })\n  return {\n    style,\n  }\n})\n\nconst example = new StyleSheetExample()\nexample.style.textContent = 'body { background-color: red; }'\n```\n\n### `onUpdate`\n\n`onUpdate` is a function that runs a callback when a reactive instance has its `update` method called. It receives the latest factory arguments. This can happen one of two ways:\n\n- By calling the `update` method directly.\n- By a component re-rendering (but only if the `useInstance(MyClass, ...args)` hook signature is used). Notably, the `useInstance(() =\u003e new MyClass(), deps)` hook signature will never trigger `onUpdate` handlers.\n\n```ts\nconst AudioPlayer = createClass((src: string) =\u003e {\n  const audio = new HTMLAudioElement()\n  // Update `audio.src` whenever `src` is changed.\n  audio.src = computed(() =\u003e src)\n\n  // By assigning to `src`, we make it reactive.\n  onUpdate\u003ctypeof AudioPlayer\u003e((...args) =\u003e ([src] = args))\n\n  return {\n    /* ... */\n  }\n})\n```\n\n### `subscribe`\n\n`subscribe` is a function that listens for changes to a given reactive object or even a reactive variable.\n\n```ts\nconst SubscribeExample = createClass(() =\u003e {\n  let a = 0\n  const b = { c: 1 }\n\n  // Listen for changes to the `a` variable.\n  subscribe(a, () =\u003e {\n    console.log('a changed to', a)\n  })\n\n  // Listen for changes to the `b` object.\n  subscribe(b, () =\u003e {\n    console.log('b changed to', b)\n  })\n\n  return {\n    update(update: { a: number; b: { c: number } }) {\n      a = update.a\n      Object.assign(b, update.b)\n    },\n  }\n})\n\nconst example = new SubscribeExample()\n\nexample.update({ a: 1, b: { c: 2 } })\n// Logs \"a changed to 1\"\n// Logs \"b changed to { c: 2 }\"\n```\n\n### `subscribeKey`\n\n`subscribeKey` is a function that listens for changes to a specific key of a reactive object.\n\n```ts\nconst SubscribeKeyExample = createClass(() =\u003e {\n  const b = { c: 1 }\n  subscribeKey(b, 'c', () =\u003e {\n    console.log('b.c changed to', b.c)\n  })\n  return {\n    update(update: { b: { c: number } }) {\n      Object.assign(b, update.b)\n    },\n  }\n})\n\nconst example = new SubscribeKeyExample()\n\nexample.update({ b: { c: 1 } })\n// Logs nothing since b.c is already 1\n\nexample.update({ b: { c: 2 } })\n// Logs \"b.c changed to 2\"\n```\n\n### `snapshot`\n\n`snapshot` is a function that returns an immutable, deep copy of a reactive object. [Learn more](https://valtio.dev/docs/api/advanced/snapshot)\n\n### `ref`\n\n`ref` is a function that prevents an object from being made reactive. [Learn more](https://valtio.dev/docs/api/advanced/ref)\n\n### `getVersion`\n\n`getVersion` is a function that returns a “version” number that represents when a reactive object was last updated. [Learn more](https://valtio.dev/docs/api/hacks/getVersion)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faleclarson%2Fvaltio-kit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faleclarson%2Fvaltio-kit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faleclarson%2Fvaltio-kit/lists"}