{"id":21025101,"url":"https://github.com/krutsch/hydro-js","last_synced_at":"2025-05-15T08:33:38.129Z","repository":{"id":37860136,"uuid":"312894683","full_name":"Krutsch/hydro-js","owner":"Krutsch","description":"A lightweight (5K compressed) reactive UI library via template literal tags.","archived":false,"fork":false,"pushed_at":"2025-05-01T12:06:09.000Z","size":1368,"stargazers_count":14,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-05-12T04:12:17.945Z","etag":null,"topics":["dom","hydro-js","javascript","library","reactive"],"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/Krutsch.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,"zenodo":null}},"created_at":"2020-11-14T20:16:27.000Z","updated_at":"2025-05-01T12:06:10.000Z","dependencies_parsed_at":"2023-10-20T15:37:24.011Z","dependency_job_id":"0cb0f739-5d8c-4748-a28f-b7624fd1fc1b","html_url":"https://github.com/Krutsch/hydro-js","commit_stats":{"total_commits":238,"total_committers":5,"mean_commits":47.6,"dds":0.6218487394957983,"last_synced_commit":"fdc952153fe13aa5e5b4fb110c7ea8deaeb99501"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Krutsch%2Fhydro-js","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Krutsch%2Fhydro-js/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Krutsch%2Fhydro-js/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Krutsch%2Fhydro-js/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Krutsch","download_url":"https://codeload.github.com/Krutsch/hydro-js/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254304837,"owners_count":22048480,"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":["dom","hydro-js","javascript","library","reactive"],"created_at":"2024-11-19T11:30:10.624Z","updated_at":"2025-05-15T08:33:38.096Z","avatar_url":"https://github.com/Krutsch.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cimg align=\"right\" alt=\"100% Coverage\" src=\"coverage.svg\"\u003e\n\n# hydro-js\n\n\u003e A lightweight (below 5K \u003cem\u003ecompressed\u003c/em\u003e) reactive UI library via template literal tags.\u003cbr\u003e Support in all modern Browsers.\n\n## Installation\n\nTo bootstrap a new app:\n\n```properties\n$ npm init hydro-app@latest \u003cproject\u003e // or npx create-hydro-app@latest \u003cproject\u003e\n```\n\nor integrate in an existing app:\n\n```properties\n$ npm install hydro-js\nimport { render, html } from 'hydro-js';\n```\n\nAlternatively you can use a CDN:\n\n```html\n\u003cscript type=\"module\"\u003e\n  import { render, html } from \"https://unpkg.com/hydro-js\";\n\u003c/script\u003e\n```\n\n## Examples\n\n- [Simple Counter](https://stackblitz.com/edit/hydro-simple-counter)\n- [Reactive CSS](https://stackblitz.com/edit/hydro-reactive-styles)\n- [Two Way Data Binding](https://stackblitz.com/edit/hydro-two-way-data)\n- [Show](https://stackblitz.com/edit/hydro-show-ternary)\n- [Destructure Attributes](https://stackblitz.com/edit/hydro-destructure-attributes)\n- [Ternary](https://stackblitz.com/edit/hydro-ternary)\n- [Promise Handling](https://stackblitz.com/edit/hydro-promise)\n- [Nested Reactivity](https://stackblitz.com/edit/hydro-nested-reactivity)\n- [Nested Reactivity 2](https://stackblitz.com/edit/hydro-nested-reactivity-2)\n\n## Concept\n\nThere are multiple things this library can do. The first thing is generating HTML from strings. This is mostly done by the `Range Web API`. There are already ways to do that, like `Element.insertAdjacentHTML()`, but this has some drawbacks, as it does not create Table Elements, like `\u003ccolgroup\u003e`, `\u003ctbody\u003e`, `\u003ctfoot\u003e`, `\u003cthead\u003e` and `\u003ctr\u003e`. Furthermore, the html function deals with inline events, objects, Handlebars / {{ Mustache }} etc. Using this function will feel like writing JSX without a build step.\n\nThe render function is used for mounting and unmounting Elements to/from the DOM and for executing lifecycle hooks. Optionally, it can diff both HTML Trees and reuse Elements (optionally too). This is not using a virtual DOM.\n\nThe functions calls for `render` and \u003cem\u003eDOM Updates\u003c/em\u003e are queued and worked on during a browser's idle periods.\n\nIn order to make the DOM reactive, `ES6 Proxy` objects are being used to map data against an array of DOM Elements. Whenever the \u003cem\u003esetter\u003c/em\u003e is being called, the Proxy will check the mapping and update the DOM granularly. No re-renders are needed!\n\nAlmost all intern maps are using `WeakMap` with DOM Elements or Proxy objects as keys and thus memory is cleared efficiently.\n\n## Documentation\n\n### html\n\nargs: `string`\u003cbr\u003e\nreturns: `DocumentFragment | Element | Text`\n\nTakes a string and transforms it to HTML. Used for internal bookkeeping too.\n\n#### Example\n\n```js\nhtml`\u003cp\u003eText\u003c/p\u003e`;\n```\n\n### render\n\nargs:\n\n- new Element (`ReturnType\u003ctypeof html\u003e | reactive Proxy`)\n- old Element (`ReturnType\u003ctypeof html\u003e | string`)\n- shouldSchedule?: `boolean` (default: true)\n\nreturns: `function` that unmounts the new Element\n\nAccepts the return value of `html` and replaces it with \u003cem\u003eold Element\u003c/em\u003e. If it is a string, it will be resolved with `querySelector`. If there is no second parameter, the Element will be appended to the `body`.\n\n#### Example\n\n```js\nrender(html`\u003cp\u003eText\u003c/p\u003e`);\n```\n\n### setGlobalSchedule\n\nargs: `boolean`\u003cbr\u003e\n\nWill enable/disable the schedule logic for `render` and \u003cem\u003eDOM Updates\u003c/em\u003e. Intern value defaults to `true`.\n\n### setReuseElements\n\nargs: `boolean`\u003cbr\u003e\n\nWill enable/disable the reuse of Elements in the diffing phase of `render`. Intern value defaults to `true`.\n\n### setInsertDiffing\n\nargs: `boolean`\u003cbr\u003e\n\nIf enabled, it will insert the new DOM Tree to the DOM before diffing. This will asssure that reused Elements will not lose their state (e.g. `\u003cvideo\u003e` in \u003cem\u003eChrome\u003c/em\u003e. Intern value defaults to `false`.\n\n### setReactivity\n\nargs: `Node`\u003cbr\u003e\n\nInserts Proxy values in the template HTML. This is useful, when HTML already exists, i.e. in a HTML file and you want to set the hydro Proxy Objects for the handlebars. Also, this can set event listener and remove the inline listener. This is a good way to send HTML over the wire.\n\n#### Example 1\n\n```js\n// \u003cp id=\"value\"\u003e{{value}}\u003c/p\u003e in HTML\nconst template = $(\"#value\");\nhydro.value = \"Hello World\";\nsetReactivity(template);\n```\n\n#### Example 2 (with event)\n\n```js\n// \u003cp id=\"value\" onclick=\"placeholder\"\u003e{{value}}\u003c/p\u003e in HTML\nconst template = $(\"#value\")!;\nhydro.value = \"Hello World\";\nsetReactivity(template, { placeholder: () =\u003e console.log(\"clicked\") }); // placeholder should be unique\n```\n\n### onRender\n\nargs:\n\n- `function`\n- elem (`ReturnType\u003ctypeof html\u003e`)\n- ...args for passed `function`\n\nCalls the passed in `function` with `...args`, after the Element is being inserted by `render`;\n\n#### Example\n\n```js\nconst elem = html`\u003cp\u003eHello World\u003c/p\u003e`;\nonRender(() =\u003e console.log(\"rendered elem\"), elem);\nrender(elem);\n```\n\n### onCleanup\n\nargs:\n\n- `function`\n- elem (`ReturnType\u003ctypeof html\u003e`)\n- ...args for passed `function`\n\nCalls the passed in `function` with `...args`, after the Element is being diffed out by `render` or removed by `unmount`;\n\n#### Example\n\n```js\nconst elem = html`\u003cp\u003eHello World\u003c/p\u003e`;\nonCleanup(() =\u003e console.log(\"removed elem\"), elem);\nconst unmount = render(elem);\nunmount();\n```\n\n### reactive\n\nargs: value: `any`\u003cbr\u003e\nreturns: unique `Proxy`\n\nReturns a Proxy object that can be used within `html`. The Proxy is wrapping a function that can set the value. There are two ways to call the function (see `Nested Reactivity 2`. If the Proxy will be called with a function, then the argument of the passed in function will be provided as the current value for the Proxy, otherwise it will take the new argument as new value.\nThe actual value will be set on the hydro Proxy.\n\u003cbr\u003e\u003cem\u003e Special behaviour for (prev) functions: the old value will be kept, if the returned value is undefined.\u003c/em\u003e\n\n#### Example\n\n```js\nconst data = reactive({ value: 42 });\nrender(html`\u003cp\u003e${data.value} €\u003c/p\u003e`);\ndata((prev) =\u003e (prev.value = 21)); // Change the value\n```\n\n### observe\n\nargs:\n\n- `ReturnType\u003ctypeof reactive\u003e`\u003cbr\u003e\n- `function`\n\nCalls the function whenever the value of reactive Proxy changes. This is only one layer deep but chaining properties on reactive Proxys will return a Proxy too. Observing a prop of an object will look like:\n\n```js\nobserve(person.name, ...)\n```\n\n#### Example\n\n```js\nconst person = reactive({ name: \"Steve\" });\nobserve(person.name, (newValue) =\u003e console.log(`Name changed to ${newValue}`));\nperson.name.setter(\"Definitely not Steve\"); // Change the value\n```\n\n### unobserve\n\nargs:\n\n- `ReturnType\u003ctypeof reactive\u003e`\n\nRemoves all observers from the reactive Proxy. This will not be called recursively for properties.\n\n### watchEffect\n\nargs: `function`\nreturns: a stop `function`\n\nThis works similarly to Vue3 watchEffect:\nTo apply and automatically re-apply a side effect based on reactive state, we can use the watchEffect method. It runs a function immediately while reactively tracking its dependencies and re-runs it whenever the dependencies are changed.\n\n#### Example\n\n```js\nconst count = reactive(0);\nwatchEffect(() =\u003e console.log(getValue(count)));\n// -\u003e logs 0\n\ncount(1);\n// -\u003e logs 1\n```\n\n### getValue\n\nargs: `ReturnType\u003ctypeof reactive\u003e`\u003cbr\u003e\nreturns: currently set value\n\nReturns the value inside the the Proxy. getValue is needed because a reactive Proxy does not have access to the value.\n\n#### Example\n\n```js\nconst person = reactive({ name: \"Steve\" });\nconsole.log(getValue(person.name)); // Get curent name\n```\n\n### setAsyncUpdate\n\nargs:\n\n- `ReturnType\u003ctypeof reactive\u003e`\u003cbr\u003e\n- `boolean`\n\nSets the schedule behavior for DOM Updates that are connected to this Proxy. This will not be called recursively for properties.\n\n### unset\n\nargs: ReturnType\u003ctypeof reactive\u003e\n\nDeletes the Proxy object and removes all observers (both recursively). This is important for keeping memory low. This happens by setting the value to `null`.\n\n### ternary\n\nargs:\n\n- condition: `function | ReturnType\u003ctypeof reactive\u003e`\n- trueVal: `any`\n- falseVal: `any`\n- proxy?: `ReturnType\u003ctypeof reactive\u003e`\n\nreturns: `ReturnType\u003ctypeof reactive\u003e`\n\nIn order to track a ternary in a template literal, this function has to be used. The proxy parameter (4th) is optional, if the first parameter is a reactive Proxy. Otherwise, the condition function is being executed, whenever the Proxy value changes, which will update the DOM to either the trueVal or the falseVal, depening on the return value. If trueVal is a function, then it will be executed. The same applies for falseVal.\n\n#### Example\n\n```js\nconst toggleValue = reactive(true);\nrender(html` \u003cbutton\u003e${ternary(toggleValue, \"ON\", \"OFF\")}\u003c/button\u003e `);\nsetTimeout(() =\u003e toggleValue(false), 1e3); // Will re-validate the ternary after 1s\n```\n\n### hydro\n\nThe actual Proxy in the library. This cannot be used with `getValue`, `observe`, `ternary` or `unset` but it offers the same functionality in a different manner.\n\u003cbr\u003e\u003cem\u003e Special behaviour for promises: the library will await promises and will set its value to the unwrapped value. If the Promise rejects, the value will be unset.\u003c/em\u003e\n\u003cbr\u003e\u003cem\u003e Special behaviour for null: null will delete all properties and observer for a value\u003c/em\u003e\n\nproperties:\u003cbr\u003e\n\n- isProxy: `boolean` (default: true)\u003cbr\u003e\n- asyncUpdate: `boolean`, (default: true, derived from globalSchedule)\u003cbr\u003e\n- observe: `function`, args: `string` as key, fn: `function`\u003cbr\u003e\n- unobserve: `function`, args: `string | undefined` - unobserve key or all, , fn: `function`\u003cbr\u003e\n- getObservers: `function`, returns: map with all observers\n\n#### Example\n\n```js\nhydro.fruit = \"Banana\";\nrender(html`\u003cspan\u003e{{ fruit }}\u003c/span\u003e`);\n```\n\n### view\n\nRender the elements whenever the data changes. It will handle the operation for deletion, addition, swapping etc. This defaults to a non-keyed solution but it can be changed by calling `setReuseElements` with false.\n\nargs:\n\n- root: `string` (CSS selector)\u003cbr\u003e\n- data: `ReturnType\u003ctypeof reactive\u003e`\u003cbr\u003e\n- renderFunction: `function`, args: item: `any`, i: `number`\u003cbr\u003e\n\n#### Example\n\n```js\nconst data = reactive([{ id: 4, label: \"Red Onions\" }])\nview('.table', data, (item, i) =\u003e \u003ctr\u003eReactive: {data[i].id}, Non-reactive: {item.id}\u003c/tr\u003e)\n```\n\n### emit\n\nargs:\n\n- event: `string`\n- data: `any`\n- who: `EventTarget`\n- options: `object` (default: `{ bubbles: true }`)\n\nEmits an event from the EventTarget \u003cem\u003ewho\u003c/em\u003e. This event bubbles by default.\n\n#### Example 1\n\n```js\nrender(\n  html`\u003cdiv onfav=${({ detail: cake }) =\u003e console.log(cake)}\u003e\n    \u003cp onclick=${({ target }) =\u003e emit(\"fav\", \"Cheesecake\", target)}\u003e\n      Click to emit your favorite cake 🍰\n    \u003c/p\u003e\n  \u003c/div\u003e`\n);\n```\n\n#### Example 2\n\n```js\n// With event options\nrender(\n  html`\u003cdiv onfav=${({ detail: cake }) =\u003e console.log(cake)}\u003e\n    \u003cp\n      onclick=${{\n        options: {\n          once: true,\n        },\n        event: ({ target }) =\u003e emit(\"fav\", \"Strawberry Cake\", target),\n      }}\n    \u003e\n      Click to emit your favorite cake 🍰\n    \u003c/p\u003e\n  \u003c/div\u003e`\n);\n```\n\n### \\$\n\nShortcut for `querySelector`.\n\n### \\$\\$\n\nShortcut for `querySelectorAll`.\n\n### internals\n\nAn object with internal data / functions for testing or deeper dives for developers. This only includes a `compare` function for DOM Elements at the moment.\n\n### Attributes\n\n- bind: binds a piece of data to an element. This is only useful, when an element should be removed from the DOM, when the data is being set to null.\n\n#### Example\n\n```js\nconst data = reactive({ name: \"Pet\" });\nrender(html`\u003cp bind=${data}\u003e${data.name}\u003c/p\u003e`);\nsetTimeout(() =\u003e unset(data), 1000); // will remove the element\n```\n\n## Further\n\nTo enable HTML highlighting in your files, you could use [leet-html](https://marketplace.visualstudio.com/items?itemName=EldarGerfanov.leet-html) in VS Code.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkrutsch%2Fhydro-js","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkrutsch%2Fhydro-js","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkrutsch%2Fhydro-js/lists"}