{"id":3282,"date":"2021-11-04T07:44:51","date_gmt":"2021-11-04T14:44:51","guid":{"rendered":"https:\/\/coderpad.io\/?p=3282"},"modified":"2023-06-07T14:15:28","modified_gmt":"2023-06-07T21:15:28","slug":"web-components-101-lit-framework","status":"publish","type":"post","link":"https:\/\/coderpad.io\/blog\/development\/web-components-101-lit-framework\/","title":{"rendered":"Web Components 101: Lit Framework"},"content":{"rendered":"\n<p>Recently we talked about <a href=\"https:\/\/coderpad.io\/blog\/development\/intro-to-web-components-vanilla-js\/\">what web components are and how you can build a web app utilizing them with only vanilla JavaScript<\/a>.<\/p>\n\n\n\n<p>While web components are absolutely usable with only vanilla JavaScript, more complex usage, especially pertaining to value binding, can easily become unwieldy.<\/p>\n\n\n\n<p>One potential solution might be using a web component framework such as VueJS or React. However, web-standard components can still be a massive boon to development.<\/p>\n\n\n\n<p>As such, there\u2019s a framework called <a href=\"https:\/\/lit.dev\/\" target=\"_blank\" rel=\"noopener\">\u201cLit\u201d<\/a> that is developed specifically to leverage web components. With <a href=\"https:\/\/lit.dev\/blog\/2021-09-21-announcing-lit-2\/\" target=\"_blank\" rel=\"noopener\">Lit 2.0 recently launching as a stable release<\/a>, we thought we\u2019d take a look at how we can simplify web component development.<\/p>\n\n\n<aside class=\"\n    cta-banner\n     cta-banner--bg-blue      cta-banner--has-media \"\ndata-block-name=\"cta-banner\">\n    <div class=\"inner\">\n        <div class=\"content\">\n                            <h2 class=\"headline\">Learn how to run front-end developer interviews that don&#8217;t suck<\/h2>\n            \n                            <div class=\"cta-buttons\">\n                                    <a href=\"https:\/\/coderpad.io\/blog\/interviewing\/5-tips-for-interviewing-frontend\/\" class=\"button  js-cta--read-our-guide\" data-ga-category=\"CTA\" data-ga-label=\"Learn how to run front-end developer interviews that don&#039;t suck|Read our guide\">Read our guide<\/a>\n                                <\/div>\n                    <\/div>\n                    <div class=\"media\">\n                <img loading=\"lazy\" decoding=\"async\" width=\"432\" height=\"342\" src=\"https:\/\/coderpad.io\/wp-content\/uploads\/2022\/08\/Illustration-of-man-with-beard-popping-out-of-computer-chat.png\" class=\"attachment-large size-large\" alt=\"\" srcset=\"https:\/\/coderpad.io\/wp-content\/uploads\/2022\/08\/Illustration-of-man-with-beard-popping-out-of-computer-chat.png 432w, https:\/\/coderpad.io\/wp-content\/uploads\/2022\/08\/Illustration-of-man-with-beard-popping-out-of-computer-chat-300x238.png 300w\" sizes=\"auto, (max-width: 432px) 100vw, 432px\" \/>\n            <\/div>\n            <\/div>\n<\/aside>\n\n\n\n<h2 class=\"wp-block-heading\">HTML<\/h2>\n\n\n\n<p>One of the greatest strengths of custom elements is the ability to contain multiple other elements. This makes it so that you can have custom elements for every scale: from a button to an entire page.<\/p>\n\n\n\n<p>To do this in a vanilla JavaScript custom element, you can use <code>innerHTML<\/code> to create new child elements.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span>&gt;<\/span><span class=\"actionscript\">\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">MyComponent<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">HTMLElement<\/span> <\/span>{\n\u00a0 connectedCallback() {\n\u00a0 \u00a0\u00a0\u00a0 <span class=\"hljs-keyword\">this<\/span>.render();\n\u00a0 }\n\n\u00a0 render() {\n\u00a0 \u00a0\u00a0\u00a0 <span class=\"hljs-keyword\">this<\/span>.innerHTML = <span class=\"hljs-string\">'&lt;p&gt;Hello!&lt;\/p&gt;'<\/span>;\n\u00a0 }\n}\n\ncustomElements.define(<span class=\"hljs-string\">'hello-component'<\/span>, MyComponent);\n<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">hello-component<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">hello-component<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This initial example looks fairly similar to what the Lit counterpart of that code looks like:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"module\"<\/span>&gt;<\/span><span class=\"javascript\">\n<span class=\"hljs-keyword\">import<\/span> { html, LitElement } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"https:\/\/cdn.skypack.dev\/lit\"<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">HelloElement<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">LitElement<\/span> <\/span>{\n    render() {\n        <span class=\"hljs-keyword\">return<\/span> html`<span class=\"xml\">\n              <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>Hello!<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n        `<\/span>;\n    }\n}\n\n<span class=\"hljs-built_in\">window<\/span>.customElements.define(<span class=\"hljs-string\">'hello-component'<\/span>, HelloElement);\n<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">hello-component<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">hello-component<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<div\n\tclass=\"sandbox-embed responsive-embed  sandbox-embed--full-width\"\n\tstyle=\"padding-top: 125%\"\ndata-block-name=\"coderpad-sandbox-embed\">\n\t<iframe src=\"https:\/\/embed.coderpad.io\/sandbox?question_id=194516&#038;use_question_button\" width=\"640\" height=\"800\" loading=\"lazy\" aria-label=\"Try out the CoderPad sandbox\"><\/iframe>\n<\/div>\n\n\n\n<p>There are two primary differences from the vanilla JavaScript example. First, we no longer need to use the <code>connectedCallback<\/code> to call <code>render<\/code>. The LitElement\u2019s <code>render<\/code> function is called by Lit itself whenever needed &#8211; such as when data changes or for an initial render &#8211; avoiding the need to manually re-call the render method.&nbsp;<\/p>\n\n\n\n<p>That said, Lit components fully support the same lifecycle methods as a vanilla custom elements.<\/p>\n\n\n\n<p>The second, easier-to-miss change from the vanilla JavaScript component to the Lit implementation, is that when we set our HTML, we don\u2019t simply use a basic template literal (<code>\\<\/code>&lt;p&gt;test&lt;\/p&gt;`<code>): we pass the function<\/code>html<code>to the template literal (<\/code>html`&lt;p&gt;test&lt;\/p&gt;&#8220;).<\/p>\n\n\n\n<p>This leverages <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Template_literals#tagged_templates\" target=\"_blank\" rel=\"noopener\">a somewhat infrequently used feature of template literals called tagged templates<\/a>. Tagged templates allow a template literal to be passed to a function. This function can then transform the output based on the string input and expected interpolated placeholders.<\/p>\n\n\n\n<p>Because tagged templates return a value like any other function, you can assign the return value of <code>html<\/code> to a variable.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">render {\n\u00a0 \u00a0 <span class=\"hljs-keyword\">const<\/span> el = html`<span class=\"xml\">\n\u00a0 \u00a0 \u00a0\u00a0\u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>Hello!<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n\u00a0 \u00a0 \u00a0 `<\/span>;\n\u00a0 \u00a0 <span class=\"hljs-keyword\">return<\/span> el;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>If you were to <code>console.log<\/code> this value, you\u2019d notice that it\u2019s not an<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/HTMLElement\" target=\"_blank\" rel=\"noopener\">HTMLElement<\/a>. Instead, it\u2019s a custom value that Lit utilizes to render to proper DOM nodes.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Event Binding<\/h2>\n\n\n\n<p>\u201cIf the syntax is so similar, why would I add a framework to build custom elements?\u201d<br><br>Well, while the Vanilla JavaScript and Lit custom element code look similar for a small demo: The story changes dramatically when you look to scale up.<\/p>\n\n\n\n<p>For example, if you wanted to render a button and add a click event to the button with vanilla JavaScript, you\u2019d have to abandon the <code>innerHTML<\/code> element assignment method.<\/p>\n\n\n\n<p>First, we\u2019ll create an element using <code>document.createElement<\/code>, then add events, and finally utilize<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Element\/append\" target=\"_blank\" rel=\"noopener\">an element method like <code>append<\/code><\/a> to add the node to the DOM.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span>&gt;<\/span><span class=\"javascript\">\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">MyComponent<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">HTMLElement<\/span> <\/span>{\n  connectedCallback() {\n    <span class=\"hljs-keyword\">this<\/span>.render();\n  }\n\n  sayHello() {\n    alert(<span class=\"hljs-string\">\"Hi there!\"<\/span>);\n  }\n\n  render() {\n    <span class=\"hljs-keyword\">const<\/span> button = <span class=\"hljs-built_in\">document<\/span>.createElement(<span class=\"hljs-string\">'button'<\/span>);\n    button.innerText = <span class=\"hljs-string\">\"Say Hello!\"<\/span>;\n    button.addEventListener(<span class=\"hljs-string\">'click'<\/span>, <span class=\"hljs-keyword\">this<\/span>.sayHello);\n    <span class=\"hljs-keyword\">this<\/span>.append(button);\n  }\n}\n\n<span class=\"hljs-built_in\">window<\/span>.customElements.define(<span class=\"hljs-string\">'hello-component'<\/span>, MyComponent);\n<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">hello-component<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">hello-component<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>While this works for the initial render, it doesn\u2019t handle any of the edgecases that,at scale,can cause long-term damage to your app\u2019s maintainability &amp; performance.&nbsp;<\/p>\n\n\n\n<p>For example, future re-renders of the element will duplicate the button. To solve this, you must iterate through all of the element\u2019s <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Element\/children\" target=\"_blank\" rel=\"noopener\"><code>children<\/code><\/a> and <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Element\/remove\" target=\"_blank\" rel=\"noopener\"><code>remove<\/code><\/a> them one-by-one.<\/p>\n\n\n\n<p>Further, once the element is removed from the DOM, the click listener is not implicitly removed in the background. Because of this, it\u2019s never released from memory and is considered a memory leak. If this issue continued to occur during long-term usage of your app, it would likely bloat memory usage and eventually crash or hang.<\/p>\n\n\n\n<p>To solve this, you\u2019d need to assign a variable for every <code>addEventListener<\/code> you had present. This may be simple for one or two events, but add too many and it can be difficult to keep track.<\/p>\n\n\n\n<p>And all of this ignores the maintenance standpoint: What does that code do at a glance?<\/p>\n\n\n\n<p>It doesn&#8217;t look anything like HTML and as a result, requires you to consistently context shift between writing standard HTML in a string and using the DOM APIs to construct elements.&nbsp;<\/p>\n\n\n\n<p>Luckily, Lit doesn\u2019t have these issues. Here\u2019s the same button construction and rendering to a custom element using Lit instead of vanilla JavaScript:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"module\"<\/span>&gt;<\/span><span class=\"javascript\">\n<span class=\"hljs-keyword\">import<\/span> { html, LitElement } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"https:\/\/cdn.skypack.dev\/lit\"<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">HelloElement<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">LitElement<\/span> <\/span>{\n    sayHello() {\n          alert(<span class=\"hljs-string\">\"Hi there!\"<\/span>);\n    }\n\n    render() {\n        <span class=\"hljs-keyword\">return<\/span> html`<span class=\"xml\">\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> @<span class=\"hljs-attr\">click<\/span>=<\/span><\/span><span class=\"hljs-subst\">${<span class=\"hljs-keyword\">this<\/span>.sayHello}<\/span><span class=\"xml\"><span class=\"hljs-tag\">&gt;<\/span>Say Hello!<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n        `<\/span>;\n    }\n}\n\n<span class=\"hljs-built_in\">window<\/span>.customElements.define(<span class=\"hljs-string\">'hello-component'<\/span>, HelloElement);\n<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">hello-component<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">hello-component<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<div\n\tclass=\"sandbox-embed responsive-embed  sandbox-embed--full-width\"\n\tstyle=\"padding-top: 125%\"\ndata-block-name=\"coderpad-sandbox-embed\">\n\t<iframe src=\"https:\/\/embed.coderpad.io\/sandbox?question_id=194518&#038;use_question_button\" width=\"640\" height=\"800\" loading=\"lazy\" aria-label=\"Try out the CoderPad sandbox\"><\/iframe>\n<\/div>\n\n\n\n<p>Yup, that\u2019s all. Lit allows you to bind elements by using the <code>@<\/code> sign and passing the function as a placeholder to the <code>html<\/code> tagged template. Not only does this look much HTML-like, it handles event cleanup, re-rendering, and more.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Attributes &amp; Properties<\/h2>\n\n\n\n<p>As we learned before, there are two ways to pass values between and into components: attributes and values.<\/p>\n\n\n\n<p>Previously, when we were using vanilla JavaScript, we had to define these separately. Moreover, we had to declare which attributes to dynamically listen to value changes of.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">MyComponent<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">HTMLElement<\/span> <\/span>{\n\u00a0 connectedCallback() {\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">this<\/span>.render();\n\u00a0 }\n\n\u00a0 <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">get<\/span> observedAttributes() {\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">return<\/span> &#91;<span class=\"hljs-string\">'message'<\/span>];\n\u00a0 }\n\n\u00a0 attributeChangedCallback(name, oldValue, newValue) {\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">this<\/span>.render();\n\u00a0 }\n\n\u00a0 render() {\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">const<\/span> message = <span class=\"hljs-keyword\">this<\/span>.attributes.message.value || <span class=\"hljs-string\">'Hello world'<\/span>;\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">this<\/span>.innerHTML = <span class=\"hljs-string\">`&lt;h1&gt;<span class=\"hljs-subst\">${message}<\/span>&lt;\/h1&gt;`<\/span>;\n\u00a0 }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>In Lit, we declare attributes and properties using a static getter and treat them as normal values in any of our functions.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">import<\/span> { html, LitElement } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"https:\/\/cdn.skypack.dev\/lit\"<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">HelloElement<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">LitElement<\/span> <\/span>{\n\u00a0 <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">get<\/span> properties() {\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">return<\/span> {\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-attr\">message<\/span>: {<span class=\"hljs-attr\">type<\/span>: <span class=\"hljs-built_in\">String<\/span>},\n\u00a0 \u00a0 \u00a0 };\n\u00a0 }\n\n\u00a0 <span class=\"hljs-keyword\">constructor<\/span>() {\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">super<\/span>();\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">this<\/span>.message = <span class=\"hljs-string\">'Hello world'<\/span>;\n\u00a0 }\n\n\u00a0 render() {\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">return<\/span> html`<span class=\"xml\">\n\u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span>&gt;<\/span><\/span><span class=\"hljs-subst\">${<span class=\"hljs-keyword\">this<\/span>.message}<\/span><span class=\"xml\"><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span>\n\u00a0 `<\/span>;\n\u00a0 }\n}\n\n<span class=\"hljs-built_in\">window<\/span>.customElements.define(<span class=\"hljs-string\">'hello-component'<\/span>, HelloElement);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>For starters, we no longer have to manually call \u201crender\u201d when a property\u2019s value is changed. Lit will re-render when values are changed.<\/p>\n\n\n\n<p>That\u2019s not all, though: Keen eyed readers will notice that we\u2019re declaring a type associated with the <code>message<\/code> property.<\/p>\n\n\n\n<p>Unlike the <a href=\"https:\/\/github.com\/facebook\/prop-types\" target=\"_blank\" rel=\"noopener\">React ecosystem\u2019s PropTypes<\/a>, the <code>type<\/code> subproperty doesn\u2019t do runtime type validation. Instead, it acts as an automatic type converter.<\/p>\n\n\n\n<p>This can be of great help as the knowledge that attributes can only be strings can be difficult to remember while debugging.<\/p>\n\n\n\n<p>For example, we can tell Lit to convert an attribute to a Number and it will migrate from a string that looks like a number to an actual JavaScript type number.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"module\"<\/span>&gt;<\/span><span class=\"javascript\">\n<span class=\"hljs-keyword\">import<\/span> { html, LitElement } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"https:\/\/cdn.skypack.dev\/lit\"<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">HelloElement<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">LitElement<\/span> <\/span>{\n\u00a0 <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">get<\/span> properties() {\n\u00a0 \u00a0\u00a0\u00a0 <span class=\"hljs-keyword\">return<\/span> {\n\u00a0 \u00a0 \u00a0 \u00a0\u00a0\u00a0 <span class=\"hljs-attr\">val<\/span>: {<span class=\"hljs-attr\">type<\/span>: <span class=\"hljs-built_in\">Number<\/span>},\n\u00a0 \u00a0\u00a0\u00a0 };\n\u00a0 }\n\n\u00a0 render() {\n\u00a0 \u00a0\u00a0\u00a0 <span class=\"hljs-keyword\">return<\/span> html`<span class=\"xml\">\n\u00a0\u00a0\u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span>&gt;<\/span><\/span><span class=\"hljs-subst\">${<span class=\"hljs-keyword\">this<\/span>.val}<\/span><span class=\"xml\"> is typeof <\/span><span class=\"hljs-subst\">${<span class=\"hljs-keyword\">typeof<\/span> <span class=\"hljs-keyword\">this<\/span>.val}<\/span><span class=\"xml\"><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span>\n\u00a0 `<\/span>;\n\u00a0 }\n}\n\n<span class=\"hljs-built_in\">window<\/span>.customElements.define(<span class=\"hljs-string\">'hello-component'<\/span>, HelloElement);\n&lt;<span class=\"hljs-regexp\">\/&lt;strong&gt;script&lt;\/<\/span>strong&gt;&gt;\n\n&lt;!-- This will show <span class=\"hljs-string\">\"123 is typeof number\"<\/span>\u00a0 --&gt;\n<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">hello-component<\/span> <span class=\"hljs-attr\">val<\/span>=<span class=\"hljs-string\">\"123\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">hello-component<\/span>&gt;<\/span><\/span>\n&lt;!-- This will show <span class=\"hljs-string\">\"NaN is typeof number\"<\/span>\u00a0 --&gt;\n<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">hello-component<\/span> <span class=\"hljs-attr\">val<\/span>=<span class=\"hljs-string\">\"Test\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">hello-component<\/span>&gt;<\/span><\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<div\n\tclass=\"sandbox-embed responsive-embed  sandbox-embed--full-width\"\n\tstyle=\"padding-top: 125%\"\ndata-block-name=\"coderpad-sandbox-embed\">\n\t<iframe src=\"https:\/\/embed.coderpad.io\/sandbox?question_id=194519&#038;use_question_button\" width=\"640\" height=\"800\" loading=\"lazy\" aria-label=\"Try out the CoderPad sandbox\"><\/iframe>\n<\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Attribute Reactivity<\/h3>\n\n\n\n<p><br>One of the biggest benefits of not having to call <code>render<\/code> manually is that Lit is able to render contents when they need to update.<\/p>\n\n\n\n<p>For example, given this example, the contents will render properly to update with new values.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">import<\/span> { html, LitElement } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"lit\"<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">ChangeMessageElement<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">LitElement<\/span> <\/span>{\n\u00a0 <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">get<\/span> properties() {\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">return<\/span> {\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-attr\">message<\/span>: {<span class=\"hljs-attr\">type<\/span>: <span class=\"hljs-built_in\">String<\/span>},\n\u00a0 \u00a0 \u00a0 };\n\u00a0 }\n\n\u00a0 changeSelectedMsg() {\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">const<\/span> newMsg = msgs&#91;<span class=\"hljs-built_in\">Math<\/span>.floor(<span class=\"hljs-built_in\">Math<\/span>.random() * msgs.length)];\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">this<\/span>.message = newMsg;\n\u00a0 }\n\n\u00a0 <span class=\"hljs-keyword\">constructor<\/span>() {\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">super<\/span>();\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">this<\/span>.message = <span class=\"hljs-string\">'Hello world'<\/span>;\n\u00a0 }\n\n\u00a0 render() {\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">return<\/span> html`<span class=\"xml\">\n\u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> @<span class=\"hljs-attr\">click<\/span>=<span class=\"hljs-string\">\"<\/span><\/span><\/span><span class=\"hljs-subst\">${<span class=\"hljs-keyword\">this<\/span>.changeSelectedMsg}<\/span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"<\/span>&gt;<\/span>Toggle<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n\u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">hello-component<\/span> <span class=\"hljs-attr\">message<\/span>=<\/span><\/span><span class=\"hljs-subst\">${<span class=\"hljs-keyword\">this<\/span>.message}<\/span><span class=\"xml\"><span class=\"hljs-tag\">&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">hello-component<\/span>&gt;<\/span>\n\u00a0 `<\/span>;\n\u00a0 }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<div\n\tclass=\"sandbox-embed responsive-embed  sandbox-embed--full-width\"\n\tstyle=\"padding-top: 125%\"\ndata-block-name=\"coderpad-sandbox-embed\">\n\t<iframe src=\"https:\/\/embed.coderpad.io\/sandbox?question_id=181069&#038;use_question_button\" width=\"640\" height=\"800\" loading=\"lazy\" aria-label=\"Try out the CoderPad sandbox\"><\/iframe>\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Reactive Data Binding<\/h2>\n\n\n\n<p>This reactivity comes with its own set of limitations. While numbers and strings are able to be set fairly trivially, objects (and by extension arrays) are a different story.<\/p>\n\n\n\n<p>This is because, in order for Lit to know what properties to update in render, an object must have a different reference value from one to another. <a href=\"https:\/\/www.coletiv.com\/blog\/dangers-of-using-objects-in-useState-and-useEffect-ReactJS-hooks\/\" target=\"_blank\" rel=\"noopener\">This is just how React and other frameworks detect changes in state as well.<\/a><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">FormElement<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">LitElement<\/span> <\/span>{\n\u00a0 <span class=\"hljs-keyword\">constructor<\/span>() { <span class=\"hljs-comment\">\/* ... *\/<\/span> }\n\u00a0 <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">get<\/span> properties() {\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">return<\/span> {\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-attr\">todoList<\/span>: {<span class=\"hljs-attr\">type<\/span>: <span class=\"hljs-built_in\">Array<\/span>},\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-attr\">inputVal<\/span>: {<span class=\"hljs-attr\">type<\/span>: <span class=\"hljs-built_in\">String<\/span>},\n\u00a0 \u00a0 \u00a0 };\n\u00a0 }\n\n\u00a0 _onSubmit(e) {\n\u00a0 \u00a0 \u00a0 e.preventDefault();\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-comment\">\/* This works, because we\u2019re changing the object reference *\/<\/span>\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">this<\/span>.todoList = &#91;...this.todoList, <span class=\"hljs-keyword\">this<\/span>.inputVal];\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-comment\">\/* But this would not, because we aren\u2019t *\/<\/span>\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-comment\">\/\/ this.todoList.push(this.inputVal);\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0this.inputVal = '';<\/span>\n\u00a0 }\n\n\u00a0 _onChange(e) {\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">this<\/span>.inputVal = e.target.value;\n\u00a0 }\n\u00a0\n\u00a0 render() {\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">return<\/span> html`<span class=\"xml\">\n\u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">form<\/span> @<span class=\"hljs-attr\">submit<\/span>=<span class=\"hljs-string\">\"<\/span><\/span><\/span><span class=\"hljs-subst\">${<span class=\"hljs-keyword\">this<\/span>._onSubmit}<\/span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"<\/span>&gt;<\/span>\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">.value<\/span>=<span class=\"hljs-string\">\"<\/span><\/span><\/span><span class=\"hljs-subst\">${<span class=\"hljs-keyword\">this<\/span>.inputVal}<\/span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"<\/span> @<span class=\"hljs-attr\">change<\/span>=<span class=\"hljs-string\">\"<\/span><\/span><\/span><span class=\"hljs-subst\">${<span class=\"hljs-keyword\">this<\/span>._onChange}<\/span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\"<\/span> \/&gt;<\/span>\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"submit\"<\/span>&gt;<\/span>Add<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n\u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">form<\/span>&gt;<\/span>\n\u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">todo-component<\/span> <span class=\"hljs-attr\">todos<\/span>=<\/span><\/span><span class=\"hljs-subst\">${<span class=\"hljs-keyword\">this<\/span>.todoList}<\/span><span class=\"xml\"><span class=\"hljs-tag\">&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">todo-component<\/span>&gt;<\/span>\n\u00a0 `<\/span>;\n\u00a0 }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<div\n\tclass=\"sandbox-embed responsive-embed  sandbox-embed--full-width\"\n\tstyle=\"padding-top: 125%\"\ndata-block-name=\"coderpad-sandbox-embed\">\n\t<iframe src=\"https:\/\/embed.coderpad.io\/sandbox?question_id=181090&#038;use_question_button\" width=\"640\" height=\"800\" loading=\"lazy\" aria-label=\"Try out the CoderPad sandbox\"><\/iframe>\n<\/div>\n\n\n\n<p>You may also notice that we\u2019re binding both the user\u2019s input and output to set and reflect the state. <a href=\"https:\/\/coderpad.io\/blog\/development\/master-react-unidirectional-data-flow\/\">This is exactly how other frameworks like React also expect you to manage user state<\/a>.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Prop Passing with Lit\u2019s Dot Synax<\/h2>\n\n\n\n<p>HTML attributes are not the only way to pass data to a web component. Properties on the element class are a way to pass more than just a string to an element.<\/p>\n\n\n\n<p>While the <code>type<\/code> field can help solve this problem as well, you\u2019re still limited by serializability, meaning that things like functions won\u2019t be able to be passed by attributes.<\/p>\n\n\n\n<p>While properties are a more robust method of data passing to web components, they\u2019re seldomly used in vanilla JavaScript due to their complexity in coding.<\/p>\n\n\n\n<p>For example, this is a simple demonstration of passing an array.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">html<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">head<\/span>&gt;<\/span>\n    <span class=\"hljs-comment\">&lt;!-- Render object array as \"ul\", passing fn to checkbox change event --&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span>&gt;<\/span><span class=\"javascript\">\n      <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">MyComponent<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">HTMLElement<\/span> <\/span>{\n        property = &#91;];\n     \n        connectedCallback() {\n          <span class=\"hljs-keyword\">this<\/span>.render();\n        }\n     \n        render() {\n          <span class=\"hljs-keyword\">this<\/span>.innerHTML = <span class=\"hljs-string\">`&lt;h1&gt;<span class=\"hljs-subst\">${<span class=\"hljs-keyword\">this<\/span>.property.length}<\/span>&lt;\/h1&gt;`<\/span>;\n        }\n      }\n   \n      customElements.define(<span class=\"hljs-string\">'my-component'<\/span>, MyComponent);\n    <\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n   \n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span>&gt;<\/span><span class=\"javascript\">\n      <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">changeElement<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n        <span class=\"hljs-keyword\">const<\/span> compEl = <span class=\"hljs-built_in\">document<\/span>.querySelector(<span class=\"hljs-string\">'#mycomp'<\/span>);\n        compEl.property = &#91;\n          <span class=\"hljs-string\">'Testing'<\/span>,\n          <span class=\"hljs-string\">'Second'<\/span>,\n          <span class=\"hljs-string\">'Another'<\/span>\n        ];      \n        compEl.render();\n      }\n    <\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n   \n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">head<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">body<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">my-component<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"mycomp\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">my-component<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">onclick<\/span>=<span class=\"hljs-string\">\"changeElement()\"<\/span>&gt;<\/span>Change to 3<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">body<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">html<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>First, you have to get a reference to the element using an API like <code>querySelector<\/code>. This means you need to introduce a new reference to the component and make sure the IDs match in both parts of code.<br><br>Then, just as is the case with updating attribute values, we need to manually call the \u201crender\u201d function in order to update the UI.<\/p>\n\n\n\n<p>But those complaints aside, there\u2019s still one more: It places your data and component tags in two different areas. Because of this, it can be more difficult to debug or figure out what data is being passed to what component.<\/p>\n\n\n\n<p>Lit takes a different approach. Within a Lit <code>html<\/code> tagged template, add a period before an attribute binding and suddenly it will pass as a property instead.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"module\"<\/span>&gt;<\/span><span class=\"javascript\">\n<span class=\"hljs-keyword\">import<\/span> { html, LitElement } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"https:\/\/cdn.skypack.dev\/lit\"<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">MyElement<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">LitElement<\/span> <\/span>{\n  <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">get<\/span> properties() {\n    <span class=\"hljs-keyword\">return<\/span> {\n      <span class=\"hljs-attr\">property<\/span>: {<span class=\"hljs-attr\">type<\/span>: <span class=\"hljs-built_in\">Array<\/span>},\n    };\n  }\n\n  render() {\n    <span class=\"hljs-keyword\">return<\/span> html`<span class=\"xml\">\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span>&gt;<\/span><\/span><span class=\"hljs-subst\">${<span class=\"hljs-keyword\">this<\/span>.property.length}<\/span><span class=\"xml\"><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span>\n    `<\/span>;\n  }\n}\n\n<span class=\"hljs-built_in\">window<\/span>.customElements.define(<span class=\"hljs-string\">'my-component'<\/span>, MyElement);\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">ChangeMessageElement<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">LitElement<\/span> <\/span>{\n    <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">get<\/span> properties() {\n      <span class=\"hljs-keyword\">return<\/span> {\n        <span class=\"hljs-attr\">array<\/span>: {<span class=\"hljs-attr\">type<\/span>: <span class=\"hljs-built_in\">Array<\/span>},\n      };\n    }\n\n    <span class=\"hljs-keyword\">constructor<\/span>() {\n      <span class=\"hljs-keyword\">super<\/span>();\n      <span class=\"hljs-keyword\">this<\/span>.array = &#91;];\n    }\n\n    changeElement() {\n      <span class=\"hljs-keyword\">this<\/span>.array = &#91;\n        <span class=\"hljs-string\">'Testing'<\/span>,\n        <span class=\"hljs-string\">'Second'<\/span>,\n        <span class=\"hljs-string\">'Another'<\/span>\n      ];      \n    }\n\n    render() {\n        <span class=\"hljs-keyword\">return<\/span> html`<span class=\"xml\">\n      <span class=\"hljs-comment\">&lt;!-- If \"property\" didn't have a period, it would pass as attribute --&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">my-component<\/span> <span class=\"hljs-attr\">.property<\/span>=<\/span><\/span><span class=\"hljs-subst\">${<span class=\"hljs-keyword\">this<\/span>.array}<\/span><span class=\"xml\"><span class=\"hljs-tag\">&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">my-component<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> @<span class=\"hljs-attr\">click<\/span>=<\/span><\/span><span class=\"hljs-subst\">${<span class=\"hljs-keyword\">this<\/span>.changeElement}<\/span><span class=\"xml\"><span class=\"hljs-tag\">&gt;<\/span>Change to 3<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n    `<\/span>;\n    }\n}\n\n<span class=\"hljs-built_in\">window<\/span>.customElements.define(<span class=\"hljs-string\">'change-message-component'<\/span>, ChangeMessageElement);\n<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">change-message-component<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">change-message-component<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<div\n\tclass=\"sandbox-embed responsive-embed  sandbox-embed--full-width\"\n\tstyle=\"padding-top: 125%\"\ndata-block-name=\"coderpad-sandbox-embed\">\n\t<iframe src=\"https:\/\/embed.coderpad.io\/sandbox?question_id=194520&#038;use_question_button\" width=\"640\" height=\"800\" loading=\"lazy\" aria-label=\"Try out the CoderPad sandbox\"><\/iframe>\n<\/div>\n\n\n\n<p>This works because properties and attributes are both created at the same time with Lit.<\/p>\n\n\n\n<p>However, due to the period binding not being HTML standard, it comes with the side effect of having to use a Lit template in order to bind properties. This tends not to be a problem in applications &#8211; since many tend to use and compose components throughout their applications.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Array Rendering<\/h2>\n\n\n\n<p>In our article about vanilla JavaScript web components, we built a simple todo list. Let\u2019s take another look at that example, but this time using Lit for our component code. We\u2019ll get started with a parent <code>FormElement<\/code>, which will manage the data and user input.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">FormElement<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">LitElement<\/span> <\/span>{\n\u00a0 <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">get<\/span> properties() {\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">return<\/span> {\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-attr\">todoList<\/span>: {<span class=\"hljs-attr\">type<\/span>: <span class=\"hljs-built_in\">Array<\/span>},\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-attr\">inputVal<\/span>: {<span class=\"hljs-attr\">type<\/span>: <span class=\"hljs-built_in\">String<\/span>},\n\u00a0 \u00a0 \u00a0 };\n\u00a0 }\n\n\u00a0 _onSubmit(e) {\n\u00a0 \u00a0 \u00a0 e.preventDefault();\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">this<\/span>.todoList = &#91;...this.todoList, {<span class=\"hljs-attr\">name<\/span>: <span class=\"hljs-keyword\">this<\/span>.inputVal, <span class=\"hljs-attr\">completed<\/span>: <span class=\"hljs-literal\">false<\/span>}];\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">this<\/span>.inputVal = <span class=\"hljs-string\">''<\/span>;\n\u00a0 }\n\n\u00a0 <span class=\"hljs-comment\">\/\/ ...<\/span>\n\n\u00a0 render() {\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">return<\/span> html`<span class=\"xml\">\n\u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> @<span class=\"hljs-attr\">click<\/span>=<\/span><\/span><span class=\"hljs-subst\">${<span class=\"hljs-keyword\">this<\/span>.toggleAll}<\/span><span class=\"xml\"><span class=\"hljs-tag\">&gt;<\/span>Toggle all<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n\u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">form<\/span> @<span class=\"hljs-attr\">submit<\/span>=<\/span><\/span><span class=\"hljs-subst\">${<span class=\"hljs-keyword\">this<\/span>._onSubmit}<\/span><span class=\"xml\"><span class=\"hljs-tag\">&gt;<\/span>\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">.value<\/span>=<\/span><\/span><span class=\"hljs-subst\">${<span class=\"hljs-keyword\">this<\/span>.inputVal}<\/span><span class=\"xml\"><span class=\"hljs-tag\"> @<span class=\"hljs-attr\">change<\/span>=<\/span><\/span><span class=\"hljs-subst\">${<span class=\"hljs-keyword\">this<\/span>._onChange}<\/span><span class=\"xml\"><span class=\"hljs-tag\"> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\"<\/span> \/&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"submit\"<\/span>&gt;<\/span>Add<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n\u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">form<\/span>&gt;<\/span>\n\u00a0 \u00a0 <span class=\"hljs-comment\">&lt;!-- Notice the period in \".todos\" --&gt;<\/span>\n\u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">todo-component<\/span> <span class=\"hljs-attr\">.todos<\/span>=<\/span><\/span><span class=\"hljs-subst\">${<span class=\"hljs-keyword\">this<\/span>.todoList}<\/span><span class=\"xml\"><span class=\"hljs-tag\">&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">todo-component<\/span>&gt;<\/span>\n\u00a0 `<\/span>;\n\u00a0 }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now that we have a form that contains an array, an important question arises: how do we iterate through an array in order to create individual elements for a list?<\/p>\n\n\n\n<p>Well, while <a href=\"https:\/\/reactjs.org\/docs\/lists-and-keys.html\" target=\"_blank\" rel=\"noopener\">React has `Array.map<\/a>` and <a href=\"https:\/\/v3.vuejs.org\/guide\/list.html#mapping-an-array-to-elements-with-v-for\" target=\"_blank\" rel=\"noopener\">Vue has <code>v-for<\/code><\/a>, Lit uses a <code>repeat<\/code> function. Here\u2019s an example:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">TodoElement<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">LitElement<\/span> <\/span>{\n\u00a0 <span class=\"hljs-comment\">\/\/ ...<\/span>\n\n\u00a0 render() {\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">return<\/span> html`<span class=\"xml\">\n\u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ul<\/span>&gt;<\/span>\n\u00a0 \u00a0 \u00a0 <\/span><span class=\"hljs-subst\">${repeat(<span class=\"hljs-keyword\">this<\/span>.todos, (todo) =&gt; html`<span class=\"xml\">\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">li<\/span>&gt;<\/span>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"checkbox\"<\/span> <span class=\"hljs-attr\">.checked<\/span>=<\/span><\/span><span class=\"hljs-subst\">${todo.completed}<\/span><span class=\"xml\"><span class=\"hljs-tag\">\/&gt;<\/span>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <\/span><span class=\"hljs-subst\">${todo.name}<\/span><span class=\"xml\">\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">li<\/span>&gt;<\/span>\n\u00a0 \u00a0 \u00a0 `<\/span>)}<\/span><span class=\"xml\">\n\u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">ul<\/span>&gt;<\/span>\n\u00a0 `<\/span>;\n\u00a0 }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<div\n\tclass=\"sandbox-embed responsive-embed  sandbox-embed--full-width\"\n\tstyle=\"padding-top: 125%\"\ndata-block-name=\"coderpad-sandbox-embed\">\n\t<iframe src=\"https:\/\/embed.coderpad.io\/sandbox?question_id=181092&#038;use_question_button\" width=\"640\" height=\"800\" loading=\"lazy\" aria-label=\"Try out the CoderPad sandbox\"><\/iframe>\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Passing Functions<\/h2>\n\n\n\n<p>Before we step away from code to talk pros and cons about Lit itself (shh, spoilers!); let\u2019s take a look at a code sample that demonstrates many of the benefits over vanilla JavaScript web components we\u2019ve talked about today.<\/p>\n\n\n\n<p>Readers of the previous blog post will remember that when passing an array of objects to a web component, things looked pretty decent.<\/p>\n\n\n\n<p>It wasn\u2019t until we tried binding event listeners to an array of objects that things got complex (and messy). Between needing to manually create elements using <code>document<\/code>, dealing with <code>querySelector<\/code> to pass properties, manually calling \u201crender\u201d, and needing to implement a custom \u201cclear\u201d method &#8211; it was a messy experience.<\/p>\n\n\n\n<p>Let\u2019s see how Lit handles the job.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">TodoElement<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">LitElement<\/span> <\/span>{\n\u00a0 <span class=\"hljs-comment\">\/\/ ...<\/span>\n\u00a0\n\u00a0 render() {\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">const<\/span> headerText = <span class=\"hljs-keyword\">this<\/span>.todos\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 .filter(<span class=\"hljs-function\"><span class=\"hljs-params\">todo<\/span> =&gt;<\/span> todo.completed).length;\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">return<\/span> html`<span class=\"xml\">\n\u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span>&gt;<\/span><\/span><span class=\"hljs-subst\">${headerText}<\/span><span class=\"xml\"><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span>\n\u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ul<\/span>&gt;<\/span>\n\u00a0 \u00a0 \u00a0 <\/span><span class=\"hljs-subst\">${repeat(<span class=\"hljs-keyword\">this<\/span>.todos, (todo) =&gt; html`<span class=\"xml\">\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">li<\/span>&gt;<\/span>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"checkbox\"<\/span> @<span class=\"hljs-attr\">change<\/span>=<\/span><\/span><span class=\"hljs-subst\">${todo.onChange}<\/span><span class=\"xml\"><span class=\"hljs-tag\"> <span class=\"hljs-attr\">.checked<\/span>=<\/span><\/span><span class=\"hljs-subst\">${todo.completed}<\/span><span class=\"xml\"><span class=\"hljs-tag\">\/&gt;<\/span>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <\/span><span class=\"hljs-subst\">${todo.name}<\/span><span class=\"xml\">\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">li<\/span>&gt;<\/span>\n\u00a0 \u00a0 \u00a0 `<\/span>)}<\/span><span class=\"xml\">\n\u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">ul<\/span>&gt;<\/span>\n\u00a0 `<\/span>;\n\u00a0 }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<div\n\tclass=\"sandbox-embed responsive-embed  sandbox-embed--full-width\"\n\tstyle=\"padding-top: 125%\"\ndata-block-name=\"coderpad-sandbox-embed\">\n\t<iframe src=\"https:\/\/embed.coderpad.io\/sandbox?question_id=181093&#038;use_question_button\" width=\"640\" height=\"800\" loading=\"lazy\" aria-label=\"Try out the CoderPad sandbox\"><\/iframe>\n<\/div>\n\n\n\n<p>You will notice that we\u2019re using a <code>filter<\/code> within our <code>render<\/code> method. Because this logic is within the <code>render<\/code> method, it will run on every UI update. This is important to note in case you have expensive operations: you should avoid running those within the render method.<\/p>\n\n\n\n<p>Outside of this, however &#8211; that\u2019s all there is! It reads just like HTML would (with the added benefit of cleanup and prop passing), handles dynamic data, and more!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>The ability to leverage Lit in an application makes maintaining and improving a project easier than rolling web components yourself.<\/p>\n\n\n\n<p>Lit demonstrates significant growth in web components from the early days of <a href=\"http:\/\/polymer-project.org\/\" target=\"_blank\" rel=\"noopener\">Polymer<\/a>. This growth is in no small part due to the Lit team themselves, either!<\/p>\n\n\n\n<p>Before it was a fully fledged framework, the project started from the <code>lit-html<\/code> package, which was an offshoot of Polymer. The Polymer team was instrumental in standardizing the modern variant of web components.<\/p>\n\n\n\n<p>The ability to use Lit can strongly enhance web component development, but there are other options out there. Next time, we\u2019ll talk about what the competitors are doing, what the pros and cons of each are, and how you can make the best choice for your applications.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The ability to leverage Lit in an application makes maintaining and improving a project easier than rolling web components yourself.<\/p>\n","protected":false},"author":1,"featured_media":3353,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[9],"tags":[],"persona":[29],"blog-programming-language":[62],"keyword-cluster":[],"class_list":["post-3282","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development"],"acf":[],"_links":{"self":[{"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/posts\/3282","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/comments?post=3282"}],"version-history":[{"count":23,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/posts\/3282\/revisions"}],"predecessor-version":[{"id":34602,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/posts\/3282\/revisions\/34602"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/media\/3353"}],"wp:attachment":[{"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/media?parent=3282"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/categories?post=3282"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/tags?post=3282"},{"taxonomy":"persona","embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/persona?post=3282"},{"taxonomy":"blog-programming-language","embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/blog-programming-language?post=3282"},{"taxonomy":"keyword-cluster","embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/keyword-cluster?post=3282"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}