{"id":20973523,"url":"https://github.com/brisa-build/diff-dom-streaming","last_synced_at":"2025-04-06T03:06:47.667Z","repository":{"id":231568738,"uuid":"781032708","full_name":"brisa-build/diff-dom-streaming","owner":"brisa-build","description":"HTML Streaming Over the Wire! 🥳  Diff DOM algorithm with streaming to make only the necessary modifications, insertions and deletions between a DOM node and an HTML stream reader.","archived":false,"fork":false,"pushed_at":"2025-02-03T23:20:47.000Z","size":99,"stargazers_count":129,"open_issues_count":1,"forks_count":1,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-04-06T02:42:05.711Z","etag":null,"topics":["diff","dom","dom-manipulation","html-streaming","javascript","typescript","virtual-dom"],"latest_commit_sha":null,"homepage":"https://stackblitz.com/edit/diff-dom-streaming?file=index.js","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/brisa-build.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"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}},"created_at":"2024-04-02T16:14:35.000Z","updated_at":"2025-03-16T14:51:34.000Z","dependencies_parsed_at":"2024-04-04T17:32:14.077Z","dependency_job_id":"78138cda-83f6-42e4-802c-694fa2a6050c","html_url":"https://github.com/brisa-build/diff-dom-streaming","commit_stats":{"total_commits":59,"total_committers":1,"mean_commits":59.0,"dds":0.0,"last_synced_commit":"cac8ca18ea4312fe91b7155f95c65fb06b5c5366"},"previous_names":["aralroca/diff-dom-streaming","brisa-build/diff-dom-streaming"],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brisa-build%2Fdiff-dom-streaming","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brisa-build%2Fdiff-dom-streaming/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brisa-build%2Fdiff-dom-streaming/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brisa-build%2Fdiff-dom-streaming/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brisa-build","download_url":"https://codeload.github.com/brisa-build/diff-dom-streaming/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247427006,"owners_count":20937201,"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":["diff","dom","dom-manipulation","html-streaming","javascript","typescript","virtual-dom"],"created_at":"2024-11-19T04:20:00.058Z","updated_at":"2025-04-06T03:06:47.644Z","avatar_url":"https://github.com/brisa-build.png","language":"TypeScript","funding_links":[],"categories":["TypeScript","Table of Contents"],"sub_categories":["Brisa packages"],"readme":"\u003cp align=\"center\"\u003e\n    \u003cpicture\u003e\n      \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://github.com/aralroca/diff-dom-streaming/assets/13313058/ca678952-6232-4db4-aff2-c4bedade4f9a\" width=\"128\"\u003e\n      \u003cimg src=\"https://github.com/aralroca/diff-dom-streaming/assets/13313058/6d544ef2-651e-4907-a246-abc6c859ab5c\" width=\"128\"\u003e\n    \u003c/picture\u003e\n        \u003ch1 align=\"center\"\u003eDiff DOM Streaming\u003c/h1\u003e\n\u003c/p\u003e\n\n[![npm version](https://badge.fury.io/js/diff-dom-streaming.svg)](https://badge.fury.io/js/diff-dom-streaming)\n![npm](https://img.shields.io/npm/dw/diff-dom-streaming)\n![size](https://img.shields.io/bundlephobia/minzip/diff-dom-streaming)\n[![PRs Welcome][badge-prwelcome]][prwelcome]\n\u003ca href=\"https://github.com/aralroca/diff-dom-streaming/actions?query=workflow%3ATest\" alt=\"Tests status\"\u003e\n\u003cimg src=\"https://github.com/aralroca/diff-dom-streaming/workflows/Test/badge.svg\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://twitter.com/intent/follow?screen_name=aralroca\"\u003e\n\u003cimg src=\"https://img.shields.io/twitter/follow/aralroca?style=social\u0026logo=x\"\n            alt=\"follow on Twitter\"\u003e\u003c/a\u003e\n\n\u003c/div\u003e\n\n[badge-prwelcome]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square\n[prwelcome]: http://makeapullrequest.com\n\nThe Diff DOM (Document Object Model) algorithm is used to compare two versions of the DOM, such as before and after an update on a web page. It aims to efficiently identify the changes between both DOMs, minimizing the number of manipulations required to update the user interface.\n\nThe Diff DOM Streaming library extends the traditional Diff DOM algorithm by introducing support for comparing a DOM node with a stream. This enables the library to process the changes incrementally as they occur during the diff process.\n\nFor more info, read this:\n\n- [HTML Streaming Over the Wire | A Deep Dive](https://dev.to/aralroca/html-streaming-over-the-wire-a-deep-dive-2n20).\n- [SPA-like Navigation Preserving Web Component State](https://dev.to/aralroca/spa-like-navigation-preserving-web-component-state-lh3)\n\n## Getting started\n\n### NPM\n\nInstall:\n\n```sh\nbun install diff-dom-streaming\n```\n\nThen import it:\n\n```ts\nimport diff from \"diff-dom-streaming\";\n```\n\n### JSR\n\nInstall:\n\n```sh\nbunx jsr add @aralroca/diff-dom-streaming\n```\n\nThen import it:\n\n```ts\nimport diff from \"@aralroca/diff-dom-streaming\";\n```\n\n### UNPKG\n\nJust import it:\n\n```tsx\nimport diff from \"https://unpkg.com/diff-dom-streaming@latest\";\n```\n\n## Using it\n\n```ts\nconst res = await fetch(/* some url */);\n\n// Diff between the current document and the stream:\nawait diff(document, res.body);\n```\n\n## API\n\n`diff(oldNode: Node, stream: ReadableStream\u003cUint8Array\u003e, options?: Options): Promise\u003cvoid\u003e`\n\nThis function performs a diffing operation between the `oldNode` and the DOM tree from a stream. It applies the necessary changes to update the `oldNode` accordingly. An optional `options` that include:\n\n```ts\ntype Options = {\n  // calback to handle each new docoument node during the streaming\n  // (default: undefined)\n  onNextNode?: NextNodeCallback;\n  // update the DOM using document.startViewTransition (default: false)\n  transition?: boolean;\n  // callback to ignore nodes (default: undefined)\n  shouldIgnoreNode?: (node: Node | null) =\u003e boolean;\n};\n```\n\n## Lists and `key` attribute\n\nKeys help to identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity:\n\n```jsx 3\nconst numbers = [1, 2, 3, 4, 5];\nconst listItems = numbers.map((number) =\u003e (\n  \u003cli key={number.toString()}\u003e{number}\u003c/li\u003e\n));\n```\n\n_(Example with JSX)_\n\nThe `diff-dom-streaming` library takes into account the `key` attribute for these cases, if it does not exist, then see if they have `id`.\n\n## Transitions between pages (View Transition API)\n\nYou can activate the View Transition API updating the DOM with this property:\n\n```diff\nawait diff(document, res.body, {\n+ transition: true\n})\n```\n\n\u003e [!TIP]\n\u003e\n\u003e To access the transition with JavaScript/TypeScript you can access the global property `window.lastDiffTransition`\n\n### Incremental vs full transition\n\nMany times it will make more sense to use a complete transition instead of incremental, especially if we do not use suspense and we want a single transition at once instead of several, in this case, instead of using the configuration, we can use the View Transition API directly:\n\n```diff\n+ document.startViewTransition(async () =\u003e {\nawait diff(document, res.body, {\n-  transition: true,\n});\n+});\n```\n\n## Strong Opinion on BODY Tag Attributes during Diffing\n\nOur library has a strong opinion regarding the handling of the BODY tag attributes during the HTML diffing process. This approach is designed to provide greater flexibility and control over runtime modifications, such as themes, fonts, and other display properties that are managed through BODY tag attributes.\n\nDuring the diffing process, all content within the HTML is typically updated to reflect the latest changes. However, we recognize that certain attributes of the BODY tag, like `class` and custom `data-attributes`, are often modified at runtime to control the presentation of the content. To avoid overwriting these runtime changes, our library's diffing algorithm specifically excludes these attributes from being updated.\n\n### Key Points\n\n- **Preservation of Attributes**: Attributes of the BODY tag (e.g., `class`, `data-attributes`) are preserved and not overwritten during the diffing process.\n- **Consistent Display**: This ensures that runtime modifications, such as theme changes or other display-related adjustments, remain intact across navigations and updates.\n- **Enhanced Customization**: Users can rely on the BODY tag attributes to manage display properties without concern for them being reset during content updates.\n\n### Example\n\nConsider the following scenario where the initial HTML and updated HTML are as follows:\n\n#### Initial HTML\n\n```html\n\u003cbody class=\"light\" data-theme=\"default\"\u003e\n  \u003cdiv\u003eContent A\u003c/div\u003e\n\u003c/body\u003e\n```\n\n#### Updated HTML\n\nAfter a navigation or content update, the new HTML may look like this:\n\n```html\n\u003cbody class=\"dark\" data-theme=\"night\"\u003e\n  \u003cdiv\u003eContent B\u003c/div\u003e\n\u003c/body\u003e\n```\n\n### Result After Diffing\n\nAfter the diffing process, the resulting HTML will be as follows:\n\n```html\n\u003cbody class=\"light\" data-theme=\"default\"\u003e\n  \u003cdiv\u003eContent B\u003c/div\u003e\n\u003c/body\u003e\n```\n\n## Examples\n\nIn the repo we have examples for you to try.\n\n### Locally\n\nThere are some examples:\n\n- Run `bun run example:boxes`\n- Run `bun run examples:spa-like`\n\n### Stackblitz\n\nYou can run the boxes demo with Vanillajs [here](https://stackblitz.com/edit/diff-dom-streaming?file=index.js).\n\n![ezgif-4-1ff18912f4](https://github.com/aralroca/diff-dom-streaming/assets/13313058/f18c01c0-4dfe-473f-8817-fb905adc20c1)\n\n## Acknowledgments\n\nThe Diff DOM Algorithm with HTML Streaming is inspired by the [set-dom](https://github.com/DylanPiercey/set-dom) library by [@dylan_piercey](https://twitter.com/dylan_piercey) and a technique for parsing streams pioneered by [@jaffathecake](https://twitter.com/jaffathecake).\n\n## Contributing\n\nSee [Contributing Guide](CONTRIBUTING.md) and please follow our [Code of Conduct](CODE_OF_CONDUCT.md).\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrisa-build%2Fdiff-dom-streaming","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrisa-build%2Fdiff-dom-streaming","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrisa-build%2Fdiff-dom-streaming/lists"}