@twobirds/microcomponents
v0.2.5
Published
Micro Components
Maintainers
Readme
MICRO COMPONENTS
MCs are function blocks that you attach to DOM nodes. These are re-usable in any frontend framework.
There are helpers for the following functionalities:
- Web Components: create native web components. Their functionality is comprised of a set of inner MCs.
- State is done by observable objects that you can attach callbacks to. This also allows for 1-way binding to DOM placeholders, and it can be extended to allow for complex data driven state machines.
- Form Handling allows for 2-way binding from an observable form object to DOM form input elements.
- Communication between DOM micro components is ensured via direct selected or directional bubbling CustomEvent's.
- Autoloading allows for build-free websites that dynamically load JS requirements into DOM HTMLElements.
The distinguishing feature is its minimalism, portability and granular modularization. In an almost literal sense MCs work like childrens building blocks, where a chest full of unassuming parts lets you compose whatever functionality lies in your imagination.
You work with one of the established frameworks? You can still put this hands-on tool in your tool belt. You then have a plugin fallback solution for when your FW fails you or lacks functionality.
Prominent Features
- No-build development
- Very shallow learning curve - easy on newbies
- Strictly web standards compliant
- Very little abstraction
Status
- API stable
- Typedoc documentation needs some brushing
Links
demo shop (on-demand loading). No build, on-demand loading elements. Check code in debugger.
demo shop (bundled in one JS file). 9 kB including the library.
Check the typedoc API reference if you want to drill into the source code.
The progress of coding is occasionally being streamed on twitch.
Examples
Functional Principle
- This section only shows the priciple: how Micro Components are stored in the DOM
- The section after this shows Web Components / Custom Elements
- All examples on this page are Javascript.
- The package has full Typescript support
html before:
<div></div>code:
// create a custom micro component class
class MyMC extends DC {
helloWorld() {
console.log('Hello World');
}
}
// get some element from the DOM
const element = document.querySelector('div');
// Add the custom micro component instance to the target element programatically
DC.add(element, 'myMC', new MyMC(element));
// Hello World
element
._mc
.myMC
.helloWorld();html after:
<div data-mc></div>- The
data-mcattribute indicates that this HTML element contains at least one micro component. This is needed for the selectors. - The
_mcproperty is the only interference with the DOM, so any other system should be happy to work with this.
Web Components / Custom Elements
You can define a custom element using a micro component as its primary functional instance. You are still free to add many more micro components to the custom element at runtime.
code:
// create a micro component class
class MyComponent extends DC {}
// define it as a web component
defineCE('my-component', MyComponent);after: you can use ...
<my-component></my-component>... in your template. The custom element will auto-attach.
Or you can construct it programmatically:
const ctor = getCE('my-component');
const body = document.body;
body.append(new ctor());- For more examples regarding web components, go to the typedocs
- Check the sources of the demo shop in the browser debugger
State and 1-way-binding
Declare states and directly connect them to DOM placeholders.
code:
observePlugin(DC);
// create a custom micro component class
class MyMC extends DC {
state = { text: 'Hello world!' };
constructor(target) {
super(target);
this.target.innerHTML = '{text}'; // simplicity, there are other methods
this
.bind( 'state', target ) // one way binding
.subscribe( 'state', (value) => { // attach your own callbacks
console.log(value);
});
// changing the state will change the DOM
this.state.text = 'Hallo Welt'; // also triggers console.log()
}
}Forms and 2-way-binding
Similar to observables above, you can 2-way-bind an observable object to form input fields.
Changes in the object properties trigger input field changes and vice versa.
code:
formValuesPlugin(DC);
// create a custom micro component class
targetHtml = /*HTML*/ `
<form>
<input name="textInput" value="Hallo Welt!"/>
</form>
`;
class MyMC extends DC {
constructor(target) {
super(target);
this.target.innerHTML = targetHtml;
this.formData = { textInput: '' };
this
.formValues(target, 'formData')// link the form property to the DOM input fields
.subscribe( 'formData', (data) => {
console.log(data); // the form data object
});
// now simply changing the form property will change the DOM input fields
this.form.textinput = 'Hello World!';
// ... and editing the form input will change the 'form' property
}
}Event Handlers
Native Events
Events and there handlers are defined and triggered inside the system automatically.
You can use events separately in vanilla JS to communicate with micro components.
code:
// create a custom micro component class
class MyMC extends DC {
constructor(target) {
super(target);
// attach event handler programatically
let onHoverHandler = this.on('hover', () => {
// ...
});
// remove event handler
this.off('hover', onHoverHandler);
}
// this native event handler is attached automatically
onClick() {
console.log('HTMLElement clicked!');
}
// this native event handler is attached automatically
oneClick() {
console.log('HTMLElement clicked! ( only fires once )');
}
}Custom Events
code:
// create a custom micro component class
class MyMC extends DC {
constructor(target) {
super(target);
payload = {
test: 42,
};
setTimeout(() => {
this.trigger('customEvent', payload);
});
}
// this event handler is attached automatically
onCustomEvent(ev) {
console.log('onCustomEvent called with', ev.data.test); // "42"
}
}Goals
This code aims at improving Javascript modularity and encapsulation. Micro Components are built on OOP for structure, so you should embrace DRY, KISS and SOLID principles.
MCs provide a runtime app structure that is directly linked to the browser DOM. That is you don't need web components, you can attach MCs to plain HTMLElements. Having said this: the library also allows for standard web component programming, acting as a framework.
In general this is a HTML / Web Standards first system. There is almost no abstraction, especially not for templating, except state binding to the DOM via simple placeholders.
It has no interference with other frameworks, since it creates a second layer that hovers above the DOM. So what you intended to work as an add-on to React, is usable e.g. in Angular also.
And, of course, you can use it as your main framework using its custom web component functionality.
Its basic principle is composition over inheritance. Also it helps to embrace DIY (Do It Yourself) over Toolchains, but this is up to your personal preference.
You can use Vanilla JS or Typescript or both in parallel in your project. For example you could use plain JS for rapid prototyping functionalities, then convert to TS for production.
Most of all this system by design allows for outsourcing without sharing the main development system. It is absolutely granular as it enforces DRY (Don't Repeat Yourself).
Finally it allows for a clear distinction between Junior, Senior and Architect roles in a project. No more hiring junior programmers with 5 years experience in 50 tools under their belt. A junior in this system is somebody who likes HTML, CSS and JS/TS - nothing more. Teaching all of this system starting at zero takes 1 day. The rest is finger muscle memory and learning/knowing web standards.
Distinguishing Features
There are a few potentially unique aspects of this micro components system that might differentiate it from other framework or library:
Pure OOP Approach: The system explicitly positions itself as a strict object-oriented alternative to the functional approaches that dominate many modern frameworks.
Minimal Interference: The system has minimal DOM interference, only adding _mc property to DOM nodes that contain code. This makes it unusually compatible with other frameworks or libraries.
Hierarchical Component Traversal: The built-in ancestor/closest/child/descendant traversal methods provide a clean way to navigate component relationships.
Direct DOM Integration: Unlike virtual DOM-based frameworks, this approach works directly with the actual DOM while still providing component encapsulation.
Outsourcing Potential: Your external coders do not need to access your code repository or your toolchain.
The strongest argument might be for developers who prefer OOP, want minimal framework lock-in, and need a lightweight solution that works well with existing code or other libraries.
Contact
Feel free to contact me for any questions or inquiries:
email Frank Thürigen
You can fund this.
Disclaimer
I decided to extract this functionality from the now deprecated twoBirds framework, because it is a useful tool in itself and it is framework agnostic.
Later this will be backported into twoBirds as a dependency, and twoBirds will become a completely modularized MC UI package on the base of MCs.
Deprecated test:
manual testing ( also a speed test ).
