{"id":22272668,"url":"https://github.com/0xjcf/ignite-element","last_synced_at":"2026-03-07T11:02:37.997Z","repository":{"id":263932305,"uuid":"891835422","full_name":"0xjcf/ignite-element","owner":"0xjcf","description":"Create web components with support for multiple state management libraries. Integrate with Redux, MobX, or XState seamlessly while maintaining flexibility and a consistent API.","archived":false,"fork":false,"pushed_at":"2025-12-16T22:55:09.000Z","size":981,"stargazers_count":0,"open_issues_count":8,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-20T11:56:46.082Z","etag":null,"topics":["lit-html","mobx","redux","state-management","vanilla-js","vitest","wallabyjs","web-components","xstate"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/ignite-element?activeTab=readme","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/0xjcf.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","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,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"0xjcf"}},"created_at":"2024-11-21T03:19:02.000Z","updated_at":"2025-12-16T22:55:12.000Z","dependencies_parsed_at":"2024-12-11T04:24:33.037Z","dependency_job_id":"ea31e435-ba9e-4f7d-a66e-27b5611098bf","html_url":"https://github.com/0xjcf/ignite-element","commit_stats":null,"previous_names":["0xjcf/igniteelement","0xjcf/ignite-element"],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/0xjcf/ignite-element","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xjcf%2Fignite-element","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xjcf%2Fignite-element/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xjcf%2Fignite-element/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xjcf%2Fignite-element/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/0xjcf","download_url":"https://codeload.github.com/0xjcf/ignite-element/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xjcf%2Fignite-element/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30212103,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-07T09:02:10.694Z","status":"ssl_error","status_checked_at":"2026-03-07T09:02:08.429Z","response_time":53,"last_error":"SSL_read: 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":["lit-html","mobx","redux","state-management","vanilla-js","vitest","wallabyjs","web-components","xstate"],"created_at":"2024-12-03T13:07:58.822Z","updated_at":"2026-03-07T11:02:37.991Z","avatar_url":"https://github.com/0xjcf.png","language":"TypeScript","funding_links":["https://github.com/sponsors/0xjcf"],"categories":[],"sub_categories":[],"readme":"# ignite-element\n\n[![CI Build](https://github.com/0xjcf/ignite-element/actions/workflows/ci.yml/badge.svg)](https://github.com/0xjcf/ignite-element/actions/workflows/ci.yml)\n[![npm version](https://img.shields.io/npm/v/ignite-element.svg)](https://www.npmjs.com/package/ignite-element)\n[![Bundlephobia](https://img.shields.io/bundlephobia/minzip/ignite-element.svg)](https://bundlephobia.com/package/ignite-element)\n[![Zero Dependencies](https://img.shields.io/badge/dependencies-0-brightgreen.svg)](https://bundlephobia.com/package/ignite-element)\n[![Tree-shakeable](https://img.shields.io/badge/tree--shakeable-yes-blue.svg)](https://bundlephobia.com/package/ignite-element)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![TypeScript Ready](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)\n[![codecov](https://codecov.io/github/0xjcf/ignite-element/graph/badge.svg?token=6SSFPOV9J8)](https://codecov.io/github/0xjcf/ignite-element)\n![CodeRabbit Pull Request Reviews](https://img.shields.io/coderabbit/prs/github/0xjcf/ignite-element?utm_source=oss\u0026utm_medium=github\u0026utm_campaign=0xjcf%2Fignite-element\u0026labelColor=171717\u0026color=FF570A\u0026link=https%3A%2F%2Fcoderabbit.ai\u0026label=CodeRabbit+Reviews)\n\n---\n\n**Ignite-Element** is a framework-agnostic way to build stateful Custom Elements. Bring your state library (XState, Redux, MobX), get typed `commands`, `states`, and `emit`, and render with the built-in Ignite JSX runtime or lit.\n\nQuick links: [Quick start](#quick-start-vite) · [Install matrix](#installation-matrix) · [Typed events](#typed-events) · [Styling](#styling) · [Examples](#examples)\n\n## Why use it?\n\n- Works with XState, Redux, or MobX (shared or per-element state, inferred automatically)\n- Fully Typed commands and emit\n- Tiny runtime; no React/Solid dependency for JSX\n- Configurable renderer and global styles through `ignite.config.ts`\n\n## Quick start (Vite)\n\n1. Install\n\n```bash\nnpm install ignite-element xstate\n```\n\n1. TypeScript JSX (required if you use the Ignite JSX renderer)\n\n```jsonc\n// tsconfig.json\n{\n  \"compilerOptions\": {\n    \"jsx\": \"react-jsx\",\n    \"jsxImportSource\": \"ignite-element/jsx\"\n  }\n}\n```\n\nIf you can’t change `tsconfig`, add `/** @jsxImportSource ignite-element/jsx */` at the top of each JSX/TSX file instead.\n\n1. Add config (all fields are optional)\n\n```ts\n// ignite.config.ts\nimport { defineIgniteConfig } from \"ignite-element/config\";\nexport default defineIgniteConfig({\n  styles: new URL(\"./styles.css\", import.meta.url).href,\n  renderer: \"ignite-jsx\", // or \"lit\"\n  logging: \"warn\",\n});\n```\n\n1. Wire the Vite plugin\n\n```ts\n// vite.config.ts\nimport { defineConfig } from \"vite\";\nimport { igniteConfigVitePlugin } from \"ignite-element/config/vite\";\nexport default defineConfig({ plugins: [igniteConfigVitePlugin()] });\n```\n\n1. Create a component\n\n```tsx\nimport { createMachine } from \"xstate\";\nimport { igniteCore } from \"ignite-element/xstate\";\n\nconst machine = createMachine({ \n  initial: \"off\", \n  states: { \n    off: { on: { TOGGLE: \"on\" } }, \n    on: { on: { TOGGLE: \"off\" } } \n  } \n});\n\nconst component = igniteCore({\n  source: machine,\n  events: (event) =\u003e ({ toggled: event\u003c{ isOn: boolean }\u003e() }),\n  states: (snapshot) =\u003e ({ isOn: snapshot.matches(\"on\") }),\n  commands: ({ actor, emit }) =\u003e ({\n    toggle: () =\u003e {\n      actor.send({ type: \"TOGGLE\" });\n      emit(\"toggled\", { isOn: actor.getSnapshot().matches(\"on\") });\n    },\n  }),\n});\n\ncomponent(\"toggle-button\", ({ isOn, toggle }) =\u003e (\n  \u003cbutton onClick={toggle}\u003e{isOn ? \"On\" : \"Off\"}\u003c/button\u003e\n));\n```\n\n1. Use it\n\n```html\n\u003ctoggle-button\u003e\u003c/toggle-button\u003e\n```\n\n## Installation matrix\n\n- XState: `npm install ignite-element xstate`\n- Redux: `npm install ignite-element @reduxjs/toolkit`\n- MobX: `npm install ignite-element mobx`\n\n### Cleanup \u0026 Teardown\n\n- **Isolated adapters** (the default when you pass factories or definitions) are created per custom element. Ignite Element automatically calls `stop()` on disconnect, so no extra work is required.\n- **Shared adapters** (long-lived instances you construct once) are reference-counted and stopped automatically when the final element disconnects. Set `cleanup: false` if you want to keep them alive and stop them manually.\n\n```ts\n// Shared XState actor example\nconst actor = createActor(machine);\nactor.start();\n\nconst shared = igniteCore({\n  source: actor,\n  cleanup: false, // leave actor running until the host decides to stop it\n  states: (snapshot) =\u003e ({ count: snapshot.context.count }),\n});\n\nshared(\"shared-counter\", ({ count }) =\u003e \u003cspan\u003e{count}\u003c/span\u003e);\n\n// Stop the actor when your host application shuts down\nwindow.addEventListener(\"beforeunload\", () =\u003e actor.stop());\n```\n\nUse the same approach for shared Redux stores, MobX observables, or any custom adapters: set `cleanup: false` if they outlive your elements and stop them yourself when the host app shuts down.\n\n### Facade callbacks\n\n`igniteCore` merges the outputs of your facade callbacks into the render arguments:\n\n- `states(snapshot)` derives the values your component needs to display.\n- `commands({ actor, emit, host })` returns the actions your component can call; when you declare `events`, it also includes the typed `emit` helper and the `host` element.\n\nBoth callbacks run once per adapter instance (shared) or per element (isolated), so you can safely memoize values or close over resources without worrying about duplicate subscriptions.\n\n### Typed events\n\nOpt in by declaring an `events` map:\n\n```ts\nconst registerCounter = igniteCore({\n  source: counterSlice,\n  events: (event) =\u003e ({\n    \"counter:incremented\": event\u003c{ amount: number }\u003e(),\n  }),\n  commands: ({ actor, emit }) =\u003e ({\n    add: (amount: number) =\u003e {\n      actor.dispatch(counterSlice.actions.addByAmount(amount));\n      emit(\"counter:incremented\", { amount });\n    },\n  }),\n});\n```\n\nCommands receive `{ actor, emit, host }`. The `emit` helper dispatches bubbling, composed `CustomEvent` instances so parents can listen with `addEventListener`. When no `events` map is supplied the helper is omitted, keeping render args lean.\n\n\u003e Heads-up: event name inference is most reliable when `events` is declared before `commands`. We’re tightening this in a future release.\n\n### Styling\n\nYou can:\n\n- Declare component-wide styles in `ignite.config.ts` (`styles`, formerly `globalStyles`, accepts a string URL or object literal stylesheet). These are injected into each component’s **shadow root**, not the page’s light DOM.\n- Provide custom CSS per component.\n- Combine both for progressive enhancement.\n\nFor page shell / light-DOM styling (e.g. body background, layout), import a stylesheet in your app entry or include a `\u003clink\u003e` in `index.html`. Use `styles` for the component layer.\n\nIf you aren’t using the Vite/Webpack plugins, keep `ignite.config.ts` and import it in your app’s entry point (e.g. `main.ts`) so `styles` and renderer defaults are applied before you register components.\n\n---\n\n## Examples\n\nEvery example demonstrates a different pattern and styling approach:\n\n| Example | State Library | Styling | Highlights |\n| --- | --- | --- | --- |\n| [XState + Tailwind](./src/examples/xstate) | XState | Tailwind CSS | Isolated machine vs. shared actor, gradient sub-component |\n| [Redux + Bootstrap](./src/examples/redux) | Redux Toolkit | Bootstrap | Store factory vs. shared store, scoped Bootstrap link injection |\n| [MobX + Custom](./src/examples/mobx) | MobX | Custom CSS | Observable reuse vs. new instances, hybrid global + component styles |\n\n### Run locally\n\n```bash\npnpm run examples:xstate\npnpm run examples:redux\npnpm run examples:mobx\n```\n\n\u003e 💡 Start with the XState example to see shared and isolated behaviour side-by-side.\n\n---\n\n## 🌐 Browser Support\n\nIgnite-Element targets evergreen browsers with:\n\n- Custom Elements v1\n- Shadow DOM v1\n- ES Modules\n\n| Chrome | Firefox | Safari | Edge |\n| --- | --- | --- | --- |\n| ✅ 67+ | ✅ 63+ | ✅ 10.1+ | ✅ 79+ |\n\nFor legacy support, include the [webcomponents polyfills](https://github.com/webcomponents/polyfills).\n\n---\n\n## 📦 Bundle Size\n\n| Package | Description | Size (min + gzip) |\n| --- | --- | --- |\n| `ignite-element` | Core runtime (facades, adapters) | ~3.2 KB |\n| `ignite-element` (Ignite JSX) | Core runtime + Ignite JSX renderer | ~4.2 KB |\n| `ignite-element` + `lit-html` | Optional lit strategy | ~8.3 KB |\n\n_Rendering engines and state libraries (`lit-html`, XState, Redux Toolkit, MobX) are optional peer dependencies. Mix only what your project needs—ignite-element itself adds ~4 KB on top of the stack you choose._\n\n---\n\n---\n\n## 📖 Documentation\n\n- [Ignite Element v2 (Starlight)](https://0xjcf.github.io/ignite-element/)\n- [Getting Started (v2)](https://0xjcf.github.io/ignite-element/getting-started/installation/)\n- [Core Concepts (v2)](https://0xjcf.github.io/ignite-element/concepts/state-adapters/)\n- [API Notes](docs/api/README.md)\n- [Styling Guide](docs/styling/README.md)\n- [Examples Overview](docs/examples/README.md)\n\n---\n\n## 🔧 Troubleshooting\n\n| Symptom | Fix |\n| --- | --- |\n| Component not rendering | Ensure you've configured `jsxImportSource` (or installed `lit-html` and selected the lit strategy). |\n| State not updating | Confirm you’re using the provided `send` function and that your store/machine handles the event. |\n| TypeScript errors | Align adapter dependencies (`xstate`, `@reduxjs/toolkit`, `mobx`) with the versions in package peer requirements. |\n\nNeed more help? Check the [FAQ](https://joseflores.gitbook.io/ignite-element/faq) or [open an issue](https://github.com/0xjcf/ignite-element/issues).\n\n---\n\n## 🎯 When to Use Ignite-Element\n\n**Best fit:**\n\n- Building reusable, state-driven component libraries.\n- Projects that need framework flexibility or native web component distribution.\n- Teams looking for deterministic state management with minimal runtime overhead.\n\n**Consider alternatives when:**\n\n- You are deeply invested in a single framework (React, Vue, etc.) and prefer their native component models.\n- Server-side rendering is a strict requirement today (SSR support is on the roadmap).\n\n---\n\n## 🤝 Contributing\n\nWe welcome all contributions!\n\n- 🐛 [Report bugs](https://github.com/0xjcf/ignite-element/issues/new?template=bug_report.md)\n- 💡 [Propose ideas](https://github.com/0xjcf/ignite-element/discussions)\n- 📝 Improve docs, clarify examples, or fix typos\n- 🔨 Submit pull requests\n\n### Development setup\n\n```bash\ngit clone https://github.com/\u003cyour-username\u003e/ignite-element.git\ncd ignite-element\npnpm install\ngit checkout -b feature/my-awesome-feature\npnpm test\n```\n\nPlease review our [Code of Conduct](CODE_OF_CONDUCT.md) before contributing.\n\n---\n\n## 📜 License\n\nIgnite-Element is released under the MIT License.\n\n---\n\n## 💬 Feedback\n\nWe appreciate feedback—let us know what helps or what’s missing.\n\n- [Open an issue](https://github.com/0xjcf/ignite-element/issues)\n- [Join GitHub Discussions](https://github.com/0xjcf/ignite-element/discussions)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0xjcf%2Fignite-element","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F0xjcf%2Fignite-element","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0xjcf%2Fignite-element/lists"}