https://github.com/elricmann/render
Experimental JavaScript UI library
https://github.com/elricmann/render
javascript rendering typescript ui virtual-dom virtual-machine webassembly
Last synced: 6 months ago
JSON representation
Experimental JavaScript UI library
- Host: GitHub
- URL: https://github.com/elricmann/render
- Owner: elricmann
- License: mit
- Created: 2024-10-16T14:20:43.000Z (12 months ago)
- Default Branch: main
- Last Pushed: 2024-12-29T04:37:11.000Z (9 months ago)
- Last Synced: 2025-04-10T18:56:37.166Z (6 months ago)
- Topics: javascript, rendering, typescript, ui, virtual-dom, virtual-machine, webassembly
- Language: TypeScript
- Homepage: https://npmjs.com/package/librender
- Size: 1010 KB
- Stars: 31
- Watchers: 2
- Forks: 0
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
render
Experimental JavaScript UI rendering library. Render uses a lightweight stack-based VM with minimal opcodes to encode DOM trees and run DOM operations either immediately or in retained mode (by diffing and patching bytecodes). Components are defined as **views** that serialize their attributes and children into `Uint8Array` instances.
### Installation & usage
```
npm install librender
```Refer to the [wiki](https://github.com/elricmann/render/wiki) for basic usage and API documentation.
> [!Note]
>
> _The documentation starts from the v0.2 release of Render, which will not cover `librender` usage in other languages (until API stability towards 1.0). For a basic introduction to this library, refer to the wiki._### Features
- [ ] Views
- [x] `View` - view instance serializer
- [x] `Text` - static text view with title
- [x] `Button` - static button with label
- [x] `Container` - dynamic view with optional attributes (e.g. tag name)
- [x] Tags corresponding to semantic elements (`Container`)
- [x] Attributes on views (`attr` method), deprecate opcode for inline styles
- [x] Event listeners on views (`Container`)
- [x] JSX elements, attributes and expressions
- [ ] Virtual machine DOM
- [x] Create DOM nodes (`OPCODE_CREATE_ELEMENT`)
- [x] Decode 8-bit entries with static offsets (UTF-16 character codes)
- [x] Set DOM node attribute to least recent node (`OPCODE_SET_ATTRIBUTE`)
- [x] Append least recent nodes as parent-child (`OPCODE_APPEND_CHILD`)
- [x] Create DOM text node (`OPCODE_TEXT_NODE`)
- [ ] Set `innerHTML` with in-memory string source
- [x] Append adjacent DOM nodes as siblings (`OPCODE_APPEND_SIBLING`)
- [ ] Diffing and patching arbitrary bytecodes (partially complete)
- [x] Event listeners on least recent node (`OPCODE_EVENT_LISTENER`, requires `__eventStore`)
- [x] Consistency in rendering with timed `requestAnimationFrame`
- [x] Streaming instruction blocks to VM (`createBytecodeStream`, `unsafe_streamBytecodeToVM`)
- [ ] Non-tracking reactive primitives in views
- [ ] Precompiled bytecode from views (requires build tools)
- [ ] Off the main thread view serialization (`ThreadView`)### Quick overview
Render relies on **views** to return byte arrays with exact alignment of data values and offsets. The VM does not _fix_ program inputs which is why views form safe high-level wrappers representing extendable components. Aside, the default settings are supposed to be configurable.
Portability is the primary goal of this library. I wrote `librender` expecting that there could be a **single optimizing bytecode IR** for various web-based rendering libraries. To this effect, there is an imperative-procedural approach when defining view methods that correspond to raw bytecode.
![]()
#### Completeness of the virtual machine
The VM does not fully take the role of a DOM-based model and opcodes are only limited to DOM operations where inputs are serializable (e.g. event handlers are _not_ serializable). It lacks features like pausability and resumability, async batch streaming, per-batch scheduling, and virtual prioritization, that could essentially improve rendering.
![]()
The structural description of the VM is:
- The **stack** is reserved for holding references to handlers and raw strings
- The **memory** applies to enabled shared memory (e.g. temporal view states) between VM instances
- `nodeCount` is a counter that allows managing the `nodeIndexStack` in the DOM operations#### Portability of the bytecode IR
WebAssembly modules are loaded as view slices into an array buffer representing a linear memory model. Since modules are precompiled, the bytecode could target `librender` with virtually no overhead. This would imply that optimizing the VM itself from the JavaScript source would improve existing programs without introducing layers of indirection.
![]()
With this enabled, various high-level libraries targeting the bytecode can be used as microfrontends with predictability. Currently, `librender` is barely useable so this will require major effort to put in place. Additionally, certain data structures can be linearly represented for uniformity, e.g. C-like structs with offsets require no deallocation in WebAssembly. Growing memory is as easy as incrementing a pointer.
Portability allows loading `.bin` files through a shared worker, which could be useful for monolithic SPAs that depend on server endpoints to fetch and render data in the main UI thread for client-side rendering.
### Acknowledgements
Render does not provide unique insights into the approach rendering is done or criteria for commiting DOM nodes for painting. Other libraries such as [React](https://react.dev/) (scheduling model) and [GlimmerVM](https://github.com/glimmerjs/glimmer-vm) (opcode-based rendering) are to be considered prior art in this regard.
### License
Copyright © 2024 Elric Neumann. MIT License.