How Rendering Works in element-vir
element-vir
is a package for defining and rendering custom HTML web components. It wraps Lit and aims to dramatically reduce the number of mistakes devs can easily make. This post will talk about how rendering within element-vir
works.
Rendering an element
Rendering in element-vir
is the process of taking the output of renderCallback
and actually displaying it to the user in the DOM.
For example, this element will render a simple greeting:
import {defineElement, html} from 'element-vir';
const MyGreeting = defineElement<{name: string}>()({
tagName: 'my-greeting',
renderCallback({inputs}) {
return html`
Hello there ${inputs.name}!
`;
},
});
renderCallback
is called in the following situations:
- on initial connection to the DOM
- when change detection triggers a re-render
Change detection
Change detection is used to detect when an element needs to be re-rendered. An element-vir
element is re-rendered whenever changes are detected in its inputs or state, based on reference equality. When a property within either of those (inputs or state) changes, the element is re-rendered.
In the below example, every time the user clicks the "Click Me" button, it updates the clickCount
state property. When that state update happens, the element re-renders (and thus updates the "You've clicked X times" message).
import {defineElement, html} from 'element-vir';
const MyGreeting = defineElement<{}>()({
tagName: 'my-greeting',
stateInitStatic: {
clickCount: 0,
},
renderCallback({state, updateState}) {
return html`
You've clicked ${state.clickCount} times!
<br />
<button ${listen('click', () => updateState({clickCount: state.clickCount + 1}))}>
Click Me
</button>
`;
},
});
Since change detection runs when state or inputs change, there are two entry points for change detection in element-vir
:
- updating inputs on a child element with
.assign()
- updating state with
updateState()
Efficient template rendering
You're probably already thinking "wow that's super wasteful to completely re-render an entire element every time!" But fear not! That's not actually what happens. Since element-vir
is a wrapper of Lit, it uses Lit's efficient template rendering. Regarding this matter, the Lit docs say:
During an update, only the parts of the DOM that change are re-rendered. Although Lit templates look like string interpolation, Lit parses and creates static HTML once, and then only updates changed values in expressions after that, making updates very efficient.
(Taken from https://lit.dev/docs/components/rendering/#when-templates-render)
Thus, in the above click count example, the only part of the DOM that gets re-rendered is the ${state.clickCount}
interpolation.