{"id":16348471,"url":"https://github.com/tivac/xstate-component-tree","last_synced_at":"2026-02-11T02:53:27.611Z","repository":{"id":38042738,"uuid":"193595523","full_name":"tivac/xstate-component-tree","owner":"tivac","description":"Build a tree of UI components based on your statechart","archived":false,"fork":false,"pushed_at":"2024-10-01T18:54:24.000Z","size":2646,"stargazers_count":47,"open_issues_count":3,"forks_count":5,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-10-12T00:52:16.706Z","etag":null,"topics":["components","javascript","xstate"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/tivac.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"2019-06-24T23:28:10.000Z","updated_at":"2024-10-01T18:53:52.000Z","dependencies_parsed_at":"2023-02-19T02:46:05.503Z","dependency_job_id":"ce2976c7-1cbe-47ad-99e9-b42537880830","html_url":"https://github.com/tivac/xstate-component-tree","commit_stats":null,"previous_names":[],"tags_count":28,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tivac%2Fxstate-component-tree","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tivac%2Fxstate-component-tree/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tivac%2Fxstate-component-tree/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tivac%2Fxstate-component-tree/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tivac","download_url":"https://codeload.github.com/tivac/xstate-component-tree/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221665313,"owners_count":16860216,"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":["components","javascript","xstate"],"created_at":"2024-10-11T00:52:18.331Z","updated_at":"2026-02-11T02:53:27.605Z","avatar_url":"https://github.com/tivac.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# xstate-component-tree [![NPM Version](https://img.shields.io/npm/v/xstate-component-tree.svg)](https://www.npmjs.com/package/xstate-component-tree) [![NPM License](https://img.shields.io/npm/l/xstate-component-tree.svg)](https://www.npmjs.com/package/xstate-component-tree) [![NPM Downloads](https://img.shields.io/npm/dm/xstate-component-tree.svg)](https://www.npmjs.com/package/xstate-component-tree)\n\nUtility method to wrap up an [XState](https://xstate.js.org) actor and read state meta information so your statechart can be used to create a tree of components to render.\n\n## Installation\n\n```bash\n$\u003e npm install xstate-component-tree\n```\n\n## Usage\n\nCreate an XState statechart, and then instantiate an XState actor with it.\n\n```js\nconst { createMachine, createActor } = require(\"xstate\");\n\nconst statechart = createMachine({\n    initial : \"one\",\n\n    states : {\n        one : {},\n    },\n});\n\nconst service = createActor(statechart);\n```\n\nAdd `meta` objects to each state that you want to represent a component.\n\n```js\ncreateMachine({\n    initial : \"one\",\n\n    states : {\n        one : {\n            meta : {\n                component : MyComponent,\n            },\n        },\n    },\n});\n```\n\nProps for the components are also supported via the `props` key.\n\n```js\n    // ...\n    one : {\n        meta : {\n            component : MyComponent,\n            props : {\n                prop1 : 1\n            },\n        },\n    },\n    // ...\n```\n\nThen pass the actor instance and a callback function to this module!\n\n```js\nconst { createMachine, createActor } = require(\"xstate\");\nconst ComponentTree = require(\"xstate-component-tree\");\n\nconst statechart = createMachine({\n    // ...\n});\n\nconst actor = createActor(statechart);\n\nnew ComponentTree(actor, (tree) =\u003e {\n    // ...\n});\n```\n\nThe second argument to the function will be called every time the machine transitions. It will pass the callback a new object representing all the views defined on currently active states, all correctly nested to match the structure of the statechart. Each element in the response will also contain a `path` value corresponding to the the specific state the object represents.\n\n```js\nnew ComponentTree(actor, (tree) =\u003e {\n    /**\n     * \n     * tree will be something like this\n     * \n     * [{\n     *     path : \"one\",\n     *     component: MyComponent,\n     *     children: [],\n     *     props: false,\n     * }]\n     * \n     * or if there are nested components\n     * \n     * [{\n     *     path : \"one\",\n     *     component: MyComponent,\n     *     props: false\n     *     children : [{\n     *         path : \"one.two\",\n     *         component : ChildComponent,\n     *         props: {\n     *             one : 1\n     *         },\n     *         children: []\n     *     }]\n     * }]\n     * \n     */ \n});\n```\n\nThis data structure can also contain components from any child statecharts you created using `invoke`, they will be correctly walked \u0026 monitored for transitions and appear in their expected position within the hierarchy. This lets you compose a larger statechart from several smaller ones and still have them all contribute components to the app.\n\n## Advanced Usage\n\nYou can dynamically load components or props using whatever functionality you like via the `load` key. To load components asynchronously return a promise or use `async`/`await`.\n\n```js\n// ...\none : {\n    meta : {\n        load : () =\u003e import(\"./my/component/from/here.js\"),\n    },\n},\n// ...\n```\n\nDynamic props are also supported. To return props return an array from `load` where the first value is the component and the second is the props for the component. Both values support a returned promise.\n\n```js\n// ...\none : {\n    meta : {\n        load : ({ context }) =\u003e [\n            import(\"./my/component/from/here.js\"), \n            {\n                prop1 : context.prop1\n            },\n        ],\n    },\n},\n// ...\n```\n\nThe `load` function will be passed the `{ context, event }` params like xstate provides to actions.\n\n## The `component` helper\n\n`xstate-component-tree/component` has a named export called `component`, which is a small function to abstract away assignment to the `meta` object in each state node that needs a component. It's a convenience wrapper that makes writing with `xstate-component-tree` a little bit cleaner.\n\n```diff\nimport { component } from \"xstate-component-tree/component\";\n\n// ...\n- one : {\n-     meta: {\n-         component: OneComponent,\n-     },\n- },\n+ one : component(OneComponent),\n```\n\nSetting props for the component is handled by passing an object with `component` and `props` keys.\n\n```diff\nimport { component } from \"xstate-component-tree/component\";\n\n// ...\n- one : {\n-     meta: {\n-        component : MyComponent,\n-        props : {\n-            prop1 : 1\n-        },\n-     },\n- },\n+ one : component({\n+    component : OneComponent,\n+    props : {\n+        prop1 : 1,\n+    },\n+ }),\n```\n\nBoth the `component` and `props` key can be a function, they'll be passed the same `{ context, event }` arg that are normally passed to `load()` methods.\n\n```diff\nimport { component } from \"xstate-component-tree/component\";\n\n// ...\n- one : {\n-     meta : {\n-         load : ({ context, event }) =\u003e [\n-             import(\"./my/component/from/here.js\"),\n-             {\n-                 prop1 : context.prop1,\n-             },\n-         ],\n-     },\n- },\n+ one : component({\n+    component : () =\u003e import(\"./my/component/from/here.js\"),\n+    props : ({ context }) =\u003e ({\n+        prop1 : context.prop1,\n+    }),\n+ }),\n```\n\n\n## API\n\n### `ComponentTree`\n\n#### `new ComponentTree(actor, callback, [options])`\n\n- `actor`, an instance of a xstate actor\n- `callback`, a function that will be executed each time a new tree of components is ready\n- `options`, an optional object containing [configuration values](#options) for the library.\n\nThe `callback` functions receives two arguments, the first is your assembled tree of components \u0026 props. The second is an object with some useful information on it:\n\n- `.state`, the returned xstate `State` object for the root machine\n- `.broadcast()`, a bound version of the [`.broadcast()` API documented below](#broadcasteventname--eventobject-payload)\n- `.can()`, a bound version of the [`.can()` API documented below](#caneventname--eventobject)\n- `.hasTag()`, a bound version of the [`.hasTag()` API documented below](#hastagtag)\n- `.matches()`, a bound version of the [`.matches()` API documented below](#matchesstatename)\n\n#### `options`\n\n- `cache` (default `true`), a boolean determining whether or not the value of `load()` functions should be cached. This can be overriden by setting `meta.cache` on any state in the tree where caching needs to be disabled.\n\n- `stable` (default: `false`), tells the library to sort states alphabetically before walking them at each tier to help ensure that the component output is more consistent between state transitions.\n\n- `verbose` (default: `false`), logs out info about the internal workings \u0026 decisions.\n\n### `ComponentTree` instance methods\n\n#### `.broadcast(eventObject, payload)`\n\nCalls the xstate `.send()` method on every running interpreter in the hierarchy. This is especially useful to avoid the use of the `autoforward` option on all of your invoked child machines.\n\n- `eventObject` is an object with a `type` property of the event name, along with other optional fields\n- `payload` is an object of optional fields to be added to the event object\n\n#### `.can(eventObject)`\n\nCalls the xstate `.can()` method on every running interpreter in the hierarchy.\n\n- `eventObject` is an object with a `type` property of the event name, along with other optional fields\n\n#### `.hasTag(tag)`\n\n- `tag` is a string, which can be defined on states using the `tags` property\n\nCalls the [xstate `.hasTag()` method](https://xstate.js.org/docs/guides/states.html#state-hastag-tag) against all the running machines and returns the result, stopping at the first successful match.\n\n#### `.matches(stateName)`\n\n- `stateName` is a full or partial state value specified as a string\n\nCalls the [xstate `.matches()` method](https://xstate.js.org/docs/guides/states.html#state-matches-parentstatevalue) against all the running machines and returns the result, stopping at the first successful match.\n\n### `component()` helper\n\nThe `component` helper returns an `xstate` node as an object literal, it is solely a convenience method for statechart authors.\n\n#### `component(Component | () =\u003e {}, [node])`\n\n- `Component` is either a component or an arrow function that will be executed. It supports functions that return either a component or a `Promise`.\n- `node` is a valid xstate node, the `meta` object will be created and mixed-in by the `component()`.\n\n#### `component({ component : Component | () =\u003e {}, props : {...} | () =\u003e {} })`\n\n- `component` is either a raw Component or an arrow function that will be executed.  It supports returning either a value or a `Promise`.\n- `props` is either a props object or a function that will be executed. It supports function returning either a value or a `Promise`.\n\n## Rendering Components\n\nOnce you have the tree of components, how you assembled that into your view layer is entirely up to you! Here's a brief [svelte](https://svelte.dev) example.\n\n```html\n{#each components as { path, component, props, children } (path)}\n    \u003csvelte:component this={component} {...props} {children} /\u003e\n{/each}\n\n\u003cscript\u003e\nexport let components;\n\u003c/script\u003e\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftivac%2Fxstate-component-tree","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftivac%2Fxstate-component-tree","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftivac%2Fxstate-component-tree/lists"}