Pack your markup.
Simplest vanilla JavaScript nano library for create components-based UI.
3 Kb gzipped; 9 Kb transpiled and minified
In 2018+ you don't need an giant framework for create UI for web apps.
This experiment shows:
- how use modern vanilla JS in UI creating (instead of React, Vue, Angular, JSX etc.);
- how remain simple and lightweight at the same time.
Packmar uses ES6 tagged template literals feature for define templates.
Install with npm:
npm install --save packmar
Use it almost like stateless React-components.
import { html, render } from 'packmar';
/**
* Return virtual DOM element of heading.
* @param {string} options.text Heading text.
* @param {Function} options.onClick Click callback.
* @return {VirtualNode} Virtual element.
*/
function Heading ({ text, onClick }) {
return html`<h1 class="heading" onclick=${onClick}>${text}</h1>`;
}
render(Heading({ text: 'Hello, world' }), document.body);
Packmar relies on values types, you passed in template:
- Strings and Numbers will be passed as normal children/attributes;
- Functions in attributes will be added as listeners;
- Booleans will add or remove attribute depending on the it truth;
For listeners write attributes names like:
const button = html`<button onclick=${() => alert('clicked!')}}>Click me!</button>`;
lists can be represented as:
const beatles = ['John Lennon', 'Ringo Starr', 'Paul McCartney', 'George Harrison'];
const list = html`
<ul class="beatles">
${beatles.map(name => html`<li>${name}</li>`)}
</ul>
`;
Trivial way:
import { html } from 'packmar';
export const Button = ({ text }) => {
return html`<button>${text}</button>`;
};
export const Form = ({ onSubmit }) => {
return html`
<form onsubmit=${onSubmit}>
<input type="email" placeholder="Your email" />
${Button({ text: 'Subscribe' })}
</form>
`;
};
With using define
:
import { html, define } from 'packmar';
// first argument must be a valid custom element' name
export const Button = define('my-button', ({ text, onClick }) => html`
<button onclick=${onClick}>${text}</button>
`);
export const Form = define('my-form', ({ text, onClick }) => html`
<form onsubmit=${onSubmit}>
<input type="email" placeholder="Your email" />
<!-- use like a function -->
${Button({ text: 'Subscribe' })}
<!-- or like custom element -->
<my-button text="Cancel"></my-button>
</form>
`);
import { html, define, render, Component } from 'packmar';
const MyComponent = define('my-component', class extends Component {
state = { count: 0 };
updateCounter () {
this.setState({ count: this.state.count + 1 });
}
render (/* props, state */) {
return html`
<div class="click-counter">
<h2>${this.props.title}</h2>
<p>Clicks: ${this.state.count}</p>
<button onclick=${() => this.updateCounter()}>Click!</button>
</div>
`;
}
});
render(MyComponent({ title: 'Click counter' }), document.body);
Packmar caches elements for reusable templates. Because parse HTML from string slower than cloning nodes.
Packmar prevents simple XSS vulnerabilities. HTML nodes creates without values from expressions.
None.
Ideas:
integration with Web Components (for nesting templates beautiful);classes with patching DOM (for creating reactive UI with MVVM data bindings).- memoize components props for speed up VDOM performance.
MIT