{"id":14990419,"url":"https://github.com/michijs/michijs","last_synced_at":"2025-09-14T22:23:26.589Z","repository":{"id":46017643,"uuid":"277625886","full_name":"michijs/michijs","owner":"michijs","description":"Web Components with TSX","archived":false,"fork":false,"pushed_at":"2025-07-21T15:04:35.000Z","size":2936,"stargazers_count":38,"open_issues_count":3,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-07-21T16:45:16.971Z","etag":null,"topics":["autonomous-web-components","built-in-components","constructable-stylesheets","css-modules","shadow-dom","tsx","web-components"],"latest_commit_sha":null,"homepage":"https://dev.michijs.com","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/michijs.png","metadata":{"files":{"readme":"README.md","changelog":"changelog.md","contributing":null,"funding":null,"license":"LICENSE.md","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":["michijs"],"patreon":null,"open_collective":"michijs","ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":"https://www.paypal.com/paypalme/lsegurado2"}},"created_at":"2020-07-06T19:04:04.000Z","updated_at":"2025-07-21T15:04:21.000Z","dependencies_parsed_at":"2023-10-12T01:13:54.000Z","dependency_job_id":"1f0c71a4-6db3-476c-a378-b0e638f3b8ba","html_url":"https://github.com/michijs/michijs","commit_stats":{"total_commits":544,"total_committers":4,"mean_commits":136.0,"dds":0.4779411764705882,"last_synced_commit":"23a12a75bd6f85b56835d00cae8565e67efa9052"},"previous_names":[],"tags_count":207,"template":false,"template_full_name":null,"purl":"pkg:github/michijs/michijs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michijs%2Fmichijs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michijs%2Fmichijs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michijs%2Fmichijs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michijs%2Fmichijs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/michijs","download_url":"https://codeload.github.com/michijs/michijs/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michijs%2Fmichijs/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266342443,"owners_count":23914239,"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","status":"online","status_checked_at":"2025-07-21T11:47:31.412Z","response_time":64,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"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":["autonomous-web-components","built-in-components","constructable-stylesheets","css-modules","shadow-dom","tsx","web-components"],"created_at":"2024-09-24T14:20:06.731Z","updated_at":"2025-07-26T16:08:32.956Z","avatar_url":"https://github.com/michijs.png","language":"TypeScript","funding_links":["https://github.com/sponsors/michijs","https://opencollective.com/michijs","https://www.paypal.com/paypalme/lsegurado2","https://opencollective.com/ls-element"],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg width=\"500px\" alt=\"Michijs Logo\" src=\"https://raw.githubusercontent.com/michijs/art/main/logo-with-background-strait-round.png\"\u003e\n  \u003cp\u003eA lightweight vanilla JavaScript library for creating Web Components\u003c/p\u003e\n  \u003cp dir=\"auto\"\u003e\n    \u003ca href=\"https://vscode.dev/github/michijs/michijs-template\" rel=\"nofollow\"\u003e\n      \u003cimg src=\"https://img.shields.io/static/v1?label=Open%20in\u0026logo=Visual%20Studio%20Code\u0026message=Visual%20Studio%20Code\u0026logoColor=007ACC\u0026color=007ACC\" alt=\"Open in Visual Studio Code\"\u003e\n    \u003c/a\u003e\n    \u003cimg src=\"https://img.shields.io/npm/v/@michijs/michijs\" alt=\"Version\"\u003e\n    \u003ca href=\"https://github.com/michijs/michijs/blob/master/LICENSE.md\" rel=\"nofollow\"\u003e\n      \u003cimg src=\"https://img.shields.io/github/license/michijs/michijs\" alt=\"MIT\"\u003e\n    \u003c/a\u003e\n    \u003cimg src=\"https://img.shields.io/npm/dt/@michijs/michijs\" alt=\"Downloads\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/repo-size/michijs/michijs\" alt=\"Repo size\"\u003e\n    \u003cimg src=\"https://img.shields.io/bundlephobia/minzip/@michijs/michijs\" alt=\"Minzipped size\"\u003e\n    \u003ca href=\"https://github.com/michijs/michijs/actions/workflows/tests.yml\" rel=\"nofollow\"\u003e\n      \u003cimg src=\"https://github.com/michijs/michijs/actions/workflows/tests.yml/badge.svg\" alt=\"Tests\"\u003e\n    \u003c/a\u003e\n  \u003c/p\u003e\n\u003c/div\u003e\n\n## Why *MichiJS*?\n\n| Feature | MichiJS | React | StencilJS | SvelteJS | VueJS | VanillaJS |\n|--|--|--|--|--|--|--|\n| Real DOM preferred over virtual DOM | ✅ | ❌ | ❌ | ✅ | ❌ | ✅ |\n| Dynamic constructable stylesheets | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |\n| General constructable stylesheets | ✅ | ❌ | ✅ | ❌ | ❌ | ✅ |\n| JavaScript templates preferred over compiled text | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ |\n| Templates with [JSX](https://es.reactjs.org/docs/introducing-jsx.html) | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |\n| [Element internals](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) support | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |\n| IDE-friendly without special extensions | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ |\n| Attribute vs property distinction in JSX/templates | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |\n| Standard Web Components | ✅ | ⭕ \u003csup\u003e1\u003c/sup\u003e | ✅ | ✅ | ⭕ \u003csup\u003e2\u003c/sup\u003e | ✅ |\n| Observables/stores support | ✅ | ⭕ \u003csup\u003e3\u003c/sup\u003e | ⭕ \u003csup\u003e3\u003c/sup\u003e | ⭕ \u003csup\u003e3\u003c/sup\u003e | ⭕ \u003csup\u003e3\u003c/sup\u003e | ❌ |\n| [Esbuild](https://esbuild.github.io/) as default bundler | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |\n| [TypeScript](https://www.typescriptlang.org) support | ✅ | ✅ | ✅ | ✅ | ✅ | ⭕ \u003csup\u003e3\u003c/sup\u003e |\n| Reactive programming | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |\n| Automatic component type generation | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |\n| Native attributes/events support | ✅ | ❌ \u003csup\u003e4\u003c/sup\u003e | ⭕ \u003csup\u003e5\u003c/sup\u003e | ✅ | ✅ | ✅ |\n| [Shadow DOM](https://developers.google.com/web/fundamentals/web-components/shadowdom) support | ✅ | ❌ | ✅ | ✅ | ❌ | ✅ |\n| Custom built-in elements support | ✅ | ❌ | ❌ | ✅ | ❌ | ✅ |\n| Cross-framework compatibility | ✅ | ❌ | ✅ | ⭕ \u003csup\u003e6\u003c/sup\u003e | ❌ | ✅ |\n\n✅ = Fully implemented  \n⭕ = Partially implemented  \n❌ = Not implemented\n\n\u003cdetails\u003e\n  \u003csummary\u003eMore details\u003c/summary\u003e\n  \u003col\u003e\n    \u003cli\u003eReact supports Web Components starting from version 19.\u003c/li\u003e\n    \u003cli\u003eVue has partial support for Web Components, but usage requires workarounds or third-party packages.\u003c/li\u003e\n    \u003cli\u003eRequires external packages, not a unique-state-first approach.\u003c/li\u003e\n    \u003cli\u003eReact only supports properties and synthetic events.\u003c/li\u003e\n    \u003cli\u003eStencilJS supports only properties.\u003c/li\u003e\n    \u003cli\u003eSvelte components can be shared only through custom elements.\u003c/li\u003e\n  \u003c/ol\u003e\n\u003c/details\u003e\n\n## Getting Started\n\nYou can start by using [this template](https://github.com/michijs/michijs-template) to quickly set up your project. Alternatively, you can explore the [Code Sandbox](https://githubbox.com/michijs/michijs-storybook-template/tree/master) version for a hands-on example.\n  \n## Creating components\n\n### Your first custom element\nMichiJS custom elements are plain objects.\n\nNew components can be created using the `jsx/tsx` extension, such as `MyCounter.tsx`.\n\n```tsx\nimport { createCustomElement, EventDispatcher } from \"@michijs/michijs\";\nimport { counterStyle } from \"./counterStyle\";\n\nexport const SimpleCounter = createCustomElement(\"simple-counter\", {\n  reflectedAttributes: {\n    count: 0,\n  },\n  methods: {\n    decrementCount() {\n      this.count(this.count() - 1);\n    },\n    incrementCount() {\n      this.count(this.count() + 1);\n    },\n  },\n  events: {\n    countChanged: new EventDispatcher\u003cnumber\u003e(),\n  },\n  adoptedStyleSheets: { counterStyle },\n  render() {\n    this.count.subscribe(this.countChanged);\n    return (\n      \u003c\u003e\n        \u003cbutton onpointerup={this.decrementCount}\u003e-\u003c/button\u003e\n        \u003cspan\u003e{this.count}\u003c/span\u003e\n        \u003cbutton onpointerup={this.incrementCount}\u003e+\u003c/button\u003e\n      \u003c/\u003e\n    );\n  },\n});\n```\n\nNote: the `.tsx` extension is required, as this is the standard for TypeScript classes that use JSX.\n\nTo use this component, just use it like any other HTML element:\n\n```tsx\nimport '../Counter';\n\n\u003cmy-counter oncountchanged={(ev) =\u003e console.log(`New count value: ${ev.detail}`)} /\u003e\n```\n\nOr if you are using JSX\n```tsx\nimport Counter from '../Counter';\n\n\u003cCounter oncountchanged={(ev) =\u003e console.log(`New count value: ${ev.detail}`)} /\u003e\n```\n\n### Component Structure\nA component consists of the following properties:\n\n\u003ctable\u003e\n  \u003cthead\u003e\n    \u003ctr\u003e\n      \u003cth\u003eProperty\u003c/th\u003e\n      \u003cth colspan=\"3\"\u003eDescription\u003c/th\u003e\n    \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003ctd\u003eattributes\u003c/td\u003e\n      \u003ctd colspan=\"3\"\u003eAllows defining attributes.\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003ereflectedAttributes\u003c/td\u003e\n      \u003ctd colspan=\"3\"\u003eAllows defining \u003ca href=\"https://developers.google.com/web/fundamentals/web-components/customelements#reflectattr\"\u003ereflected attributes\u003c/a\u003e and follows the Kebab case. A reflected attribute cannot be initialized with a true value.\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003emethods\u003c/td\u003e\n      \u003ctd colspan=\"3\"\u003eMethods are functions that notify changes at the time of making the change.\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003eadoptedStyleSheets\u003c/td\u003e\n      \u003ctd colspan=\"3\"\u003eAllows using \u003ca href=\"https://developers.google.com/web/updates/2019/02/constructable-stylesheets\"\u003eConstructable Stylesheets\u003c/a\u003e. Remember that you need to use Shadow DOM to utilize Constructable Stylesheets; otherwise, it will return a style tag.\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003ecssVariables\u003c/td\u003e\n      \u003ctd colspan=\"3\"\u003eAllows defining CSS variables.\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003ereflectedCssVariables\u003c/td\u003e\n      \u003ctd colspan=\"3\"\u003eAllows defining reflected CSS variables and follows the Kebab case. A reflected CSS variable cannot be initialized with a true value.\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003ecomputedStyleSheet\u003c/td\u003e\n      \u003ctd colspan=\"3\"\u003eAllows defining a Constructable Stylesheet that depends on the component's state. When there is no shadow root, the style will be reflected in the style attribute.\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003erender\u003c/td\u003e\n      \u003ctd colspan=\"3\"\u003eFunction that renders the component.\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd rowspan=\"16\"\u003elifecycle\u003c/td\u003e\n      \u003ctr\u003e\n        \u003ctd rowspan=\"10\"\u003eCustom Element related\u003c/td\u003e\n        \u003ctr\u003e\n          \u003ctd\u003ewillConstruct\u003c/td\u003e\n          \u003ctd\u003eThis method is called at the start of the constructor.\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n          \u003ctd\u003edidConstruct\u003c/td\u003e\n          \u003ctd\u003eThis method is called at the end of the constructor.\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n          \u003ctd\u003eadopted\u003c/td\u003e\n          \u003ctd\u003eThis method is called when the element is adopted by another document.\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n          \u003ctd\u003econnected\u003c/td\u003e\n          \u003ctd\u003eThis method is called when a component is connected to the DOM.\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n          \u003ctd\u003ewillMount\u003c/td\u003e\n          \u003ctd\u003eThis method is called right before a component mounts.\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n          \u003ctd\u003edidMount\u003c/td\u003e\n          \u003ctd\u003eThis method is called after the component has mounted.\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n          \u003ctd\u003ewillReceiveAttributeCallback\u003c/td\u003e\n          \u003ctd\u003eThis method is called before a component does anything with an attribute.\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n          \u003ctd\u003edisconnected\u003c/td\u003e\n          \u003ctd\u003eThis method is called when a component is disconnected from the DOM.\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n          \u003ctd\u003edidUnmount\u003c/td\u003e\n          \u003ctd\u003eThis method is called after a component is removed from the DOM.\u003c/td\u003e\n        \u003c/tr\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n        \u003ctd rowspan=\"5\"\u003eForm-associated Custom Element related\u003c/td\u003e\n        \u003ctr\u003e\n          \u003ctd\u003eformAssociatedCallback\u003c/td\u003e\n          \u003ctd\u003eCalled when the browser associates the element with a form element or disassociates the element from a form element.\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n          \u003ctd\u003eformDisabledCallback\u003c/td\u003e\n          \u003ctd\u003eCalled after the disabled state of the element changes, either because the disabled attribute of this element was added or removed; or because the disabled state changed on a fieldset that's an ancestor of this element. The disabled parameter represents the new disabled state of the element. The element may disable elements in its shadow DOM when it is disabled.\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n          \u003ctd\u003eformResetCallback\u003c/td\u003e\n          \u003ctd\u003eCalled after the form is reset. The element should reset itself to some kind of default state. For input elements, this usually involves setting the value property to match the value attribute set in markup (or, in the case of a checkbox, setting the checked property to match the checked attribute).\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n          \u003ctd\u003eformStateRestoreCallback\u003c/td\u003e\n          \u003ctd\u003eCalled in one of two circumstances: when the browser restores the state of the element (for example, after a navigation, or when the browser restarts) or when the browser's input-assist features such as form autofilling sets a value. The type of the first argument depends on how the setFormValue() method was called.\u003c/td\u003e\n        \u003c/tr\u003e\n      \u003c/tr\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003eevents\u003c/td\u003e\n      \u003ctd colspan=\"3\"\u003eAllows you to define an \u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events\"\u003eevent\u003c/a\u003e to its parent and trigger it easily. It will be defined using lowercase. For example, countChanged will be registered as countchanged.\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003eshadow\u003c/td\u003e\n      \u003ctd colspan=\"3\"\u003eAllows you to add a Shadow DOM. By default, it uses open mode on Autonomous Custom elements and does not use Shadow DOM on Customized built-in elements. Only \u003ca href=\"https://dom.spec.whatwg.org/#dom-element-attachshadow\"\u003ethese elements\u003c/a\u003e are allowed to use Shadow DOM.\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003eformAssociated\u003c/td\u003e\n      \u003ctd colspan=\"3\"\u003eThis tells the browser to treat the element like a \u003ca href=\"https://web.dev/more-capable-form-controls/\"\u003eform control\u003c/a\u003e.\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd rowspan=\"3\"\u003eextends\u003c/td\u003e\n      \u003ctd rowspan=\"3\"\u003eAllows creating a \u003ca href =\"https://developers.google.com/web/fundamentals/web-components/customelements#extendhtml\"\u003eCustomized built-in element\u003c/a\u003e.\u003c/td\u003e\n      \u003ctr\u003e\n        \u003ctd\u003etag\u003c/td\u003e\n        \u003ctd colspan=\"3\"\u003eThe tag to extend.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n        \u003ctd\u003eclass\u003c/td\u003e\n        \u003ctd colspan=\"3\"\u003eThe class you want to extend.\u003c/td\u003e\n      \u003c/tr\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\nIf the extends field is not provided an [Autonomous custom element](https://developers.google.com/web/fundamentals/web-components/customelements#shadowdom) will be created.\n\n### Component Lifecycle\n\n```mermaid\nstateDiagram-v2\n    [*] --\u003e willConstruct\n    willConstruct --\u003e didConstruct\n    didConstruct --\u003e adopted: If the element was adopted by another document\n    adopted --\u003e connected\n    didConstruct --\u003e connected\n    connected --\u003e willMount: Only the first time\n    willMount --\u003e didMount\n    didMount --\u003e disconnected\n    disconnected --\u003e didUnmount\n    didUnmount --\u003e [*]\n    disconnected --\u003e connected: If element was moved\n    connected --\u003e disconnected\n    didUnmount --\u003e connected: If the element was cached\n\n    willConstruct --\u003e formAssociated: Only if formAssociated\n    formAssociated --\u003e didConstruct\n```\n\nCallbacks can be called at almost any point of the lifecycle\n\n## How This Works?\n### The problem with stores - the traditional approach\nLibraries traditional approach is usually based on stores.\n\n```mermaid\ngraph TD;\n    subgraph Store\n    A[\"Property A\"]\n    B[\"Property B\"]\n    C[\"Property C\"]\n    end\n    Store --\u003e D[\"Component D\"];\n    Store --\u003e E[\"Component E\"];\n    Store --\u003e F[\"Component F\"];\n    F --\u003e H[\"Component H\"];\n    F --\u003e I[\"Component I\"];\n    Store --\u003e G[\"Component G\"];\n```\n\nThis approach brings three major issues:\n- Any update on the store will trigger an update on a component, even if the property that changed in the store has no relation to the component. Every tag, attribute, etc., will need to be checked for changes in every re-render.\n- Any update on a component will trigger an update on the children, which might be unnecessary.\n- There is no way to set static properties in a dynamic environment. Take this React example:\n```tsx\nconst [value, setValue] = useState(0);\n\u003cinput type=\"number\" value={value} onChange={(e) =\u003e setValue(e.target.value)}/\u003e\n```\nIn this example, the value is updated every time the input changes, which, by definition, is incorrect. Why? Because \"value\" *\"Specifies the default value\"*. This means that the value does not need to be updated after the first render since it has no effect. *\"But React says that you can use defaultValue!\"* Yes, but it's not the standard way to do it and it's one of the most common mistakes most React developers make. All this is due to not using the platform.\n\nWith Michijs the solution is:\n```tsx\nconst value = useObserve(0);\n\n\u003cinput type=\"number\" value={value()} onchange={(e) =\u003e value(e.target.value)}/\u003e\n```\n\n### Observers / Signals\n Observers are a behavioral design pattern that defines a one-to-many dependency between objects. When the observable / subject undergoes a change in state, all its dependents (observers / subscribers) are notified and updated automatically with a signal.\n\n```mermaid\nsequenceDiagram\n    box rgb(71,73,73) Observable\n    participant Value\n    participant Proxy\n    end\n    Subscriber-\u003e\u003eProxy: Subscribes to\n    Environment-\u003e\u003eProxy: Request to change a value\n    Proxy--\u003e\u003eValue: Value is different?\n    Value--\u003e\u003eValue: Yes! Update\n    Value--\u003e\u003eProxy: Sends a clone of the value\n    Proxy-\u003e\u003eSubscriber: Notifies with a signal (new value)\n```\n\nThis approach allows for much more granular updates. Instead of updating an entire component, you can update HTML elements, attributes, or a simple text node and still maintain the principle of a single source of truth.\n\n```mermaid\ngraph TD;\n    subgraph Observable\n    A[\"Property A\"]\n    B[\"Property B\"]\n    C[\"Property C\"]\n    end\n    A --\u003e D[\"Attribute D\"];\n    A --\u003e E[\"Text node E\"];\n    B --\u003e F[\"Another observable F\"];\n    C --\u003e G[\"Text node G\"];\n```\nWhen a node is garbage collected, it will be unsubscribed in the next update.\n\n### Rendering - Static vs Dynamic\nTaking the above into account, the rendering process changes drastically. Instead of rendering the entire component with each change, **we render the component only once, and the changes are managed through the observables**.\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003eExample:\u003c/b\u003e\u003c/summary\u003e\n  \n```tsx\nimport { createCustomElement, useComputedObserve } from \"@michijs/michijs\";\n\ncreateCustomElement(\"test-component\", {\n  reflectedAttributes: {\n    valueA: 0,\n    valueB: 1,\n  },\n  methods: {\n    incrementValueB() {\n      this.valueB(this.valueB() + 1);\n    },\n  },\n  render() {\n    const sum = useComputedObserve(() =\u003e this.valueA() + this.valueB(), [this.valueA, this.valueB]);\n    return (\n      \u003c\u003e\n        \u003cbutton onpointerup={this.incrementValueB}\u003eIncrement B\u003c/button\u003e\n        {/* Renders 0, but is static */}\n        \u003cspan\u003e{this.valueA()}\u003c/span\u003e\n        {/* Renders 1, but is dynamic and will change when clicking on the button */}\n        \u003cspan\u003e{this.valueB}\u003c/span\u003e\n        {/* Renders 1, but is static */}\n        \u003cspan\u003e{this.valueA() + this.valueB()}\u003c/span\u003e\n        {/* Renders 1, but is dynamic and will change when clicking on the button */}\n        \u003cspan\u003e{sum}\u003c/span\u003e\n      \u003c/\u003e\n    );\n  },\n});\n```\n\n\u003c/details\u003e\n\n### Operators\nSince all observables are objects, operators work in a different way.\nWe support most operators without explicitly calling the getter of the observable.\n\n```tsx\n  const a = useObserve(0);\n  // Valid Javascript - Not valid Typescript\n  const b = a + 1;\n```\nThis is valid Javascript but is [not valid in Typescript yet](https://github.com/microsoft/TypeScript/issues/43826).\n\n```tsx\n  const a = useObserve(\"Hello\");\n  // Valid\n  const b = a + \" World\";\n```\n\nWe do not support boolean operators since proxies are objects:\n```tsx\n  const a = useObserve(false);\n  // Valid - Returns 2\n  const b = a() ? 1: 2;\n  // Valid but wrong usage - Returns 1 since \"a\" is an object and evaluates \"true\"\n  const b = a ? 1: 2;\n```\n\n## Hooks\nThere are several differences between our hooks and traditional ones:\n- Can be used in various contexts, including top-level script code, functional components, and custom hooks. This flexibility allows developers to encapsulate logic and state management using hooks in different parts of their application.\n- Most of them return observables.\n\nThe ability to use hooks outside of component code can be beneficial for managing application-wide state, setting up global side effects, or encapsulating reusable logic in utility functions or modules.  \nIt provides more flexibility in organizing code and separates concerns by allowing developers to centralize state management and side effects in hooks that can be reused across components or accessed from different parts of the application.\n\n### Basic hooks\n#### useObserve\nResponsible for observing changes on different types of values. Takes two arguments:\n- **item**: The value to be observed.\n- **isPrimitive**: If you want an unproxied version of useObserve. Similar to [tc39 signals proposal](https://github.com/tc39/proposal-signals)\n\nThis is the most basic hook and it is the basis of the entire component structure.\n\nIf the item contains a function, it will return an observable that observes for changes in the object itself. \n\n**A function in an observable should never mutate the observable.**\n\n#### usePureFunction\nIt is used to create a memoized function that encapsulates the result of the provided callback function and updates it only when any of the dependencies change. Takes two arguments:\n- **callback**: A function that returns a value of type T.\n- **deps**: An array of dependencies that the callback function depends on.\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003eExample:\u003c/b\u003e\u003c/summary\u003e\n\n```tsx\nimport { usePureFunction } from \"@michijs/michijs\";\n\nconst sum = usePureFunction((a, b) =\u003e a + b, [a, b]);\n\nconsole.log(sum(1, 2)); // Outputs 3\nconsole.log(sum(1, 2)); // Outputs 3 - without calling the callback - returning the cached value\n```\n\n\u003c/details\u003e\n\n#### useAsyncComputedObserve\nIt is used for computing a value and observing its changes. Takes four arguments:\n- **callback**: A function that returns a promise of type T.\n- **initialValue**: Initial value of type T.\n- **deps**: Dependencies to watch for changes.\n- **options**: Optional object that may contain `onBeforeUpdate` and `onAfterUpdate` callback functions. Also includes an option usePrimitive if you want an unproxied version of useAsyncComputedObserve. Similar to [tc39 signals proposal](https://github.com/tc39/proposal-signals)\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003eExample:\u003c/b\u003e\u003c/summary\u003e\n  \n```tsx\nimport { useAsyncComputedObserve } from \"@michijs/michijs\";\n\nconst fetchData = useAsyncComputedObserve(\n  async () =\u003e {\n    const response = await fetch(\"https://api.example.com/data\");\n    return response.json();\n  },\n  [], // Initial value\n  {\n    onBeforeUpdate: () =\u003e console.log(\"Fetching data...\"),\n    onAfterUpdate: () =\u003e console.log(\"Data fetched:\", fetchData()),\n  }\n);\n```\n\n\u003c/details\u003e\n\n#### useComputedObserve\nIt is used for computing a value and observing its changes. Takes three arguments:\n- **callback**: A function that returns a value of type T.\n- **deps**: Dependencies to watch for changes.\n- **options**: Optional object that may contain `onBeforeUpdate` and `onAfterUpdate` callback functions. Also includes an option usePrimitive if you want an unproxied version of useComputedObserve. Similar to [tc39 signals proposal](https://github.com/tc39/proposal-signals)\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003eExample:\u003c/b\u003e\u003c/summary\u003e\n  \n```tsx\nimport { useComputedObserve } from \"@michijs/michijs\";\n\nconst a = useObserve(2);\nconst b = useObserve(3);\n\nconst sum = useComputedObserve(() =\u003e a() + b(), [a, b], {\n  onBeforeUpdate: () =\u003e console.log(\"Calculating sum...\"),\n  onAfterUpdate: () =\u003e console.log(\"New sum:\", sum()),\n});\n\nconsole.log(sum()); // Outputs the computed sum\n```\n\n\u003c/details\u003e\n\n#### useStringTemplate\nIt is used to create a string template by interpolating dynamic values.\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003eExample:\u003c/b\u003e\u003c/summary\u003e\n  \n```tsx\n  const a = useObserve(3);\n  // Returns an observable with initial value 'Test 3' and subscribed to a\n  const b = useStringTemplate`Test ${a}`;\n```\n\n\u003c/details\u003e\n\n#### useWatch\nA simple mechanism for watching dependencies and invoking a callback when any of them change. Takes two parameters:\n- **callback**: A function that returns a value of type T. This is the function that will be invoked when any dependency changes.\n- **deps**: Optional array of dependencies to watch for changes.\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003eExample:\u003c/b\u003e\u003c/summary\u003e\n  \n```tsx\nimport { useObserve, useWatch } from \"@michijs/michijs\";\n\nconst count = useObserve(0);\n\nuseWatch(() =\u003e {\n  console.log(`Count has changed to: ${count()}`);\n}, [count]);\n\n// Simulating a change\ncount(1); // Outputs: Count has changed to: 1\n```\n\n\u003c/details\u003e\n\n#### useFetch\nFetches data from a URL, parses the response as JSON, and allows managing the result as an observable. Takes three parameters:\n- **callback**: A function that returns the request options.\n- **shouldWait**: An optional array of promises that should resolve before executing the fetch.\n- **options**: Additional options for the fetch operation.\n\nReturns: An object of type `FetchResult\u003cR\u003e`, which includes:\n- **promise**: An observable representing the fetch promise.\n- **recall()**: A method to call the promise again, available after the first call.\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003eExample:\u003c/b\u003e\u003c/summary\u003e\n\n```tsx\nimport { useFetch } from \"@michijs/michijs\";\n\nconst { promise, recall } = useFetch(async () =\u003e {\n  const token = tokenCookie.token();\n  const input = \"/some/endpoint\";\n  const searchParams = { query: \"example\" };\n  \n  return {\n    input: `https://api.github.com${input}`,\n    searchParams,\n    headers: {\n      Accept: \"application/vnd.github+json\",\n      Authorization: `${token!.type} ${token!.value}`,\n      \"X-GitHub-Api-Version\": \"2022-11-28\",\n    },\n  };\n}, [validationProps, ...(shouldWait ?? [])], {});\n\n// Example usage of the promise\npromise().then(data =\u003e {\n  console.log(data); // Outputs the fetched data\n}).catch(error =\u003e {\n  console.error(error);\n});\n\n// To call the promise again\nrecall();\n```\n\n\u003c/details\u003e\n\n#### usePromise\nUses a promise and allows managing the result as an observable. Takes two parameters:\n- **callback**: The operation that returns a promise.\n- **shouldWait**: An optional array of promises that should resolve before executing the promise.\n\nReturns: A `PromiseResult` object, which includes:\n- **promise**: An observable representing the promise.\n- **recall()**: A method to call the promise again, available after the first call.\n\n\u003c!-- \u003e [!TIP] --\u003e\n\u003e You can also use `doPromise` for an imperative alternative.\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003eExample:\u003c/b\u003e\u003c/summary\u003e\n  \n```tsx\nimport { usePromise } from \"@michijs/michijs\";\n\nconst { promise, recall } = usePromise(async () =\u003e {\n  const response = await fetch(\"https://api.github.com/users/octocat\");\n  return response.json();\n}, []);\n\npromise().then(user =\u003e {\n  console.log(user); // Outputs the user data\n});\n\n// To recall the promise later\nrecall();\n```\n\n\u003c/details\u003e\n\n### Route management hooks\n#### useHash\nThe `useHash` hook manages the hash portion of the URL, allowing you to observe and synchronize changes between the hash value and an observable state. This is particularly useful for single-page applications (SPAs) where routing is handled client-side. Parameters:\n  \nReturns: An observable with keys of type `T` and boolean values. \n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003eExample:\u003c/b\u003e\u003c/summary\u003e\n  \n```tsx\nimport { useHash } from \"@michijs/michijs\";\n\n// Using useHash to manage the hash state\nconst hashState = useHash\u003c'#drawerOpened'\u003e();\n\n// Opening a drawer\nhashState['#drawerOpened'](true);\n```\n\n\u003c/details\u003e\n\n#### useSearchParams\nFacilitates the management and observation of search parameters in the URL, providing a reactive way to handle changes and update the URL accordingly.\n\nReturns: An observable object containing the search parameters defined by the generic type `T`.\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003eExample:\u003c/b\u003e\u003c/summary\u003e\n  \n```tsx\nimport { useSearchParams } from \"@michijs/michijs\";\n\nconst searchParams = useSearchParams\u003c{\n    textParam: string;\n}\u003e();\n\n// To update the search parameters\nsearchParams.textParam(\"Hello\");\n```\n\n\u003c/details\u003e\n\n#### useTitle\nAllows to observe the document title. Do not use document.title use this hook instead\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003eExample:\u003c/b\u003e\u003c/summary\u003e\n  \n```tsx\nimport { useTitle } from \"@michijs/michijs\";\n\nconst title = useTitle();\n\ntitle('test')\n```\n\n\u003c/details\u003e\n\n### Storage hooks\n#### useStorage\nAllows for observing changes in an object and synchronizing it with the browser's storage (such as localStorage). Takes two parameters:\n- **item**: The object to be observed and synchronized with storage.\n- **storage**: (Optional) The storage object to be used, defaults to `localStorage`.\n\n\u003c!-- \u003e [!TIP] --\u003e\n\u003e If you want to use cookies we provide a class that acts like an storage called CookieStorage\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003eExample:\u003c/b\u003e\u003c/summary\u003e\n  \n```tsx\nconst { lang } = useStorage({\n  // Default value\n  lang: navigator.language,\n});\n```\n\n\u003c/details\u003e\n\n#### useIndexedDB\nIt sets up event listeners for changes in the IndexedDB database. It returns a Proxy object that intercepts property accesses and performs corresponding IndexedDB operations. IndexedDB operations are performed asynchronously and return Promises. Takes three arguments:\n- **name**: Specifies the name of the IndexedDB database to be used or created.\n- **objectsStore**: Is a generic type that describes the structure of the object stores. It's defined as an object where each key represents the name of a property in the stored objects, and the value represents the configuration options for that property.\n- **version**: (Optional) specifies the version number of the IndexedDB database. If the database with the specified name already exists and its version is lower than the provided version, it will perform any necessary upgrades.\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003eExample:\u003c/b\u003e\u003c/summary\u003e\n  \n```tsx\nconst storedCount = useIndexedDB\u003c{\n  counter: {\n    count: number;\n    id: number;\n  };\n}\u003e(\"counter\", {\n  counter: {\n    keyPath: \"id\",\n  },\n});\n\nconst count = useAsyncComputedObserve(\n  async () =\u003e {\n    return (await storedCount.counter.get(1))?.count ?? 0;\n  },\n  (await storedCount.counter.get(1))?.count ?? 0,\n  [storedCount],\n);\n\nfunction decrementCount() {\n  storedCount.counter.put({ count: count() - 1, id: 1 });\n}\nfunction incrementCount() {\n  storedCount.counter.put({ count: count() + 1, id: 1 });\n}\n```\n\n\u003c/details\u003e\n\n### CSS hooks\nTo use css we provide functions to create Constructable Stylesheets.\n__Our stylesheets can also subscribe to observables.__\n\n#### useStyleSheet\nAllows to create a Constructable Stylesheet with a CSSObject.\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003eExample:\u003c/b\u003e\u003c/summary\u003e\n  \n```tsx\nexport const counterStyle = useStyleSheet({\n  ':host': {\n    display: 'flex',\n    flexDirection: 'row'\n  },\n  span: {\n    minWidth: '60px',\n    textAlign: 'center'\n  }\n});\n```\n\n\u003c/details\u003e\n\n#### css\nAllows to create a Constructable Stylesheet with a Template String.\n[Recomended extension for VSCode](https://marketplace.visualstudio.com/items?itemName=paulmolluzzo.convert-css-in-js).\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003eExample:\u003c/b\u003e\u003c/summary\u003e\n  \n```tsx\nexport const counterStyle = css`\n  :host {\n      display: flex;\n      flex-direction: row;\n  }\n\n  span {\n      min-width: 60px;\n      text-align: center;\n  }\n`\n```\n\n\u003c/details\u003e\n\n#### useAnimation\nGenerates CSS keyframes and animation properties based on the provided keyframes and options.\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003eExample:\u003c/b\u003e\u003c/summary\u003e\n  \n```tsx\nconst hiddenState = {\n  opacity: 0,\n} satisfies CSSProperties;\nconst shownState = {\n  opacity: 1,\n} satisfies CSSProperties;\n\nconst [hideKeyframe, hideProperties] = useAnimation([shownState, hiddenState], {\n  duration: '2s',\n  fill: 'forwards'\n});\nconst [showKeyframe, showProperties] = useAnimation([hiddenState, shownState], {\n  duration: '1s',\n  fill: 'forwards'\n});\n\nexport const dialogStyle = useStyleSheet((tag) =\u003e ({\n  ...showKeyframe,\n  ...hideKeyframe,\n  [tag]: {\n    ...hideProperties,\n    display: 'flex',\n    flexDirection: 'row',\n    '[open]': showProperties\n  },\n}));\n```\n\n\u003c/details\u003e\n\n#### useTransition\nHook to generate CSS transition properties based on the provided configuration.\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003eExample:\u003c/b\u003e\u003c/summary\u003e\n  \n```tsx\nconst opacityTransition = useTransition({\n  property: [\"opacity\"],\n  duration: \"1s\",\n});\n\nexport const dialogStyle = useStyleSheet((tag) =\u003e ({\n  [tag]: {\n    ...opacityTransition,\n    display: 'flex',\n    flexDirection: 'row',\n    opacity: 0,\n    '[open]': {\n      opacity: 1\n    }\n  },\n}));\n```\n\n\u003c/details\u003e\n\n#### CSS module scripts\nWe do not provide support for this functionality yet as ESBuild does not support it yet. You can read how it works [here](https://web.dev/css-module-scripts/)\n\n## Components\n\n### Title\nTitle component for dynamically updating the document's title.\n\n### Redirect\nRedirect component for navigating to a different URL or location.\n\n### List\nA generic list rendering component that supports both static arrays and observable arrays.\n\nIf the `data` is an observable array, the component delegates rendering to the array's internal `.List` method. Otherwise, it directly maps through the array and renders each item.\n\n### Host\nAllows to set attributes and event listeners to the host element itself.\n\n### Fragment\nCreates a virtual node that wrapps elements\n\n### ElementInternals\n*(Only available if formAssociated is true)*\n\nIt allows to:\n- Make the element accessible to the browser\n- Access element internals \n- Validate and assign values to forms\n\n### AsyncComponent\nAsynchronously renders a component after the promise ends. In the meantime you can choose to show a load component or not show anything.\n\n### Slot\nChecks if the context element has a shadow root and renders either a standard \u003cslot\u003e or a MichiSlot custom element, passing along attributes and children.\n\nWhen nodes are added, it checks if they have a slot attribute matching the slot's name or if no name is set, appending them to the MichiSlot and triggering a slotchange event. \n\nThe main difference between the standard slot aned the MichiSlot is that the parent does not have a shadow DOM so **every** child appended to the parent is moved to the slot.\n\n## Custom element methods\n### child\nAllows to get a child element from the host with the selector\n\n### idGen\nCreate unique IDs with a discernible key\n\n## Attributes vs Properties in jsx\nUsually, if you want to get an HTML like this:\n```html\n\u003cdiv class='test'\u003e\u003c/div\u003e\n```\nIn React / Stencil / etc you should write a jsx like this:\n```tsx\n() =\u003e \u003cdiv className='test'\u003e\u003c/div\u003e\n```\nAnd eventually code like this would be executed:\n```tsx\nconst el = document.createElement('div');\nel.className = 'test';\n```\nIn MichiJS you have the freedom to use both attributes and properties and the result will be the same:\n```tsx\n// Using properties\n() =\u003e \u003cdiv _={{className: 'test'}}\u003e\u003c/div\u003e\n// Using attributes\n() =\u003e \u003cdiv class='test'\u003e\u003c/div\u003e\n```\nAnd eventually code like this would be executed:\n```tsx\nconst el = document.createElement('div');\n// Using properties\nel.className = 'test';\n// Using attributes\nel.setAttribute('class', 'test')\n```\nIn this way the jsx syntax of MichiJS is more similar to HTML.\n\n## If - Conditional Rendering\nThe If utility provides a declarative, unified, and reactive way to handle conditional rendering across CSS and JSX. It’s especially useful in reactive UI frameworks or libraries where you want fine-grained control over conditional logic. This is the only way to do it dynamically.\n\n### Features\n- Dual mode support: JSX rendering and CSS conditional string generation\n- Reactivity via observables\n- Optional DOM fragment caching\n\n### CSS mode\nIt is designed to work like the new CSS if function:\n```css\n  background: if(\n    style(--theme:dark): #000;\n    style(--theme:system): var(--system-color);\n    else: #fff)\n```\n\nThe same code can be translated like\n```tsx\n  const cssVariables = useCssVariables\u003c{ theme: string, systemColor: string }\u003e();\n\n  const style = useStyleSheet({\n    background: If(cssVariables.theme, [\n      [\"dark\", \"#000\"],\n      [\"system\", cssVariables.systemColor()],\n    ], \"#fff\");\n  })\n```\n\n### JS mode\nIn jsx you can use it the same way\n```tsx\n  \u003cdiv\u003e\n    {If(\n      someObservable, [                           // condition\n        [true, \u003cdiv\u003eVisible\u003c/div\u003e]\n      ], \n      \u003cdiv\u003eHidden\u003c/div\u003e,                          // elseValue\n      {\n        as: \"section\",                            // Optional wrapper element\n        enableCache: true,                        // Optional caching\n        attrs: { class: \"wrapper\" }               // Optional props for the wrapper\n      }\n    )}\n  \u003c/div\u003e\n  // Or\n  \u003cdiv\u003e\n    {If(\n      someObservable, \u003cdiv\u003eVisible\u003c/div\u003e, \n      \u003cdiv\u003eHidden\u003c/div\u003e,                          // elseValue\n      {\n        as: \"section\",                            // Optional wrapper element\n        enableCache: true,                        // Optional caching\n        attrs: { class: \"wrapper\" }               // Optional props for the wrapper\n      }\n    )}\n  \u003c/div\u003e\n```\n\n## Lists\nThere are 2 ways to create a list\n### The static way - Using map\nIt's the way to create static lists from an array object. Since the result will be static, it will reflect the state of a variable when it is rendered. Useful for read-only lists.\n```tsx\nconst arrayTest = [0, 1, 2];\n\narrayTest.map(item =\u003e \u003cdiv\u003e{item}\u003c/div\u003e)\n```\nThis will generate an element like:\n\n```html\n  \u003cdiv\u003e0\u003c/div\u003e\n  \u003cdiv\u003e1\u003c/div\u003e\n  \u003cdiv\u003e2\u003c/div\u003e\n```\n\n### The dynamic way - Using List component\nIt is a component that avoids using dom diff algorithms to render dynamic lists. This allows it to have a performance close to vanilla js. Operations on the array trigger corresponding changes in the DOM elements, making it ideal for dynamic lists.\n```tsx\nconst arrayTest = useObserve([0, 1, 2]);\n\n\u003carrayTest.List \n  as=\"span\"\n  renderItem={item =\u003e \u003cdiv\u003e{item}\u003c/div\u003e}\n/\u003e\n```\nThis will generate an element like:\n\n```html\n\u003cspan\u003e\n  \u003cdiv\u003e0\u003c/div\u003e\n  \u003cdiv\u003e1\u003c/div\u003e\n  \u003cdiv\u003e2\u003c/div\u003e\n\u003c/span\u003e\n```\n\n### Comparison\n\u003ctable\u003e\n  \u003cthead\u003e\n    \u003ctr\u003e\n      \u003cth\u003e\u003c/th\u003e\n      \u003cth\u003eMap\u003c/th\u003e\n      \u003cth\u003eList component\u003c/th\u003e\n    \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003ctd\u003ePerformance\u003c/td\u003e\n      \u003ctd colspan=\"2\"\u003eClose to vanilla\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003eContainer\u003c/td\u003e\n      \u003ctd colspan=\"2\"\u003eVirtual fragment or any other element\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003eData\u003c/td\u003e\n      \u003ctd\u003eStatic\u003c/td\u003e\n      \u003ctd\u003eDynamic\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003eUpdates\u003c/td\u003e\n      \u003ctd\u003eNo\u003c/td\u003e\n      \u003ctd\u003eOnly when an operator is triggered on the list or its elements\u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\n## Routing\nThe custom routing tool avoids using strings to represent URLs and instead utilizes modern APIs like the `URL` object. It also allows separating route components, promoting cleaner code.\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003eExample:\u003c/b\u003e\u003c/summary\u003e\n  \n```tsx\n//Parent routes\nexport const [urls, Router] = registerRoutes({\n  syncRoute: \u003cdiv\u003eHello World\u003c/div\u003e,\n  //Redirect route\n  '/': \u003cRedirect to={url} /\u003e\n});\n\n//Child routes\nexport const [urlsChild, RouterChild] = registerRoutes({\n  // Async route\n  asyncChildRoute: (\n    \u003cAsyncComponent\n      promise={async () =\u003e (await import('./AsyncChildExample')).AsyncChildExample}\n      loadingComponent={\u003cspan\u003eloading...\u003c/span\u003e}\n    /\u003e\n  ),\n  //The parent route\n}, urls.syncRoute);\n\n// Will generate this url: /sync-route/async-child-route?searchParam1=param+1\u0026searchParam2=2#hash1\nconst generatedUrl = urlsChild.asyncChildRoute({ \n  searchParams: { \n    searchParam1: 'param 1', \n    searchParam2: 2\n  }, \n  hash: '#hash1' \n})\n```\nRouter and RouterChild are components representing the mount points for each registered route.\n\n```tsx\nconst AsyncChildExample: FC = () =\u003e {\n    const searchParams = useSearchParams\u003c{\n      searchParam1: string, \n      searchParam2: number\n    }\u003e();\n    const hash = useHash\u003c'#hash1'| '#hash2'\u003e();\n    return (\n      \u003c\u003e\n        {/* Will show the value of searchParam1 */}\n        \u003cdiv\u003e{searchParams.searchParam1}\u003c/div\u003e\n        {/* Will show true if the hash is #hash1 */}\n        \u003cdiv\u003e{hash['#hash1']}\u003c/div\u003e\n      \u003c/\u003e\n    );\n}\n\nexport default AsyncChildExample\n```\n\n\u003c/details\u003e\n\n## I18n\nInternationalization (i18n) is supported through observables. By default, the desired languages are inferred from the browser settings. If your code supports an exact match (e.g., \"en-UK\") or a general match (e.g., \"en\"), that language will be selected. Otherwise, it falls back to the default language, which is the first one in the list. The default language cannot be obtained asynchronously.\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003eExample:\u003c/b\u003e\u003c/summary\u003e\n  \n```tsx\n\nconst { lang } = useStorage({\n  lang: navigator.language,\n});\n\nconst translator = new I18n([\"en-uk\", \"es\"], lang);\n\nconst t = translator.createTranslation({\n  \"en-uk\": {\n    dogBit: \"The dog bit its owner\",\n    birthDay: (date: Date) =\u003e `My birthday is ${date.toLocaleDateString('en-uk')}`,\n  },\n  es: () =\u003e import(\"./translations/es.json\"),\n});\n\nexport const MyComponent = createCustomElement('my-component', {\n  render() {\n    return (\n      \u003c\u003e\n        \u003cp\u003e{t.dogBit}\u003c/p\u003e\n        \u003cp\u003e{t.birthDay(new Date(1997, 20, 2))}\u003c/p\u003e\n      \u003c/\u003e\n    );\n  }\n});\n```\n\n\u003c/details\u003e\n\n## Limitations\n### Observable objects\nBecause some objects are not proxy compatible we limit the observable objects to:\n- Arrays\n- Dates\n- Maps\n- Sets\n- Any object whose prototype is Object\n\nHowever, we still support assignments to such complex objects in that case you will have to cast those ones with ObservableComplexObject.\n\n```tsx\n  const observable = useObserve({\n    file: new File([''], 'test') as unknown as ObservableComplexObject\u003cFile\u003e\n  })\n```\n\nThis is because Typescript doesnt provide any tool to know if a type is part of the global namespace.\n\n## Polyfills\nIf you REALLY need polyfills i recommend you to read this topics:\n\n- https://www.webcomponents.org/polyfills\n- https://ungap.github.io\n\n### Built-in elements in Safari\nWe provide partial support for Safari's built-in elements by emulating their behavior with a custom element, michi-generic-element. This is necessary to manage the element's lifecycle and support adoptedStyleSheets.\n\n## Browser Support\n- **Customized built-in elements**: [Chrome feature status](https://www.chromestatus.com/feature/4670146924773376)\n- **Autonomous custom elements**: [Chrome feature status](https://www.chromestatus.com/feature/4696261944934400) [WebComponents.org](https://www.webcomponents.org/)\n- **Framework Compatibility**: [Custom Elements Everywhere](https://custom-elements-everywhere.com)\n- **Element internals**: [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/attachInternals)\n\n## Supporting MichiJS\n### Sponsors\nSupport us with a donation and help us continue our activities [here](https://www.paypal.com/paypalme/lsegurado2).\n\u003c!-- ### Contributors\nTODO:\n\u003ca href=\"https://github.com/@lsegurado/ls-element/graphs/contributors\"\u003e\n  \u003cimg src=\"https://opencollective.com/ls-element/contributors.svg?width=890\u0026amp;button=false\" style=\"max-width:100%;\"\u003e\n\u003c/a\u003e --\u003e\n\n\n\u003c!-- ### Open Collective\n\nSupport us with a donation and help us continue our activities. [[Contribute](https://opencollective.com/ls-element)]\n\n### Sponsors\n\nBecome a sponsor and get your logo on our README on GitHub with a link to your site. [[Become a sponsor](https://opencollective.com/ls-element#sponsor)] --\u003e\n\n## License\n - [MIT](https://github.com/michijs/michijs/blob/master/LICENSE.md)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmichijs%2Fmichijs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmichijs%2Fmichijs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmichijs%2Fmichijs/lists"}