<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hasani Rogers</title>
    <description>The latest articles on DEV Community by Hasani Rogers (@deificarts).</description>
    <link>https://dev.to/deificarts</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F900522%2Fce8410b4-851e-439b-850c-d885398e6e0a.jpg</url>
      <title>DEV Community: Hasani Rogers</title>
      <link>https://dev.to/deificarts</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/deificarts"/>
    <language>en</language>
    <item>
      <title>Lit is better than React: Signals in Lit</title>
      <dc:creator>Hasani Rogers</dc:creator>
      <pubDate>Thu, 19 Feb 2026 15:36:27 +0000</pubDate>
      <link>https://dev.to/deificarts/lit-is-better-than-react-signals-in-lit-34gc</link>
      <guid>https://dev.to/deificarts/lit-is-better-than-react-signals-in-lit-34gc</guid>
      <description>&lt;p&gt;I once asked Gemini about the performance between React and Lit. It led me down a path to enlightenment. Here’s what it told me:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;React&lt;/th&gt;
&lt;th&gt;Lit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Generally slower (must build the VDOM)&lt;/td&gt;
&lt;td&gt;Very fast (native browser templates)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Higher (stores two copies of the DOM tree)&lt;/td&gt;
&lt;td&gt;Extremely low (no tree copies needed)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;~30KB+ (requires the React runtime)&lt;/td&gt;
&lt;td&gt;~5KB (uses native browser APIs)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Complexapps with assive state trees&lt;/td&gt;
&lt;td&gt;Design system, micro-frontends, performance-critical UI&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Lit is generally faster in raw benchmarks because it avoids the "diffing tax." It doesn't have to spend CPU cycles comparing two large JavaScript objects to find out what changed. React excels at developer ergonomics.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The gist of this is that while Lit performs better than React, React is familiar to devs and has a large ecosystem. That’s why it’s more popular. Gemini is not hallucinating here either. I asked Gemini to simplify data from &lt;a href="https://krausest.github.io/js-framework-benchmark/current.html" rel="noopener noreferrer"&gt;Krausest Benchmark&lt;/a&gt; where you can verify its findings. Lit was the clear winner.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Lit&lt;/th&gt;
&lt;th&gt;React (v19+)&lt;/th&gt;
&lt;th&gt;Winner&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Duration (Weighted Mean)&lt;/td&gt;
&lt;td&gt;~1.05 – 1.15&lt;/td&gt;
&lt;td&gt;~1.6 – 1.8&lt;/td&gt;
&lt;td&gt;Lit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Startup / Boot Time&lt;/td&gt;
&lt;td&gt;Fastest&lt;/td&gt;
&lt;td&gt;Moderate&lt;/td&gt;
&lt;td&gt;Lit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Swap 2 Rows&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;Slow (VDOM Overhead)&lt;/td&gt;
&lt;td&gt;Lit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory usage&lt;/td&gt;
&lt;td&gt;Minimal&lt;/td&gt;
&lt;td&gt;High (~10MB+)&lt;/td&gt;
&lt;td&gt;Lit&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Funny enough my personal preference for Lit has more to do with me wanting to remain as close to web standards as possible. The benefit of that is performance because what utilizes the platform will always win. At the same time though vanilla Web Components are cumbersome to write in my opinion. Hardly any one who writes web components does it vanilla. Lit is a leader here so I stuck with it.&lt;/p&gt;

&lt;p&gt;If you’re new to Lit you can check out my beginner level video on YouTube that shows you how to create a dark mode in it.&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/5RTU19mIbyI"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;Moving on.&lt;/p&gt;

&lt;h1&gt;
  
  
  The VDOM Paradigm is on its way out
&lt;/h1&gt;

&lt;p&gt;React relies on a VDOM and the VDOM is slow compared to the platform. That’s why Lit scores so well. But outside of that, the VDOM gets in the way in other cases as well. Mostly it gets in the way of the mental model many developers have about the code they write. The VDOM has 3 main reasons why it’s a problem that even React acknowledges:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Re-rendering&lt;/li&gt;
&lt;li&gt;Bundle Size Bloat&lt;/li&gt;
&lt;li&gt;Memory Usage&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;React deals with the entire dom tree, virtualizes it, does a diff then renders components that change. Bad React code can easily slow down an app fast as this process creates a lot of rendering overhead. Anyone who’s spent a good time with React professionally knows re-renders can become a pain. On top of that, React has to download the entire diff engine just to render one button. And this download is not cheap. It can be 30KB+. That's a considerable size bloat. Finally the VDOM keeps a copy of your UI in memory at all times, leading to considerable memory usage for larger applications.&lt;/p&gt;

&lt;p&gt;React’s way of trying to solve these problems is the React compiler. But in my opinion that’s more like a band-aid because it still relies on the VDOM. The real solution is to avoid using the VDOM all together. It was good for its time but the web has evolved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter Signals
&lt;/h2&gt;

&lt;p&gt;Signals is the new kid on the block and is the real solution for these issues. A signal is a kind of graph that creates a direct link between a piece of data and piece of text on the screen. When the data changes so does the UI and without all the VDOM overhead. Many JavaScript libraries are beginning to adopt Signals or are entirely based on it, like SolidJS.&lt;/p&gt;

&lt;p&gt;I’ll admit, when I first heard of SolidJS I went “oh great, yet another JavaScript library.” I never built anything in it but I did test integrating Web Components for some design systems with it. My experience was good. Later on I found out that you can actually create Web Components in SolidJS. This got my attention. I became more curious about Signals, something SolidJS implements, because of this. Turns out Signals are legit, so much so that there is &lt;a href="https://github.com/tc39/proposal-signals" rel="noopener noreferrer"&gt;a proposal to make Signals standard JavaScript&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So I definitely have an eye on SolidJS and I’ll be exploring it more in the future. &lt;/p&gt;

&lt;h2&gt;
  
  
  Whaaat, Signals in Lit?!
&lt;/h2&gt;

&lt;p&gt;Did you know that you can use a signal in Lit now? That’s right, we can thank Lit Labs for this. Lit Labs is a collection of experimental tech that works with Lit. It’s experimental now, so you probably want to avoid using this to build a production app. But as the proposal for signals becomes standard expect this to grow with it.&lt;/p&gt;

&lt;p&gt;Let's take a look at some code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LitElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;customElement&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lit/decorators.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SignalWatcher&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@lit-labs/signals&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// definte a variable as a signal outside your class&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;customElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-element&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;SignalWatcher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;LitElement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// use the SignalWatcher mixin&lt;/span&gt;
  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// use the watch function to subscribe to updates&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
      &amp;lt;button class="counter" @click=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_onClick&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; part="button"&amp;gt;
        Count is &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt; 
      &amp;lt;/button&amp;gt;
    `&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;_onClick&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// use the set method to update the signal&lt;/span&gt;
    &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://stackblitz.com/edit/signals-in-lit?file=src%2Fmy-element.ts" rel="noopener noreferrer"&gt;I recreated the count functionality on Stackblitz with signals&lt;/a&gt;. All you really need to do is use the Mixin to construct your element. Then instead of using a property decorator, use the signal function to create a const that you’ll get and set later. That’s it. Lit plays well with Signals. Checkout the &lt;a href="https://www.npmjs.com/package/@lit-labs/signals" rel="noopener noreferrer"&gt;@lit-labs/signals package docs&lt;/a&gt; for more details.&lt;/p&gt;

&lt;p&gt;I’m excited to see what may come of the Signals proposals and how this may evolve with it. Right now though if you are building microfrontends, design systems, or content driven apps with an Island Architecture, then going with Lit over React is a no brainer.  &lt;/p&gt;

</description>
      <category>lit</category>
      <category>signals</category>
      <category>webcomponents</category>
    </item>
    <item>
      <title>It’s time to get comfortable with web standards again</title>
      <dc:creator>Hasani Rogers</dc:creator>
      <pubDate>Mon, 08 Sep 2025 17:44:05 +0000</pubDate>
      <link>https://dev.to/deificarts/its-time-to-get-comfortable-with-web-standards-again-220</link>
      <guid>https://dev.to/deificarts/its-time-to-get-comfortable-with-web-standards-again-220</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Originally posted on &lt;a href="https://hasanirogers.me/blog/its-time-to-get-comfortable-with-web-standards-again/" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Typescript is ok though.&lt;/p&gt;

&lt;p&gt;The point of this blog is to highlight some mentalities I’ve noticed amongst developers. Especially React developers. React has become so ubiquitous that a lot of developers do not know how to think outside its model for developing components. Sometimes, developers are also wholly dependent on its eco system as well. I saw the term “React Brain” used the other day and it made me chuckle.&lt;/p&gt;

&lt;p&gt;This reminds me of the days when jQuery was king. jQuery was great for its time but it solved a problem browsers eventually caught up with. It took 10 something years but eventually people realized they didn’t need jQuery anymore. Not only that, jQuery was actually bloated and convoluted compared to standard JavaScript. Browsers could eventually do the things we relied on jQuery to do. Querying the dom became easy with querySelector. Fetching data with ajax was replaced by the Fetch API. The cross browser issues it solved for were mostly hammered out by fairly consistent browser standards.&lt;/p&gt;

&lt;p&gt;Now let's look at React. React was released by Facebook (now Meta) in 2013. Its goal was to reduce UI complexity. It did this by introducing a declarative UI based component architecture. This was revolutionary at the time. Nowadays a declarative component based architecture is the standard amongst UI libraries. So much so that many developers make the mistake of thinking of Web Component as “another UI library” with “extra flavors they aren’t used to or need to learn”. They do this because to them, React is the standard and “other” component architectures are just “alternatives” to them like Angular or Svelte or something.&lt;/p&gt;

&lt;p&gt;This thinking behind UI development is as mistaken as thinking of jQuery as the “standard”. jQuery may have felt like the standard, because it was so common, but the reality is that in its time it had “alternatives” too like Prototype and MooTools. They all fell to something superior, browser standards. React will eventually do the same. At least when it comes to component architecture. Let me highlight a practical example of how the component architecture of Web Components is simpler to work with than React.&lt;/p&gt;

&lt;p&gt;I was working with Swiper JS to display some real estate properties. I had a pagination component control the main swiper container from children nested multiple levels deep. Typically in React if you want communication between components nested deep in a component you use something like context or some state management tool to avoid prop drilling. With web components, you don’t need any of this. All I had to do was use &lt;code&gt;.closest&lt;/code&gt; to find the closest swiper-container in the dom within my web component. I was then able to access the swiper instance in my component and control the pagination by this alone. This is one of many examples of how web component APIs make things in the React world non issues inherently. &lt;/p&gt;

&lt;p&gt;Another thing is styling. Web Components have a shadow dom and thus your styles are scoped to that shadow dom by default. You can write simple contextual styles without the need of utility libs like tailwind, styled components, or css modules, etc. You don’t even need any JavaScript to use a custom element. Just write the custom html tag, it's ok to make up your own html now. This approach automatically creates a scope (like a block in bem), for you to select your styles against. For example, write the tag &lt;code&gt;&amp;lt;blog-header&amp;gt;&lt;/code&gt; and then select it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;blog-header&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* your styles */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The beauty of a custom element is that it’s like selecting a low specificity ID that supports multiple instances. It’s unique but safe from specificity problems. This approach to styling is so revolutionary that I’ve created my own styling methodology around it called &lt;a href="https://hasanirogers.me/blog/the-new-bem-for-custom-elements-cea" rel="noopener noreferrer"&gt;CEA&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Web Components should be thought of in the context of standard JS with progressive enhancements. You can use any JavaScript lib that supports standard JS with Web Components. This includes state management tools like Zustand or animation libs like Motion. You don’t need a web component version of any of these libraries because Web Components &lt;strong&gt;is standard JavaScript&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;With Web Components there’s another intrinsic advantage. A return to focusing on describing the document semantically. Like my &lt;code&gt;&amp;lt;blog-header&amp;gt;&lt;/code&gt; example, a well described accessible UI can use any html you make up along with HTML5 semantics. The browsers will treat your custom elements generically like a div. Consider this, you have a structure that looks like something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;main&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;blog-header&amp;gt;&lt;/span&gt;…content&lt;span class="nt"&gt;&amp;lt;/blog-header&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;article&amp;gt;&lt;/span&gt;…content&lt;span class="nt"&gt;&amp;lt;article&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This improves DX because the semantics alone describes exactly what you mean. You don’t need to write wrapper classes for &lt;code&gt;&amp;lt;blog-header&amp;gt;&lt;/code&gt;, the element is your wrapper. Better yet you don’t need to write a bunch of utility classes either. Classes have semantic meaning and were never intended to be magic strings for just styling. &lt;/p&gt;

&lt;p&gt;Because a web component’s architecture is bound to a custom element, they are also perfect for content management systems like AEM or WordPress. In AEM, simply write the HTML of your custom element in your AEM component’s HTL. There is no need for more complex archetypes with slow and problematic webpack configurations that try to marry React and AEM components. In WordPress just write your custom element as HTML in your Gutenberg blocks or templates in PHP, or both.&lt;/p&gt;

&lt;p&gt;Since Web Components don’t use a virtual dom they are often faster. Take Lit for example, a library that makes writing Web Components simple, when it renders it only renders changes in the dom directly. You don’t need to think about component trees and re-rendering issues with things like context in React.&lt;/p&gt;

&lt;p&gt;Finally, you don’t even have to completely ditch React. You can use web components with React. Doing this is pretty simple these days now that React 19 and Next 15 finally added support for custom elements. I’m on a project at my day job where we used web components in a Next JS app to write some custom animations with motion. We did this so that our web components could be reused in a variety of tech stacks.&lt;/p&gt;

&lt;p&gt;Almost every complaint I have ever seen about web components comes from the dev having the wrong mental model of web components because they think of them as if they were components in one of their frameworks or libraries. It's either that or framework makers themselves complaining because their framework deviates from using the DOM as a model for a component. &lt;/p&gt;

&lt;p&gt;All the typical complaints like “web components aren't composable” or “doesn’t support rich data as property values” or “you have to use shadow dom” are false. They come from people who are inexperienced with working with web components. As loud as this voice is, it's wrong.&lt;/p&gt;

&lt;p&gt;As far as framework makers goes, how many devs are actually trying to create their own framework? A framework will never trump standards implemented in a browser. Your job is to serve the browser because that's what end users actually interact with. Not your framework. Why fight this?&lt;/p&gt;

&lt;p&gt;Web Components are a return to basics and web standards. You should be thinking of the document semantically again and enhancing it with custom elements. Write a web component when you need to have some behavior around a custom element. Style them appropriately with CEA. Web components bring a clear and standards based component architecture to the front end when working with content management systems with little tooling outside of JS Module support. &lt;/p&gt;

&lt;p&gt;Libraries and Frameworks like Next/React are great with handling data driven applications. However I’ve found that Libraries like Lit offer simple approaches to building data driven UIs with web components as well, especially when combined with tools like Zustand and Astro.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Web Components and SSR with Next.js</title>
      <dc:creator>Hasani Rogers</dc:creator>
      <pubDate>Fri, 09 Aug 2024 02:57:50 +0000</pubDate>
      <link>https://dev.to/deificarts/web-components-and-ssr-with-nextjs-13b4</link>
      <guid>https://dev.to/deificarts/web-components-and-ssr-with-nextjs-13b4</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Originally posted on &lt;a href="https://blog.hasanirogers.me/2024/08/web-components-and-ssr-with-nextjs.html" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'm an advocate of Web Components because I believe presentational components should be written once and used through any tech stack. This has been a dream of mine for years. Web Components achieve this but not without caveats. I've written about using Web Components in libraries like &lt;a href="https://kemet.dev/libraries/react/" rel="noopener noreferrer"&gt;React&lt;/a&gt; and &lt;a href="https://kemet.dev/libraries/angular/" rel="noopener noreferrer"&gt;Angular&lt;/a&gt; before. The most complicated caveat tends to be React but React 19 is adding native support for them. One thing I have yet to write about is how to handle Web Components in an SSR framework like Next.js. This is a tricky subject because SSR support for a Web Components are weak at the moment.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;SSR has html rendered server side. That html is then served to the browser. Web Components typically make use of a custom Shadow Dom that's initialized by JavaScript. This is a process that takes place on the client. How does the server then know how to render the Shadow Dom of a component? The short answer is that it can't. The long answer is that there is an emerging technology called &lt;a href="https://developer.chrome.com/docs/css-ui/declarative-shadow-dom" rel="noopener noreferrer"&gt;Declarative Shadow Dom&lt;/a&gt; that solves the problem. DSD is in its early stages and support for it is buggy at best in a real application. Definitely not ready for use in production applications in my opinion.&lt;/p&gt;

&lt;p&gt;So how do we reliably use Web Components in an SSR framework now in 2024 then? The answer to that is that you need to avoid trying to render the Shadow Dom of a component on the server and have the client do the work. This is totally achievable. I found examples of doing this lacking though, hence why I'm writing this blog. In this article I'll be detailing how I did this with Next.js, but the principals here can be used theoretically in any SSR framework.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Main Idea
&lt;/h2&gt;

&lt;p&gt;The main idea is that we need to generate a JavaScript bundle that will execute only on the client so that the Shadow Dom of an Web Component will be hydrated on the client. We don't want the server trying to do it. It's not that complicated as we dive into it more. We'll need:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A bundler that takes components and bundle them into a JavaScript file used in our build&lt;/li&gt;
&lt;li&gt;A way to load that bundle as a standard script rather than with the rest of the JavaScript modules that get executed server side&lt;/li&gt;
&lt;li&gt;This is optional but if you're using TypeScript you'll need a way to tell your app to recognize the custom Web Components in JSX.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Breaking it down in Next.js
&lt;/h2&gt;

&lt;p&gt;I put together a &lt;a href="https://github.com/hasanirogers/web-components-with-nextjs" rel="noopener noreferrer"&gt;repo that demonstrates how to achieve the three steps in Next.js&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Bundler
&lt;/h3&gt;

&lt;p&gt;My bundler of choice is &lt;a href="https://rollupjs.org/" rel="noopener noreferrer"&gt;Rollup.js&lt;/a&gt;. It's the thing that powers &lt;a href="https://vitejs.dev/" rel="noopener noreferrer"&gt;Vite&lt;/a&gt; and I think it's great. So in my repo you'll find the following &lt;code&gt;rollup.config.mjs&lt;/code&gt; file in the app root &lt;a href="https://github.com/hasanirogers/web-components-with-nextjs/blob/main/rollup.config.mjs" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@rollup/plugin-node-resolve&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;multi&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@rollup/plugin-multi-entry&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;commonjs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@rollup/plugin-commonjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;terser&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@rollup/plugin-terser&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// You'll need to create an object per library bundle&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;src/app/bundles/shoelace.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;public/shoelace/shoelace.bundle.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shoelace-bundle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;umd&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;sourcemap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;commonjs&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;multi&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;terser&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;src/app/bundles/kemet.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;public/kemet/kemet.bundle.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;kemet-bundle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;umd&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;sourcemap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;commonjs&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;multi&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;terser&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file creates two bundles. One is for &lt;a href="https://shoelace.style" rel="noopener noreferrer"&gt;the popular Shoelace component library&lt;/a&gt;. The other is for &lt;a href="https://kemet.dev" rel="noopener noreferrer"&gt;my design system Kemet UI&lt;/a&gt;. The file is pretty simple and should be mostly self explanatory if you've worked with bundlers before. The most important thing here is that you place your bundle in the public directory so that it can be loaded as a static assets on the front end.&lt;/p&gt;

&lt;h3&gt;
  
  
  Loading the Script
&lt;/h3&gt;

&lt;p&gt;The reason for creating the bundle is to include it as a script when the page is rendered on the client. SSR frameworks like Next.js &lt;em&gt;should&lt;/em&gt; do a shallow render of the Web Component. This means that it'll render the Light Dom of the Web Component. This is enough for our bundle to find the light dom, execute, then hydrate it with its corresponding Shadow Dom.&lt;/p&gt;

&lt;p&gt;In my Next.js repo I have a default layout file called &lt;code&gt;default.tsx&lt;/code&gt; &lt;a href="https://github.com/hasanirogers/web-components-with-nextjs/blob/main/src/app/layouts/default.tsx" rel="noopener noreferrer"&gt;here&lt;/a&gt;. It looks like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Script&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/script&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;paths&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../shared/paths&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../shared/styles&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../styles/shared.scss&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;RootLayout&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;Readonly&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Script&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shoelaceBundle&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Script&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kemetBundle&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the &lt;code&gt;&amp;lt;Script /&amp;gt;&lt;/code&gt; components. Their &lt;code&gt;src&lt;/code&gt; prop is set to paths can be found in the &lt;a href="https://github.com/hasanirogers/web-components-with-nextjs/blob/main/src/app/shared/paths.ts" rel="noopener noreferrer"&gt;paths.ts&lt;/a&gt; file. This path should match the output of the bundle file created in your bundler.&lt;/p&gt;

&lt;p&gt;So as long as you're generating a bundle and calling it on the front end, you're good and your Web Components should work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Supporting JSX in TypeScript
&lt;/h3&gt;

&lt;p&gt;Great. We got Web Components to work in the browser. At this point you'll notice that your editor is probably complaining about the Web Components. This is because TypeScript doesn't know what the components are. Even if the library you're using ships with declarations for Typescript, since we're loading the bundle on the front end your editor has no access to this information. A not so ideal work around is to simply declare each web component to Typescript. In my repo I do this with &lt;code&gt;declarations.d.ts&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;declare&lt;/span&gt; &lt;span class="nx"&gt;namespace&lt;/span&gt; &lt;span class="nx"&gt;JSX&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IntrinsicElements&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;kemet-button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  A Note on React 19
&lt;/h3&gt;

&lt;p&gt;Before React 19 you needed a wrapper to get Web Components to fully work in React. This is no longer the case in React 19. My demo repo uses Next.js 15, which comes with React 19 but is in beta at the time of this writing. So the code there, specifically in &lt;code&gt;layouts/default.tsx&lt;/code&gt;, is written in the style of React but doesn't use a wrapper. You'll probably want to use a stable version of Next.js. That unfortunately means you can't follow this code using a &lt;code&gt;useState&lt;/code&gt; for the &lt;code&gt;opened&lt;/code&gt; prop and handling events declaratively such as with &lt;code&gt;onkemet-drawer-opened&lt;/code&gt;. On top of that wrappers don't work with SSR.&lt;/p&gt;

&lt;p&gt;All is not lost though, to work with events and rich data with Web Components in Next.js you'll need to do it the classic way and get a ref to the Web Component. From there you can use &lt;code&gt;addEventListener&lt;/code&gt; and assign objects/arrays/booleans directly to the component. You can find example of this in the &lt;a href="https://github.com/hasanirogers/web-components-with-nextjs/blob/main/src/app/layouts/legacy.tsx" rel="noopener noreferrer"&gt;legacy.tsx file&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;There's more to the repo but these points are what you really need to know for now. Feel free to reach out with questions! Personally I can't wait for the day we see an SSR Frameworks built on top of &lt;a href="https://www.npmjs.com/package/@lit-labs/ssr" rel="noopener noreferrer"&gt;this&lt;/a&gt; for Web Components. But we're not quite there yet.&lt;/p&gt;

</description>
      <category>webcomponents</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Lit and State Management with Zustand</title>
      <dc:creator>Hasani Rogers</dc:creator>
      <pubDate>Mon, 08 Jul 2024 10:01:52 +0000</pubDate>
      <link>https://dev.to/deificarts/lit-and-state-management-with-zustand-kf</link>
      <guid>https://dev.to/deificarts/lit-and-state-management-with-zustand-kf</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Originally posted on &lt;a href="https://blog.hasanirogers.me/2024/07/lit-and-state-management-with-zustand.html" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I come across a lot of developers who think that web components are for trivial things like buttons and input components. The reality is that you can build an entire application with web components. One of the most important pieces to an application's UI is state management. There are a lot of state management tools out there, like redux and mobx, that work perfectly fine with web components. These tools are a bit cumbersome though and many developers prefer more simple solutions for more simple applications. Enter Zustand, a "bare necessities" state management tool that's making strides in the React world.&lt;/p&gt;

&lt;p&gt;I was (sorta) recently introduced to Zustand on a React project I'm working on. I thought it was pretty neat. So naturally I was curious about how you would use it with an application built with Lit and web components. I new it was possible because the Zustand docs has a "Using Zustand without React" section. But I found absolutely no resources out there on how to accomplish using it with Lit. Thus I decided to experiment with an &lt;a href="https://stackblitz.com/edit/zustand-lit-todo" rel="noopener noreferrer"&gt;Stackblitz todo app&lt;/a&gt;. I had a lot of fun! Since I could find no resources out there I also decided this would be a good topic to blog about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting the basics down
&lt;/h2&gt;

&lt;p&gt;The first thing to know about using Zustand outside of React is that instead of hooks, which is a React thing, you'll be working with it's API utilities. These are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;getState&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;setState&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;subscribe&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;getInitialState&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I found that &lt;code&gt;getInitialState&lt;/code&gt; and &lt;code&gt;subcribe&lt;/code&gt; was the most critical for working with Lit. I've put together &lt;a href="https://stackblitz.com/edit/zustand-lit" rel="noopener noreferrer"&gt;a basic demo app illustrating how to work with Lit and Zustand&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This app only has one store and one Lit component. All it does is count bears and remove the count. I made this because I want to demonstrate the basic principals behind using Lit and Zustand. The most important thing to know in this app is lines 75-83 of my-element.ts, the constructor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// we need to subscribe to appStore state changes to rerender the UI when state has been updated&lt;/span&gt;
  &lt;span class="nx"&gt;appStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prevState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// update bears locally&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bears&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bears&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the constructor you are going to want to listen to your store by subscribing to it. From there you want to update data in Lit based off state changes. In this example we update the bears count in Lit (&lt;code&gt;this.bears&lt;/code&gt;) with the bears count in state (&lt;code&gt;state.bears&lt;/code&gt;) on lines 81. We want to do this because as state changes we need to render a new UI by triggering an update.&lt;/p&gt;

&lt;p&gt;The next thing to know is that we want to store the state of the store in a Lit property. In my case I've called this &lt;code&gt;appState&lt;/code&gt; here and I've used &lt;code&gt;getInitialState&lt;/code&gt; from Zustand API utilities to populate it. This way we can run methods in our store by simply referencing our &lt;code&gt;appState&lt;/code&gt; like I've done with the &lt;code&gt;handleAdd&lt;/code&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;handleAdd&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increasePopulation&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;increasePopulation&lt;/code&gt; is a method in the store taken directly from Zustand docs. As for the Store itself, we don't need to do anything special for Lit here. Set this up as you would setup Zustand in an vanilla JavaScript app according to the Zustand docs.&lt;/p&gt;

&lt;p&gt;Everything else in this app should be self explanatory if you know Lit.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Todo App
&lt;/h2&gt;

&lt;p&gt;My todo app takes the principals I've outlined above and extend upon them a bit. In this app we're using the subscribe and appState pattenrs I've shown you. And example of the appState can be found in &lt;code&gt;todo-app.ts&lt;/code&gt; on lines 59-60.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;property&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;todoState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ITodoStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;todoStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInitialState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we call it &lt;code&gt;todoState&lt;/code&gt;. In the constructor you'll also see our subscribe.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;todoStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;numberOfItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todoList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this app though we're keeping track of the number of todo items by getting the length of &lt;code&gt;todoList&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding a todo
&lt;/h3&gt;

&lt;p&gt;The responsibility of adding todos is handled by the &lt;code&gt;todo-input&lt;/code&gt; component. This component makes use of the &lt;code&gt;addTodo&lt;/code&gt; method in the store to add a todo. In our app a todo is represented by an object that has two properties, value and checked. They're self explanatory. To add the todo we turn to our store. The code looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newTodo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;todoList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todoList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newTodo&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;})),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On line 20 of &lt;code&gt;store/todo.ts&lt;/code&gt;. Adding the todo is mostly standard Zustand stuff. We use &lt;code&gt;set&lt;/code&gt;, which gives us state and we then return the new state of &lt;code&gt;todoList&lt;/code&gt; by spreading in the current state + &lt;code&gt;newTodo&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Updating State
&lt;/h3&gt;

&lt;p&gt;Where things get interesting is handling how to update state. An example of updating state is updating the checked state of a todo. I've opted to use lodash for this. So lets look at the &lt;code&gt;todoToggle&lt;/code&gt; method on line 26 to understand how to achieve this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;todoToggle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;lodashSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`todoList[&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;].checked`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todoList&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;checked&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once again we're using &lt;code&gt;set&lt;/code&gt;. But this time we also have &lt;code&gt;lodashSet&lt;/code&gt; which takes an object, a path to thing you want to set, and a value. In our case we give state, a path to the checked property, and value to be the opposite of the last checked state for a toggle effect. We then return the new state object.&lt;/p&gt;

&lt;p&gt;But there's a catch, we've updated state and Lit doesn't know about it. The state has been updated but when we do this we need to tell Lit to update the UI because the state is different. You can do that by using &lt;code&gt;requestUpdate&lt;/code&gt;. Turn to lines 87-90 in &lt;code&gt;todo-list.ts&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;handleChecked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todoState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;todoToggle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requestUpdate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we call &lt;code&gt;todoToggle&lt;/code&gt; we also need to request an update in Lit to re-render the UI. The same is true for &lt;code&gt;removeTodo&lt;/code&gt; which uses lodash's &lt;code&gt;pullAt&lt;/code&gt; to remove a todo in the todoList array.&lt;/p&gt;

&lt;p&gt;That's about it folks. I hope you build something more cool than my Todo app with Lit and Zustand. Here's the Todo app for reference:&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://stackblitz.com/edit/zustand-lit-todo?embed=1&amp;amp;file=src%2Ftodo-app.ts&amp;amp;view=editor" width="100%" height="500"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>lit</category>
      <category>zustand</category>
      <category>webcomponents</category>
    </item>
    <item>
      <title>The new BEM for Custom Elements, CEA</title>
      <dc:creator>Hasani Rogers</dc:creator>
      <pubDate>Fri, 09 Sep 2022 11:57:28 +0000</pubDate>
      <link>https://dev.to/deificarts/the-new-bem-for-custom-elements-cea-4830</link>
      <guid>https://dev.to/deificarts/the-new-bem-for-custom-elements-cea-4830</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Originally posted on &lt;a href="https://blog.hasanirogers.me/2022/09/the-new-bem-for-custom-elements-cea.html" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Web Components have been around for a while. But with the plethora of tech out there, like React and Angular, the average developer simply doesn't have that much experience with them. So best practices and methodologies are lacking. We definitely need some formal approaches to developing with Web Components. They are designed to work together with, and not replace, Front End libraries. Developers need more exposure to web components and &lt;a href="https://web.dev/custom-elements-v1/" rel="noopener noreferrer"&gt;custom elements&lt;/a&gt; in general.&lt;/p&gt;

&lt;p&gt;The thing I've notice that developers struggle with the most when being introduced to Web Components is the concept of the Shadow Dom. Developers are used to tools that fake what a Shadow Down does naturally. You find this in all sorts of technology. Take styling in React for example. React has something called &lt;a href="https://github.com/css-modules/css-modules" rel="noopener noreferrer"&gt;CSS Modules&lt;/a&gt; which allows you to scope CSS to a component. It's cool but the issue it solves for is a non-issue in the Web Component world. Constructed style sheets are &lt;em&gt;inherently&lt;/em&gt; scoped. Then there is SCSS. SCSS is nice but it assumes the perspective of working in one massive light DOM tree. The "light DOM" is the regular DOM that developers are used to. SCSS is based on this. Think of nesting selectors for example. You create a kind of "scope" by putting once selector as a child of another. This was incredibly popular and useful when BEM emerged.&lt;/p&gt;

&lt;p&gt;If you've been living under a rock BEM is an acronym for Block, Element, Modifier. Developed by Yandex almost a decade ago, BEM is a naming convention that "componenitizes" styles (yes I just made the word componentizes). BEM was brilliant. But like SCSS it assumed one light DOM. In the world of the light DOM BEM and SCSS makes sense. But in the Web Component world we have &lt;em&gt;native&lt;/em&gt; encapsulation. Naming conventions like BEM aren't nearly as useful and it takes developers a while to understand this. Hell it took me a while. Just two years ago when I wrote the drawer component for my UI Library I still used BEM. But over time I started to learn my own methodology that makes more sense for Web Components. Well today I'm here to name that methodology and formalize it in writing so that others may have the same insight as me! &lt;/p&gt;

&lt;h2&gt;
  
  
  CEA is my BEM replacement for Custom Elements.
&lt;/h2&gt;

&lt;p&gt;CEA is an acronym for Component, Element, Attribute. It sorta has a correspondence with BEM but with one major difference. &lt;em&gt;Use attributes to style the state of a component.&lt;/em&gt; This is huge because we're so used to styling with classes that a lot of us don't know how to think outside the box on this one. However the primary thing I've noticed is that your component will usually have some kind of state, represented by a property, which can be reflected as an attribute. Your style "modifiers" thus correspond to this state that you access via attributes. I'll demonstrate this more clearly in a second. For now, let me define some terms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Component&lt;/strong&gt;: This is your main scope. Think of it as Block in BEM. Instead of a establishing a class name for a block, you simply select by host element or custom element name however. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Element&lt;/strong&gt;: This is any element that lives in the Shadow Dom of your component or a child of your component. You may select by class name if you wish. You'll often find that literally selecting the element works just as well however.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Atrribute&lt;/strong&gt;: These are attributes on your host component. They represent a &lt;em&gt;state&lt;/em&gt; of the component such as &lt;code&gt;disabled&lt;/code&gt; on a button or &lt;code&gt;opened&lt;/code&gt; on a dialog. &lt;/p&gt;

&lt;p&gt;Of course this can all be illustrated better by example, so lets dive into it. FYI the following example is going to use &lt;a href="https://lit.dev/" rel="noopener noreferrer"&gt;Google Lit&lt;/a&gt; which I highly recommend if you're writing web components.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn by example
&lt;/h2&gt;

&lt;p&gt;Let say you're working on a button. Your button has two styles to it. Standard and outlined. Now you could write a modifier class for the outline version of the button based on an outlined property like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="s2"&gt;`
        .button {
          ...
        }

        .button--outlined {
          ...
        }
      `&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
      &amp;lt;a href=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; class="button &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outlined&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button--outlined&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" role="button"&amp;gt;
        &amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;
      &amp;lt;/a&amp;gt;
    `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, you could simplify the code under CEA and &lt;em&gt;style the outlined attribute directly&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="s2"&gt;`
        a {
          ...
        }

        :host([outlined]) a {
          ...
        }
      `&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
      &amp;lt;a href=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; role="button"&amp;gt;
        &amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;
      &amp;lt;/a&amp;gt;
    `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that in CEA we take advantage of the &lt;code&gt;:host&lt;/code&gt; selector. We also don't really need a class for the button. We can simply select it by the &lt;em&gt;element&lt;/em&gt; as we're not going to have more than one &lt;code&gt;a&lt;/code&gt; in the component. The result is that the markup in our template is much cleaner and easier to read and maintain. In my opinion the styles become easier to read too. That's the primary of benefit of CEA. &lt;/p&gt;

&lt;h2&gt;
  
  
  Reflecting Attributes with Lit
&lt;/h2&gt;

&lt;p&gt;Lit has a nice feature that allows you to reflect the value of property as an attribute on your host component. CEA becomes very useful when engaging in this practice. Instead of the user of the component controlling for an attribute like &lt;em&gt;opened&lt;/em&gt; you can have Lit automatically apply the attribute when something opens or closes. Let's take a modal for example. The following modal will close when the user clicks outside of the modal. To close the modal we control the &lt;code&gt;opened&lt;/code&gt; property by setting it to &lt;code&gt;false&lt;/code&gt;. This will automatically remove the &lt;code&gt;opened&lt;/code&gt; attribute from the custom element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExampleModal&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="s2"&gt;`
        *,
        *::before,
        *::after {
          box-sizing: border-box;
        }

        :host {
          position: fixed;
          display: flex;
          top: 0;
          left: 0;
          z-index: 99;
          width: 100%;
          height: 100vh;
          visibility: hidden;
          backface-visibility: hidden;
        }

        /* in CEA we style for attributes on the host element as they correspond to "state"
        :host([opened]) {
          visibility: visible;
        }

        /* .content and .overlay elements where given class names because they are both 
         * divs and we need to distinguish between them
         */ 
        .content {
          position: relative;
          z-index: 3;
          margin: auto;
        }

        :host([mobile]) .content {
          padding: 1rem;
        }

        .overlay {
          position: fixed;
          width: 100%;
          height: 100%;
          visibility: hidden;
          top: 0;
          left: 0;
          z-index: 1;
          opacity: 0;
          background: rgba(0,0,0,0.2);
          transition: all 0.3s;
        }

        :host([opened]) .overlay {
          opacity: 1;
          visibility: visible;
        }
      `&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;opened&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// set reflect to true so you may style the attribute&lt;/span&gt;
        &lt;span class="na"&gt;reflect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;breakpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;mobile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;reflect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;opened&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;breakpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;600px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;firstUpdated&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;opened&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tagName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;example-modal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// this will automatically remove the opened attribute from the host element &lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;opened&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resize&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isMobile&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;updated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prevProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;prevProps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;opened&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;opened&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Fires when the modal opens&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CustomEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;example-modal-opened&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;bubbles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;composed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prevProps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;opened&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;opened&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Fires when the modal closes&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CustomEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;example-modal-closed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;bubbles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;composed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isMobile&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
      &amp;lt;div class="content"&amp;gt;
        &amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div class="overlay"&amp;gt;&amp;lt;/div&amp;gt;
    `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;isMobile&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* 
     * here we represent mobile as state. this way users can easily 
     * control the breakpoint where the modal is considered to be mobile 
     */&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mediaQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;matchMedia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`(max-width: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;breakpoint&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mobile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mediaQuery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On line 89 we manually close the modal by setting &lt;code&gt;opened&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt;. With CEA, instead of writing logic to toggle classes, we simply write a selector for the &lt;code&gt;opened&lt;/code&gt; attribute. Since it's a boolean if &lt;code&gt;opened&lt;/code&gt; is not on the component it's automatically considered closed.&lt;/p&gt;

&lt;h2&gt;
  
  
  You can write CEA without Web Components
&lt;/h2&gt;

&lt;p&gt;CEA works even when you're not writing formal Web Components with a Shadow Dom. It works with custom elements in general. The most groundbreaking thing about this is thinking in terms of Custom Elements. Custom Elements is a standard browser API, so they work with Vanilla JavaScript and even React. Unlike when BEM was created, we now have the ability to make up our own HTML and attributes. &lt;a href="https://caniuse.com/?search=custom%20elements" rel="noopener noreferrer"&gt;All modern browser support autonomous custom elements&lt;/a&gt;. So CEA still applies, you'll just need to write more JavaScript for your components. Here's an example Pen that I wrote using CEA in Vanilla JavaScript.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://stackblitz.com/edit/cea-vanilla?embed=1&amp;amp;file=index.html" width="100%" height="500"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;You'll see that the methodology is the same while working with Vanilla components. The code is just a little bit more complex. Instead of using the &lt;code&gt;host&lt;/code&gt; selector we style our custom element directly. That is the &lt;em&gt;Component&lt;/em&gt; part of CEA. We then style our children under the scope of the component element, that is the &lt;em&gt;Element&lt;/em&gt; part of CEA. Finally we write JavaScript to manually handle the status (opened/closed) of the component as an attribute, that is the &lt;em&gt;attribute&lt;/em&gt; part of CEA. It's super straight forward when you get pass a class being the primary way you style something. You can even use CEA in React if you wanted too, although you'd have to manually handle the attributes like with Vanilla components.&lt;/p&gt;

&lt;p&gt;If you're working with React, the CEA methodology is the same. You'll simply need to use custom elements and attributes. The syntax for toggling an boolean attribute is a little weird. But it works. Here's an example.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://stackblitz.com/edit/cea-react?embed=1&amp;amp;file=src/components/PopUp.js" width="100%" height="500"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Notice how the attribute corresponds to state.&lt;/p&gt;

&lt;p&gt;I'm gonna end this article with a note. CEA is pronounced (See - ah). It's actually wordplay for the Egyptian god &lt;a href="https://en.wikipedia.org/wiki/Sia_(god)" rel="noopener noreferrer"&gt;Sia&lt;/a&gt;. I thought it was fitting because Sia is the deification of &lt;em&gt;perception&lt;/em&gt; which thinking in CEA takes a shift in &lt;em&gt;perception&lt;/em&gt;. Ha!&lt;/p&gt;

</description>
      <category>bem</category>
      <category>webcomponents</category>
      <category>css</category>
      <category>html</category>
    </item>
    <item>
      <title>The MEWN Stack with Shoelace</title>
      <dc:creator>Hasani Rogers</dc:creator>
      <pubDate>Sat, 30 Jul 2022 14:24:00 +0000</pubDate>
      <link>https://dev.to/deificarts/the-mewn-stack-with-shoelace-lbg</link>
      <guid>https://dev.to/deificarts/the-mewn-stack-with-shoelace-lbg</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Originally posted on &lt;a href="https://blog.hasanirogers.me/2022/04/the-mewn-stack-with-shoelace.html" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'm surprised. Before writing this article I googled "MEWN stack" and got zero relevant results. I mean I know someone out there has probably create something on a MEWN stack but nobody is using the term? Funny. Anyways, first there was MEAN who's primary front end technology was Angular. Then MERN came around once React got popular. Now there's MEWN with web components. I'll let the internet decide how to pronounce it. Now on on the meat.&lt;/p&gt;

&lt;p&gt;I recently was asked to do a simple landing page generator app. The app will be used for users who will scan a QR code and get taken to a page with audio or video that plays. The client needs to manage the landing pages via a dashboard. Since I'm a Front End Developer with a lot of experience in WordPress I tend to go to WordPress when I need server side tech. WordPress was overkill for this project though. Each landing page only need a small amount of info. I do have experience with Express.js and MongoDB though. So I said screw it, I'm gonna build an app with Express, MongoDB, Web Components, and Node.&lt;/p&gt;

&lt;p&gt;You can &lt;a href="https://github.com/hasanirogers/indelible-landing" rel="noopener noreferrer"&gt;find a work in progress repo for the entire app at my Github&lt;/a&gt;. However since the client needs exclusive access to it to manage the pages I won't be posting a link to the live app. Instead in this article I'll review concepts with examples of how to execute the code. Read on to learn how to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Roll out an generated Express app&lt;/li&gt;
&lt;li&gt;Configure Express to connect to a remote MongoBD on Atlas&lt;/li&gt;
&lt;li&gt;Setup a database model with Mongoose&lt;/li&gt;
&lt;li&gt;Add server side routes&lt;/li&gt;
&lt;li&gt;Use EJS templating to render the server side page&lt;/li&gt;
&lt;li&gt;Review various web components that make up the front end&lt;/li&gt;
&lt;li&gt;Setup a front end build chain with Rollup&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Roll out an generated Express app
&lt;/h2&gt;

&lt;p&gt;Generating an Express.js app is easy. Just run &lt;code&gt;npx express-generator --view=ejs&lt;/code&gt;. EJS is the name of the view engine we're gonna use to render pages. More on that later. After you run the generator do a &lt;code&gt;npm install&lt;/code&gt;. You'll then be able to start the app with &lt;code&gt;npm start&lt;/code&gt;. But we want to take things a little further. Lets install something called Nodemon with &lt;code&gt;npm i -D nodemon&lt;/code&gt;. Nodemon will watch for changes in our Express and reload the app when we make a change to it. After you've installed Nodemon replace &lt;code&gt;node&lt;/code&gt; in the start script with &lt;code&gt;nodemon&lt;/code&gt;. Your script should look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nodemon ./bin/www"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;npm start&lt;/code&gt; again and this time you'll start with an auto refreshing server.&lt;/p&gt;

&lt;p&gt;At this point you'll have these files and directories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;bin/www&lt;/code&gt; - Starts the server&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;public/&lt;/code&gt; - Serves up static assets for the front end&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;routes/&lt;/code&gt; - Backend routes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;view/&lt;/code&gt; - Templates for the front end&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;app.js&lt;/code&gt; - The main server config file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a standard Express.js install. Most of this stuff you can leave intact unless other wise noted. &lt;/p&gt;

&lt;h2&gt;
  
  
  Configure Express to connect to a remote MongoBD on Atlas
&lt;/h2&gt;

&lt;p&gt;Instead of setting up MongoDB ourselves, we're gonna to defer to a service called &lt;a href="https://www.mongodb.com/atlas" rel="noopener noreferrer"&gt;Atlas&lt;/a&gt;. Our application is simple so we can use the free account. Once you create an account select a project and look off to the left sidebar. You should see a link named "Databases". Click on it and it should take you to an "Overview" tab. While on the Overview tab there should be button with the label "Connect". Click on it and you'll get a modal. Next click on the option that says "Connect your application." You should see a script that looks something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MongoClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ServerApiVersion&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mongodb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mongodb+srv://&amp;lt;username&amp;gt;:&amp;lt;password&amp;gt;@cluster0.glgp5.mongodb.net/&amp;lt;database-name&amp;gt;?retryWrites=true&amp;amp;w=majority&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MongoClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;useNewUrlParser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;useUnifiedTopology&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;serverApi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ServerApiVersion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;v1&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;collection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;devices&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// perform actions on the collection object&lt;/span&gt;
  &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the root of your project create a file called &lt;code&gt;.env&lt;/code&gt;. Copy the value of the const uri and paste it inside &lt;code&gt;.env&lt;/code&gt;. Prefix the value with &lt;code&gt;DB_DSN=&lt;/code&gt;. Your &lt;code&gt;.env&lt;/code&gt; file should look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;DSN_DB&lt;/span&gt;=&lt;span class="n"&gt;mongodb&lt;/span&gt;+&lt;span class="n"&gt;srv&lt;/span&gt;://&lt;span class="n"&gt;your_username&lt;/span&gt;:&lt;span class="n"&gt;your_password&lt;/span&gt;@&lt;span class="n"&gt;cluster0&lt;/span&gt;.&lt;span class="n"&gt;glgp5&lt;/span&gt;.&lt;span class="n"&gt;mongodb&lt;/span&gt;.&lt;span class="n"&gt;net&lt;/span&gt;/&lt;span class="n"&gt;your_database_name&lt;/span&gt;?&lt;span class="n"&gt;retryWrites&lt;/span&gt;=&lt;span class="n"&gt;true&lt;/span&gt;&amp;amp;&lt;span class="n"&gt;w&lt;/span&gt;=&lt;span class="n"&gt;majority&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace the strings that begin with &lt;code&gt;your_&lt;/code&gt; with your actual username, password, and database name.&lt;/p&gt;

&lt;p&gt;We're putting our authentication to the database in an &lt;code&gt;.env&lt;/code&gt; for security reasons. Node.js has a library called dotenv that will read this file and create environment variables from them. This is what we want so install dotenv with &lt;code&gt;npm i -D dotenv&lt;/code&gt;. Open up &lt;code&gt;bin/www&lt;/code&gt; and place this line of code on line 2, &lt;code&gt;require('dotenv').config();&lt;/code&gt;. We should now be able to access this in Express with &lt;code&gt;process.env.DB_DSN&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Create a file in the root of the project named &lt;code&gt;database.js&lt;/code&gt; place this in it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mongoose&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mongoose&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;dsn&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;mongoose&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dsn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;useNewUrlParser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since we'll be using something called Mongoose to interface with our Database, go ahead an install it with &lt;code&gt;npm i mongoose&lt;/code&gt;. This script setups mongoose to connect to our database. Next open up &lt;code&gt;www/bin&lt;/code&gt; again and add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../database&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB_DSN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;connected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes the actual connection to our Database using the DB_DSN credentials we've provided.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup a database model with Mongoose
&lt;/h2&gt;

&lt;p&gt;Now that we have a connection to our database we need to setup a model for how data will be entered into the database. We do this using Schemas with Mongoose. Create a file directory named &lt;code&gt;models&lt;/code&gt; and place a file called &lt;code&gt;page.js&lt;/code&gt; in it. Inside &lt;code&gt;page.js&lt;/code&gt; add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mongoose&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mongoose&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;AutoIncrement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mongoose-sequence&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;mongoose&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emailValidator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email-validator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PageSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mongoose&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;minlength&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;minlength&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;lowercase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;emailValidator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; is not a valid email address`&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;mediaType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;mediaURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;minlength&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;websiteLink&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;websiteLinkLabel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;timestamps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;PageSchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AutoIncrement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;inc_field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mongoose&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Pages&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PageSchema&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're using two packages to assist Mongoose here so lets install them. Run &lt;code&gt;npm i mongoose-sequence email-validator&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Mongoose Sequence allows us to auto increment a database field. We want to do that with the id because we want a unique id auto generated every time a record is created. The code for this can be found on line 55. We take our schema object and run the plugin on it. Use the property &lt;code&gt;inc_field&lt;/code&gt; in the object as options. The value of &lt;code&gt;inc_field&lt;/code&gt; should be the name of the field you want to auto increment. In our case that's id.&lt;/p&gt;

&lt;p&gt;The other package we're using is Email Validator. This allows us to have the server reject emails that don't match valid email validation. In our app emails are actually optional. So you'll see in the code that the email can return either a valid email or an empty string. Checkout line 24 in the code. Mongoose allows us to setup custom validators on any field. To do so use the &lt;code&gt;validate&lt;/code&gt; property. The &lt;code&gt;validate&lt;/code&gt; property should be an object with two properties, &lt;code&gt;validator&lt;/code&gt; and &lt;code&gt;message&lt;/code&gt;. Validator is a function that returns a boolean. It also passes the data used in the field as an arg. This way we can use EmailValidator to run validation on what the user entered. Since its optional we can also return true if the email is an empty string.&lt;/p&gt;

&lt;p&gt;The rest of the properties for the fields controls predefined rules such as should white space to be trimmed or the minimum length of the data that was inputted. You can find more about all the options you have in &lt;a href="https://mongoosejs.com/docs/schematypes.html" rel="noopener noreferrer"&gt;mongoose's schema types documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add server side routes
&lt;/h2&gt;

&lt;p&gt;With our model in place we need to add a way to actually send data to the database. We also need to display that data back to the user. We do this with routes. Routes allows us to handle both &lt;code&gt;POST&lt;/code&gt; and &lt;code&gt;GET&lt;/code&gt; on the server. When the user has submitted data we want to handle it as a post request and save that data through our model. When a user wants to see data (view a page), we handle it as a GET request.&lt;/p&gt;

&lt;p&gt;We need plenty of routes for our app.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;/create&lt;/code&gt; - handles creating a new page&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/dashboard&lt;/code&gt; - displays all the pages that have been created&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/delete&lt;/code&gt; - deletes the specified page&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/edit&lt;/code&gt; - edits the specified page&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/page&lt;/code&gt; - view the specified page&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I'm not going to go over all the code in the routes here. After all you can &lt;a href="https://github.com/hasanirogers/indelible-landing/tree/main/routes" rel="noopener noreferrer"&gt;checkout it out on at my repo&lt;/a&gt;. What I do want to cover though is the basic concept of handling &lt;code&gt;POST&lt;/code&gt; and &lt;code&gt;GET&lt;/code&gt; requests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handling POST
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;POST&lt;/code&gt; request happens when the front end makes submits a form. Typically an AJAX call through fetch, axios, or maybe even jQuery is made. In Express, the data sent in this call is accessible through the request body parameter. So to handle a post request you need to use the post method of router and do something with the data by accessing &lt;code&gt;request.body&lt;/code&gt;. In our create route for example first we import router:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we use post.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PageModel&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;mediaType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mediaType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;mediaURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mediaURL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;websiteLink&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;websiteLink&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;websiteLinkLabel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;websiteLinkLabel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;savedPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;savedPage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SUCCESS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ERROR&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;responseData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ERROR&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;responseData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One line 3 we create a PageModel with Mongoose. That model is an object that represents all the data we've collected from the form. Mongoose takes that data and stores it in the database so as long as it passes validation and saves correctly. Speaking of saving, notice how we have conditional logic on line 16 that handles the save. IF the save fails we need to return an error the front end can handle. We also have it in a try/catch block. If for some reason something goes wrong with the process of saving to the database we catch that error and give it to the front end.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handling GET
&lt;/h3&gt;

&lt;p&gt;Get requests require us to send a page when the user access a particular url in a browser. To do this we render a template. That template can receive data from the server to display dynamic information such as a user name. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/:pageId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;PageModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pageId&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;templateInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;bodyClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`body--error`&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mediaType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;mediaType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;templateInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'s &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;mediaType&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;templateInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nx"&gt;templateInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bodyClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;body--landing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;page&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;templateInfo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;templateInfo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the code used to gender the unique landing page in our app. Notice on line 1 we have &lt;code&gt;/:pageId&lt;/code&gt; as a parameter of &lt;code&gt;get&lt;/code&gt;. This allows us to collect information from the url that was entered. In our case &lt;code&gt;/page/99&lt;/code&gt; would mean that the &lt;code&gt;pageId&lt;/code&gt; is &lt;code&gt;99&lt;/code&gt;. You can access this page id via request params or &lt;code&gt;request.params.pageId&lt;/code&gt;. On line 2 we grab information specific to the page that was requested via &lt;code&gt;pageId&lt;/code&gt;. We then use that information later on to generate a unique landing page. On line 15 we actually render our page. The page comes with &lt;code&gt;templateInfo&lt;/code&gt; or an object that contains information from the page that was returned. If the page is not found we default to an error page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure the routes in app.js
&lt;/h3&gt;

&lt;p&gt;You may have notice that there is no &lt;code&gt;/page&lt;/code&gt; given as an argument for &lt;code&gt;get&lt;/code&gt; in the code above. That's because the path to our routes is actually configured in &lt;code&gt;app.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;On line 28 of &lt;code&gt;app.js&lt;/code&gt; we have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/page&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pageRouter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...and &lt;code&gt;pageRouter&lt;/code&gt; is an import of our router in &lt;code&gt;routes/page.js&lt;/code&gt;; This allows us to only have to specify the route param when writing the logic of our page router.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use EJS templating to render the server side page
&lt;/h2&gt;

&lt;p&gt;With our routes in place we now need to render a page when users want to view some data from the server. In the beginning of this article I had you generate an express app using the switch &lt;code&gt;--view=ejs&lt;/code&gt;. EJS is the name of the templating engine we're going to use to render pages. In a EJS file we have access to all the info we stored in our &lt;code&gt;templateInfo&lt;/code&gt; object. &lt;a href="https://github.com/hasanirogers/indelible-landing/tree/main/views" rel="noopener noreferrer"&gt;We have many views&lt;/a&gt; but I'm going to be covering &lt;a href="https://github.com/hasanirogers/indelible-landing/blob/main/views/dashboard.ejs" rel="noopener noreferrer"&gt;dashboard.ejs&lt;/a&gt;. You can take the concepts here and run with them.&lt;/p&gt;

&lt;p&gt;The dashboard needs to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Include the header and footer&lt;/li&gt;
&lt;li&gt;Loop through all the pages and display links and actions associated with them.&lt;/li&gt;
&lt;li&gt;Handle pagination.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Be sure to &lt;a href="https://github.com/hasanirogers/indelible-landing/blob/main/routes/dashboard.js" rel="noopener noreferrer"&gt;checkout the route that renders dashboard.ejs&lt;/a&gt; to get clarity on where all this data comes from. &lt;/p&gt;

&lt;p&gt;Our &lt;a href="https://github.com/hasanirogers/indelible-landing/blob/main/views/partials/head.ejs" rel="noopener noreferrer"&gt;header&lt;/a&gt; and &lt;a href="https://github.com/hasanirogers/indelible-landing/blob/main/views/partials/footer.ejs" rel="noopener noreferrer"&gt;footer&lt;/a&gt; is in a partial that's a separate file. So we need to use include to embed them in our view.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;%-&lt;/span&gt; &lt;span class="nf"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;partials/head.ejs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;bodyClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;bodyClass&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;%-&lt;/span&gt; &lt;span class="nf"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;partials/footer.ejs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first argument is the location of the partial. The second is an object containing data for the template. We don't really need to pass the title down to the footer. I was probably in a rush when I did that.&lt;/p&gt;

&lt;p&gt;Next we need to loop through all of our pages. Our dashboard route sends down an array of objects named &lt;code&gt;pageData&lt;/code&gt;. This page data is aware of pagination rules so we can just loop through what's sent from the server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ul&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pages&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="cm"&gt;/* actions concerning the page go here */&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/li&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ul&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's a simple for loop to achieve this. We loop through the pages variable which is found in the dashboard's &lt;code&gt;templateInfo&lt;/code&gt; object. Every property on this object will be available to us as a variable here.&lt;/p&gt;

&lt;p&gt;Finally we need the pagination. In this app I've created a pagination web component. The pagination web component has a number of properties most of which are straight forward to figure out. We do want to build some logic in our template though. For example, we only want to display the pagination component if the pages are actually paginated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pagination&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentPage&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;pagination&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="c1"&gt;// pagination component goes here&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can do this by checking the &lt;code&gt;currentPage&lt;/code&gt; and &lt;code&gt;limit&lt;/code&gt; properties of pagination object. Limit is something we use to tell mongoose to limit the number of records to fetch from the database. If these properties aren't truthy then we don't have any pagination. We also need to provide values to our pagination component's properties and even conditionally add some properties such as last. Last shouldn't display if the user is on the last page because it's unnecessary at that point.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;indelible&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;pagination&lt;/span&gt;
  &lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/dashboard?page=[[current]]&amp;amp;limit=&amp;lt;%= pagination.limit %&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;previous&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/dashboard?page=[[previous]]&amp;amp;limit=&amp;lt;%= pagination.limit %&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/dashboard?page=[[next]]&amp;amp;limit=&amp;lt;%= pagination.limit %&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/dashboard?page=[[first]]&amp;amp;limit=&amp;lt;%= pagination.limit %&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;last&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/dashboard?page=[[last]]&amp;amp;limit=&amp;lt;%= pagination.limit %&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;%= pagination.totalPages %&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;%= pagination.currentPage %&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pagination&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;previousPage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;previous&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;%= pagination.previousPage %&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;previous&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;disabled&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pagination&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nextPage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;%= pagination.nextPage %&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;disabled&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pagination&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentPage&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pagination&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentPage&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;pagination&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;totalPages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;last&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;%= pagination.totalPages %&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/indelible-pagination&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're interested to know how the pagination component was written &lt;a href="https://github.com/hasanirogers/indelible-landing/blob/main/src/javascript/components/indelible-pagination.js" rel="noopener noreferrer"&gt;you can checkout its code here&lt;/a&gt;. It's basically a bunch of dynamically generated links.&lt;/p&gt;

&lt;h2&gt;
  
  
  Review various web components that make up the front end
&lt;/h2&gt;

&lt;p&gt;This app uses various components from &lt;a href="https://shoelace.style/" rel="noopener noreferrer"&gt;Shoelace&lt;/a&gt; and custom components written by me. You can find all the &lt;a href="https://github.com/hasanirogers/indelible-landing/tree/main/src/javascript/components" rel="noopener noreferrer"&gt;custom component here&lt;/a&gt;. I'm going to discuss the delete component because it also contains Shoelace components. The delete component is a form that sends a POST request to our &lt;a href="https://github.com/hasanirogers/indelible-landing/blob/main/routes/delete.js" rel="noopener noreferrer"&gt;delete route&lt;/a&gt;. When the users initially click the delete button we don't immediately send the request. Instead we pop open a dialog asking them to confirm the deletion. This is where Shoelace comes in. &lt;a href="https://shoelace.style/components/dialog" rel="noopener noreferrer"&gt;Shoelace has a dialog component&lt;/a&gt; that we're using inside of delete.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;sl-dialog&lt;/span&gt; &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;"Delete this page?"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Are you sure you want to delete this page?
  &lt;span class="nt"&gt;&amp;lt;sl-button&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"footer"&lt;/span&gt; &lt;span class="na"&gt;variant=&lt;/span&gt;&lt;span class="s"&gt;"primary"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Yes&lt;span class="nt"&gt;&amp;lt;/sl-button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/sl-dialog&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have a method on our delete component that will handle what happens when delete is clicked.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;handleClick&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dialog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sl-dialog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;dialog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the delete button is click, we just need to call &lt;code&gt;show()&lt;/code&gt; on the dialog element. You'll notice that the button in our &lt;code&gt;sl-dialog&lt;/code&gt; and has a type of &lt;code&gt;submit&lt;/code&gt;. This means that it'll work like a submit button for a form. The real deletion happens when we submit the form with the &lt;code&gt;sl-button&lt;/code&gt;. On the form we have a submit handler.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/delete"&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;submit=&lt;/span&gt;&lt;span class="s"&gt;${(event)&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; this.submitForm(event)}&amp;gt;
  &lt;span class="c"&gt;&amp;lt;!-- form --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;submitForm()&lt;/code&gt; method needs to do a POST request to &lt;code&gt;/delete&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;submitForm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;form&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

          &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reload&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;There was a problem deleting the page.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/FormData" rel="noopener noreferrer"&gt;formData API&lt;/a&gt; to get data from the form. In this case the only thing we need to collect is the &lt;code&gt;pageID&lt;/code&gt; of the page that needs to be deleted. The user doesn't need actually enter the &lt;code&gt;pageID&lt;/code&gt; since our template knows it. So we use a hidden form field to collect the data. If the deletion is successful we reload the page. If not we pop up an alert explaining to the user that there was a problem. Btw we're sending the data as JSON so be sure to set the headers to &lt;code&gt;application/json&lt;/code&gt; as shown in the snippet above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup a front end build chain with Rollup
&lt;/h2&gt;

&lt;p&gt;The last thing we need to cover for building a MEWN stack application is bundling front end assets. These days I prefer Rollup for this. Install Rollup with &lt;code&gt;npm i -D rollup&lt;/code&gt;. We also want to install a couple of Rollup plugins.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; @rollup/plugin-commonjs @rollup/plugin-node-resolve rollup-plugin-copy rollup-plugin-scss
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have an npm script named &lt;code&gt;build&lt;/code&gt; that runs the &lt;code&gt;rollup&lt;/code&gt; command with a config file flag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rollup -c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-c&lt;/code&gt; in the command means use a config file. You can specify where this config file lives but the default location Rollup will look for is a file in the root of the project named &lt;code&gt;rollup.config.js&lt;/code&gt;. We're using the default.&lt;/p&gt;

&lt;p&gt;Our Rollup config file looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@rollup/plugin-node-resolve&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;commonjs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@rollup/plugin-commonjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;copy&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rollup-plugin-copy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;scss&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rollup-plugin-scss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src/javascript/index.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public/bundle.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;es&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nf"&gt;commonjs&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nf"&gt;scss&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public/bundle.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;sass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sass&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="nf"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node_modules/@shoelace-style/shoelace/dist/assets&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public/vendors/shoelace&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src/assets&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The starting point for our bundle output is &lt;a href="https://github.com/hasanirogers/indelible-landing/blob/main/src/javascript/index.js" rel="noopener noreferrer"&gt;src/javascript/index.js&lt;/a&gt;. This is a file that contains a bunch of imports including our custom web components and Shoelace components. Rollup is going to take all the files here bundle them to a file called &lt;code&gt;public/bundle.js&lt;/code&gt;. Our application then loads the bundle file.&lt;/p&gt;

&lt;p&gt;We're also using the scss plugin. This means that Rollup is going to handle any &lt;code&gt;.scss&lt;/code&gt; files we import in &lt;code&gt;index.js&lt;/code&gt;. We've configured the plugin to output a complied scss file to a css file called &lt;code&gt;public/bundle.css&lt;/code&gt;. Like the JS we load this CSS file on the front end of the application.&lt;/p&gt;

&lt;p&gt;Some Shoelace components such as &lt;code&gt;sl-icon&lt;/code&gt; has assets associated with them. We need to copy those assets from the &lt;code&gt;node_modules&lt;/code&gt; directory to a directory in our app so that we can load them. I've chosen to copy them to &lt;code&gt;public/vendors/shoelace&lt;/code&gt;. We also use the copy plugin to copy everything in &lt;code&gt;src/assets&lt;/code&gt; to &lt;code&gt;public&lt;/code&gt; since public is our static assets directory. Think of it as a build directory. We don't version it.&lt;/p&gt;

&lt;p&gt;And that sums up the MEWN stack application folks. I know I didn't cover all the code but I've provided plenty of links to my repo with the application. I hope I've demonstrated that working with web components in an Node/Express/Mongo context is really straight forward. I hope to see the term MEWN catch on one day!&lt;/p&gt;

</description>
      <category>webcomponents</category>
      <category>node</category>
      <category>mongodb</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
