<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" version="2.0">
  <channel>
    <title>Al's garden</title>
    <link>https://alpalmtree.dev/</link>
    <atom:link href="https://alpalmtree.dev/feed.rss" rel="self" type="application/rss+xml"/>
    <description>Where I put my thoughts</description>
    <lastBuildDate>Fri, 19 Jun 2026 17:31:10 GMT</lastBuildDate>
    <language>en</language>
    <generator>Lume 3.2.6</generator>
    <author>
      <name>Álvaro Palma</name>
      <uri>https://alpalmtree.dev</uri>
    </author>
    <item>
      <title>This blog was built using a custom CMS</title>
      <link>https://alpalmtree.dev/blog/this-blog-was-built-using-a-custom-cms/</link>
      <guid isPermaLink="false">https://alpalmtree.dev/blog/this-blog-was-built-using-a-custom-cms/</guid>
      <content:encoded>
        <![CDATA[<p>More than a year ago, I opened my first blog ever at this same url. It started as an Astro project, but shortly after, I created a custom static site generator from scratch (code still available <a href="https://codeberg.org/alpalmtree/legacy.alpalmtree.dev">in this new repository</a>).</p>
<p>It was really fun! I think I managed to build something usable. Useful tho? That's a different story.</p>
<p>I wrote about the experience on <a href="https://alpalmtree.dev/blog/este-blog-estaba-hecho-con-astro/">this post</a>. Doing a retrospective now, it seems like it has a lot of moving parts. The simplicity I initially aimed for kinda disappeared. Every new article was an exploratory task on &quot;how the f*ck was I supposed to do this&quot;, and every new update suddenly seemed like a daunting task.</p>
<p>I don't think a personal blog is supposed to feel that way lmao. Got the experience, had fun, but that's all.</p>
<h2>Migrating to Lume</h2>
<p>If you don't know it, there's this amazing static site generator built natively for Deno called <a href="https://lume.land/">Lume</a>. Not only it relies on the good old Deno we all knew and loved (before delving into the chase of Node compatibility), but it's also built by a fellow spaniard, who also happened to build the templating engine powering this very page you are reading (<a href="https://vento.js.org/">Vento</a>, might write a post about it someday). Love to see that I'm not the only spaniard succumbing to the Deno fever! (tho I would love to connect to more people from Spain who prove me wrong!).</p>
<h2>Key advantages</h2>
<ol>
<li>I still get a super simple site generator that grows with me. It's got a lot of plugins for common things I honestly didn't want to deal with, like generating an RSS feed, posts queries, image resizing and transformation...</li>
<li>It feels kinda like the old style web. You can totally use Lume with TSX, but I love the fact that we can use nunjucks-like (and nunjucks itself) templating engines. Build step for assets pipelines is also optional.</li>
<li>While having a small-ish community, it's very popular in the Deno ecosystem, with a growing community, tons of examples and super well documented. It's also very simple to reason about when it comes to customizing the behaviour via plugins.</li>
<li>It's a project I'm proud to contribute to, even if it's only to its plugin ecosystem or by spreading the word (like I'm doing with this post).</li>
<li>It's got a built-in integration with another in-house project, <a href="https://lume.land/cms/">LumeCMS</a>. I'm actually writing this article using it and the experience is awesome!</li>
</ol>
]]>
      </content:encoded>
      <category>JavaScript</category>
      <category>Deno</category>
      <pubDate>Mon, 15 Jun 2026 00:00:00 GMT</pubDate>
    </item>
    <item>
      <title>Web Components: Fear and Loathing in La Web</title>
      <link>https://alpalmtree.dev/blog/web-components-fear-and-loathing-in-la-web/</link>
      <guid isPermaLink="false">https://alpalmtree.dev/blog/web-components-fear-and-loathing-in-la-web/</guid>
      <content:encoded>
        <![CDATA[<p>Honestly, I believe there's no middle ground: web components are either over or underrated. While some people state <a href="https://dev.to/ryansolid/web-components-are-not-the-future-48bh">that the cost of Web Components</a> is not worth the use case, some others state that &quot;you don't need React (or any other frontend framework) anymore&quot;.</p>
<p>Actually, yes, there is a middle ground of course. Don't blindly believe strangers on the internet. Not so long ago I came across <a href="https://nolanlawson.com/2023/08/23/use-web-components-for-what-theyre-good-at/">this article</a> talking about what Web Components are good at — and it does have a great point. There are few articles in this line, I'd recommend you do a little research on the topic.</p>
<p>However, most literature I read on the internet makes the mistake, from my point of view, of comparing Web Components with other libraries (hereafter, <em>React</em>, since it's much more ergonomic to write than <em>&lt;insert_your_framework_here&gt;</em>). I believe this is intrinsically wrong, since Web Components are a technology (or better yet, a <strong>group of technologies</strong>) native to the web platform; and as such, we can have a lot of different use cases — so the list of &quot;what are web components good at&quot; grows with every case that works for you.</p>
<p>If you are totally new to Web Components, I'd highly recommend following any tutorial you find out there and then revisiting this post. Anything works, really. There is a <strong>heck of a lot</strong> of articles and videos on this subject. Pick your favorite content creator and most certainly they'll have a video/article about Web Components. Trust me (or don't, I'm just another stranger on the internet).</p>
<h2>The three pillars: Shadow DOM, <code>template</code> element and Custom Elements</h2>
<p>Web components are the name given to this set of native technologies available in modern browsers. Probably, Custom Elements would be at the basic level of the Web Components <a href="https://en.wikipedia.org/wiki/Prototype_theory">prototype</a>. As such, most literature out there focuses and exemplifies use cases with Custom Elements.</p>
<h3>Shadow DOM</h3>
<p>Broadly speaking, the Shadow DOM API allows you to create a sort of scope inside an HTMLElement (yes, HTMLElement, not necessarily a custom element). You can read more about it <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM">in MDN's documentation</a>.</p>
<p>This is typically used together with Custom Elements because is a great way of providing our beloved encapsulation we see in React (or &lt;your_framework_here&gt;, remember). When attaching a shadow DOM to an HTMLElement:</p>
<code-hl group="shadow-dom" name="app.js" open>
<pre><code class="language-js">const appRoot = document.getElementById(&quot;app-root&quot;);

appRoot.attachShadow({ mode: &quot;open&quot; });
const paragraph = document.createElement(&quot;p&quot;);
paragraph.textContent = &quot;Inside shadow DOM&quot;;

// Notice how we are appending it to the shadowRoot property, created with the attachShadow method
appRoot.shadowRoot.appendChild(paragraph);

// appRoot.appendChild(paragraph) --&gt; would work, but it won't be visible in the browser.
</code></pre>
</code-hl>
<code-hl group="shadow-dom" name="app.html">
<pre><code class="language-html">&lt;p&gt;Outside shadow DOM&lt;/p&gt;
&lt;div id=&quot;app-root&quot;&gt;&lt;/div&gt;
</code></pre>
</code-hl>
<p>We can see the following in our browser's inspector:</p>
<code-hl>
<pre><code class="language-html">&lt;p&gt;Outside shadow DOM&lt;/p&gt;
&lt;div id=&quot;app-root&quot;&gt;
  #shadow-root (open)
    &lt;p&gt;Inside shadow DOM&lt;/p&gt;
&lt;/div&gt;
</code></pre>
</code-hl>
<p>As you might have (at least partially) guessed, statements like the following won't affect the paragraph contained inside the <code>shadowRoot</code> of the element:</p>
<code-hl>
<pre><code class="language-html">&lt;style&gt;
  p {
    color: red;
  }
&lt;/style&gt;
&lt;script type=&quot;module&quot;&gt;
  document
    .querySelectorAll(&quot;p&quot;)
    .forEach(
      (p) =&gt; (p.textContent = &quot;Modified from JS&quot;)
    );
&lt;/script&gt;
</code></pre>
</code-hl>
<p>That's cool, isn't it? You should know, however, that the shadow DOM comes with a few quirks of its own. I highly recommend reading this article about <a href="https://nolanlawson.com/2022/11/28/shadow-dom-and-accessibility-the-trouble-with-aria/">shadow DOM and a11y</a> and this other one about <a href="https://www.matuzo.at/blog/2023/pros-and-cons-of-shadow-dom">issues with styles encapsulation</a>.</p>
<p>Overall, I'd argue it's a rather useful technology, but you can totally avoid it, and it'd be ok. No one is going to web-component-shame you for not using it (and if they do, they are not your friends). It's definitely got its <strong>lights and shadows</strong> (<em>BA DUM TSS</em>).</p>
<h3>The <code>template</code> element</h3>
<p>Looking at the example above (or if you are used to creating elements in vanilla JS), we can very easily see how creating and attaching elements to the DOM can be pretty boilerplate-ish. This is one of the reasons why we have the <code>template</code> element. While you could still create one and setting its <code>innerHTML</code> via JavaScript, they allow for pretty interesting patterns. Learn more about it <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/template">on MDN's documentation</a>.</p>
<p>The most interesting bit, I'd argue, it is the fact that even if they exist in the DOM, they won't be visible until you append them somehow (either manually or using the declarative shadow DOM). In case you want the content that you will later append to exist in the DOM (for instance, if you are server-side-rendering/statically-generating it), your users won't see any flashed content neither you have to worry about using any workaround (classic AngularJS' <code>ng-cloak</code>, if you know what I mean).</p>
<code-hl group="template" name="app.js" open>
<pre><code class="language-js">const appRoot = document.getElementById(&quot;app-root&quot;);
const form = document.getElementById(&quot;hi-form&quot;);
const messageTemplate = document.getElementById(&quot;message-template&quot;);

form.addEventListener(&quot;submit&quot;, (e) =&gt; {
  e.preventDefault();
  const name = new FormData(e.target).get(&quot;name&quot;);
  e.target.reset();

  const message = messageTemplate.content.cloneNode(true);
  message.querySelector(&quot;strong&quot;).textContent = name;

  appRoot.querySelector(&quot;p&quot;)?.replaceWith(message);
});

const initialMessage = messageTemplate.content.cloneNode(true);
initialMessage.querySelector(&quot;strong&quot;).textContent = &quot;stranger&quot;;
appRoot.appendChild(initialMessage);
</code></pre>
</code-hl>
<code-hl group="template" name="app.html">
<pre><code class="language-html">&lt;div id=&quot;app-root&quot;&gt;
    &lt;form action=&quot;#&quot; id=&quot;hi-form&quot;&gt;
        &lt;input type=&quot;text&quot; placeholder=&quot;Tell us your name&quot; name=&quot;name&quot; /&gt;
        &lt;button type=&quot;submit&quot;&gt;Send&lt;/button&gt;
    &lt;/form&gt;
&lt;/div&gt;
&lt;template id=&quot;message-template&quot;&gt;
    &lt;p&gt;Hello, &lt;strong&gt;&lt;/strong&gt;! Welcome to my app!&lt;/p&gt;
&lt;/template&gt;
</code></pre>
</code-hl>
<p>Creating elements with <code>document.createElement</code> and manipulating the attributes and properties imperatively is still slightly faster, but this allows for &quot;server side rendering&quot; some content that you will later use via JavaScript, with a much cleaner approach; and much safer and performant than setting the <code>innerHTML</code> property (as we can see in several Web Components tutorials out there).</p>
<h4>The <code>template</code> element and the shadow DOM</h4>
<p>If you found the two technologies useful so far, I have great news: they DO pair <strong>very</strong> well, allowing even for more complex composition while not having to write a single line of JavaScript:</p>
<code-hl group="template-shadow" name="app.html" open>
<pre><code class="language-html">&lt;div id=&quot;app-root&quot;&gt;
    &lt;p&gt;
      I belong to the light DOM and I'll be placed between two red paragraphs
    &lt;/p&gt;

    &lt;template shadowrootmode=&quot;open&quot;&gt;
      &lt;style&gt;
        p {
          color: red;
        }
      &lt;/style&gt;

      &lt;p&gt;I belong to the shadow DOM and hence I'm red&lt;/p&gt;
      &lt;slot&gt;&lt;/slot&gt;
      &lt;p&gt;I also belong to the shadow DOM&lt;/p&gt;
    &lt;/template&gt;
&lt;/div&gt;
</code></pre>
</code-hl>
<code-hl group="template-shadow" name="app.js">
<pre><code class="language-js">// we are referencing the shadow root directly,
// attached automatically by the browser thanks
// to the &quot;shadowrootmode&quot; attribute
const appRoot = document.getElementById(&quot;app-root&quot;).shadowRoot;

// Length: 2
const shadowParagraphs = appRoot.querySelectorAll(&quot;p&quot;);
// Length: 1
const lightDOMParagraphs = document.querySelectorAll(&quot;p&quot;);
</code></pre>
</code-hl>
<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/slot"><code>slot</code></a> element is our friend here. Notice that it comes with a few quirks as well, but as a rule of thumb: whatever is slotted, will be in the light DOM, while the template itself will be in the shadow DOM. This allows for pretty interesting patterns, where you can compose some base layout with encapsulated logic and styles and snap in there your elements from the light DOM.</p>
<h3>Custom Elements</h3>
<p>Now that I hope I made my point clear, let's dive right into the <em>good sauce</em>. As you hopefully have learned already, the three technologies can be perfectly decoupled and used as stand-alone solutions for problems you may encounter in your day-to-day <strong>vanilla JS apps</strong>.</p>
<p>Why the nuance? Well, let's remember that Web Components are (altogether) a mechanism baked right into the web platform, much like a <code>querySelector</code> or a <code>MutationObserver</code>. As such, they aim at solving problems that might or might not be solved already by another framework or library. Take the encapsulation for instance. If you are using <em>React</em>, most certainly there is a solution for that already (single file components, CSS modules, classes or functions for the logic...). Of course, also for the &quot;write declarative HTML and re-use it via JavaScript&quot;-bit. Custom element are no different.</p>
<p>As stated in the foreword of this post, there is <strong>a heck of a lot</strong> of articles written about Custom Elements (usually, under the namespace of Web Components, even though, as we already know, it's just one of the corners of the triangle). Let me try to shift the mindset a bit. I'm not going to talk about reusable components, but at what they are good at if we isolate them from the Web Components trio.</p>
<h4>Declaratively hydrating parts of your application</h4>
<p>The &quot;confusion&quot; (or merely coining by the community) of the concept <em>Web Components</em> as interchangeable with <em>Custom Elements</em> have led to a very interesting concept: <em>HTML Web Components</em>. I believe <a href="https://adactio.com/journal/20618">this article</a> is a pretty good summary of this concept.</p>
<p>Simply put: you don't (necessarily) have to use <code>template</code>s, the shadow DOM or do any sort of client side rendering. You just use custom elements to wrap a piece of HTML and hydrate it. What a good-old <code>querySelector</code> would do, but on steroids. Let's put an example by making one of the previous examples a tiny little bit more complicated:</p>
<code-hl group="custom-elements" name="app.html" open>
<pre><code class="language-html">&lt;body&gt;
  &lt;header&gt;
    &lt;nav&gt;
      &lt;ul&gt;
        &lt;li&gt;Some static content&lt;/li&gt;
        &lt;li&gt;
          &lt;input
            id=&quot;toggle-schema&quot;
            type=&quot;checkbox&quot;
            aria-label=&quot;toggle dark and light mode&quot;
          /&gt;
        &lt;/li&gt;
      &lt;/ul&gt;
    &lt;/nav&gt;
  &lt;/header&gt;
  &lt;div id=&quot;app-root&quot;&gt;
    &lt;form action=&quot;#&quot; id=&quot;hi-form&quot;&gt;
      &lt;input type=&quot;text&quot; placeholder=&quot;Tell us your name&quot; name=&quot;name&quot; /&gt;
      &lt;button type=&quot;submit&quot;&gt;Send&lt;/button&gt;
    &lt;/form&gt;
  &lt;/div&gt;
  &lt;template id=&quot;message-template&quot;&gt;
    &lt;p&gt;Hello, &lt;strong&gt;&lt;/strong&gt;! Welcome to my app!&lt;/p&gt;
  &lt;/template&gt;
&lt;/body&gt;
</code></pre>
</code-hl>
<code-hl group="custom-elements" name="app.js">
<pre><code class="language-js">const toggleSchema = document.getElementById(&quot;toggle-schema&quot;);
const appRoot = document.getElementById(&quot;app-root&quot;);
const form = document.getElementById(&quot;hi-form&quot;);
const messageTemplate = document.getElementById(&quot;message-template&quot;);

const doToggle = (toggle) =&gt; {
  document.body.dataset.theme = toggle.checked ? &quot;dark&quot; : &quot;light&quot;;
};

const createMessage = (text) =&gt; {
  const message = messageTemplate.content.cloneNode(true);
  message.querySelector(&quot;strong&quot;).textContent = text;
  appRoot.querySelector(&quot;p&quot;)?.replaceWith(message) ??
    appRoot.appendChild(message);
};

toggleSchema.addEventListener(&quot;change&quot;, ({ target }) =&gt; {
  doToggle(target);
});

form.addEventListener(&quot;submit&quot;, (e) =&gt; {
  e.preventDefault();
  const name = new FormData(e.target).get(&quot;name&quot;);
  e.target.reset();

  createMessage(name);
});

// Setting some initial state imperatively when the document loads
doToggle(toggleSchema);
createMessage(&quot;stranger&quot;);
</code></pre>
</code-hl>
<p>There are few things I'd like to highlight. Keep in mind that this example is extremely simple, let us imagine we have a much larger application:</p>
<ol>
<li>Even though we have clearly separated hydration regions, we are still looking in the whole DOM. We could still select the specific regions (<code>nav</code>, <code>div#app-root</code>) and then select elements inside, but it can get pretty boilerplate-ish.</li>
<li>We are manually/imperatively initializing everything.</li>
<li>Should any element not be present in the DOM by the time of the execution of the script, it would be ignored, and we should account for any DOM mutation if we want our new elements to be hydrated. Imagine that our template element had any logic inside or that you are sending the <code>appRoot</code> element &quot;over the wire&quot;, with solutions like <a href="https://turbo.hotwired.dev/">Turbo</a> or <a href="https://htmx.org/">HTMX</a>.</li>
<li>Overall, we are writing a script with global variables and a step-by-step declaration. This is fine and can be mitigated by taking some architectural decisions, but still. If you wanted to start in a single file and then move the logic to separate files, you'd for sure need a refactor.</li>
</ol>
<p>Let's now see the same example using Custom Elements:</p>
<code-hl group="custom-elements-2" name="app.html" open>
<pre><code class="language-html">&lt;body&gt;
  &lt;header&gt;
    &lt;nav&gt;
      &lt;ul&gt;
        &lt;li&gt;Some static content&lt;/li&gt;
        &lt;li&gt;
          &lt;toggle-element&gt;
            &lt;input
              id=&quot;toggle-schema&quot;
              type=&quot;checkbox&quot;
              aria-label=&quot;toggle dark and light mode&quot;
            /&gt;
          &lt;/toggle-element&gt;
        &lt;/li&gt;
      &lt;/ul&gt;
    &lt;/nav&gt;
  &lt;/header&gt;
  &lt;app-root&gt;
    &lt;form action=&quot;#&quot;&gt;
      &lt;input type=&quot;text&quot; placeholder=&quot;Tell us your name&quot; name=&quot;name&quot; /&gt;
      &lt;button type=&quot;submit&quot;&gt;Send&lt;/button&gt;
    &lt;/form&gt;
    &lt;!-- We have moved the template inside the custom element for convenience --&gt;
    &lt;template id=&quot;message-template&quot;&gt;
      &lt;p&gt;Hello, &lt;strong&gt;&lt;/strong&gt;! Welcome to my app!&lt;/p&gt;
    &lt;/template&gt;
  &lt;/app-root&gt;
&lt;/body&gt;
</code></pre>
</code-hl>
<code-hl group="custom-elements-2" name="app.js">
<pre><code class="language-js">customElements.define(
  &quot;toggle-element&quot;,
  class extends HTMLElement {
    toggle = this.querySelector(&quot;input[type=checkbox]&quot;);
    doToggle = () =&gt; {
      document.body.dataset.theme = this.toggle.checked ? &quot;dark&quot; : &quot;light&quot;;
    };

    connectedCallback() {
      this.doToggle();
      this.addEventListener(&quot;change&quot;, this.doToggle);
    }
  }
);

customElements.define(
  &quot;app-root&quot;,
  class extends HTMLElement {
    form = this.querySelector(&quot;form&quot;);
    messageTemplate = this.querySelector(&quot;template&quot;);

    createMessage = (text) =&gt; {
      const message = this.messageTemplate.content.cloneNode(true);
      message.querySelector(&quot;strong&quot;).textContent = text;
      this.querySelector(&quot;p&quot;)?.replaceWith(message) ??
        this.appendChild(message);
    };

    connectedCallback() {
      this.createMessage(&quot;stranger&quot;);
      this.form.addEventListener(&quot;submit&quot;, (e) =&gt; {
        e.preventDefault();
        const name = new FormData(e.target).get(&quot;name&quot;);
        e.target.reset();

        this.createMessage(name);
      });
    }
  }
);
</code></pre>
</code-hl>
<p>With this approach:</p>
<ol>
<li>The regions are clearly defined by their respective custom elements wrapper.</li>
<li>The code is &quot;better organized&quot; (I reckon it's also a matter of taste). Each function for manipulating the DOM as well as the references belong to an actual HTML Element that we called <code>toggle-element</code> and <code>app-root</code>. They are contained inside each instance and hence not polluting the global scope.</li>
<li>Since we are selecting them from the Custom Element, we can have much looser selectors. It is much more unlikely that we add an element with the same selector inside a custom element than in the DOM in general.</li>
<li>They are <em>actual</em> DOM elements. While this can come with few downsides, it's worth noticing that <code>document.querySelector(&quot;toggle-element&quot;).doToggle()</code> is as valid as accessing any other method native of a DOM element. This allows for pretty interesting state sharing that we should resolve via prop drilling or global state management in <em>React</em>.</li>
<li>Notice the <code>connectedCallback</code> method. This method will be automatically called by the browser whenever the element enters the DOM. There is a <code>disconnectedCallback</code> method as well for when the element exits the DOM. This means that they have actual lifecycle methods. Natively, without the need for any <code>MutationObserver</code>.</li>
<li>In general, whenever the browser detects that a Custom Element is created (either via <code>document.createElement</code> or directly in the HTML), it will automatically create the instance and perform whatever you declared. If you ask me, it's like CSS but for JavaScript. Imagine having to re-declare styles every time you manipulate the DOM. Not fun, uh?</li>
</ol>
<p>We could of course still break things down even more. For instance, by moving the <code>form</code> element to its own custom element. But I believe this would defeat the purpose of this post.</p>
<h2>Wrapping things up</h2>
<p>I hope it's clear by now that it's not fair to compare Web Components to <em>React</em>. Web Components are a conglomerate of technologies native to the web platform, and as such, they come with their own downsides and quirks that <em>React</em> is trying to solve. As did jQuery in the past.</p>
<p>What I believe Web Components are excellent at is at mitigating some common pain points we find in our day-to-day developments in vanilla JavaScript. You can totally make a mix and match of all three technologies, just use one or two of them, commit only until the point you feel is right.</p>
<p>The most beautiful thing about Web Components is that they are not going anywhere as long as the web platform lives. They won't become yet another outdated dependency in your project that you promised your team lead you'd update one year ago. They can only improve from here. Who knows, maybe in the future we'll see some reactivity baked in, a more ergonomic approach to componentization and rendering, improvements in accessibility and performance, browser compatibility...</p>
<p>Who knows. Maybe, and only <strong>maybe</strong> then we could compare Web Components with a framework. Until then, we'd better compare them with a <code>querySelector</code> or with setting the <code>innerHTML</code> property.</p>
]]>
      </content:encoded>
      <category>JavaScript</category>
      <pubDate>Sat, 14 Jun 2025 00:00:00 GMT</pubDate>
    </item>
  </channel>
</rss>