<?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>Forem: Hasan Ali</title>
    <description>The latest articles on Forem by Hasan Ali (@hasanhaja).</description>
    <link>https://forem.com/hasanhaja</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%2F1037790%2F276d09b6-b831-4af3-91aa-9fdb4ff8378a.jpg</url>
      <title>Forem: Hasan Ali</title>
      <link>https://forem.com/hasanhaja</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/hasanhaja"/>
    <language>en</language>
    <item>
      <title>Web Fundamentals: Web Components Part 2</title>
      <dc:creator>Hasan Ali</dc:creator>
      <pubDate>Thu, 08 Feb 2024 19:22:30 +0000</pubDate>
      <link>https://forem.com/hasanhaja/web-fundamentals-web-components-part-2-mg</link>
      <guid>https://forem.com/hasanhaja/web-fundamentals-web-components-part-2-mg</guid>
      <description>&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Recap&lt;/li&gt;
&lt;li&gt;More with Custom Elements&lt;/li&gt;
&lt;li&gt;React, not overreact&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  1. Recap
&lt;/h2&gt;

&lt;p&gt;So far, we’ve defined what web components are and caught a glimpse into what they can do. We've looked at how to define custom elements, how the browser sees them, and looked at what component lifecycle methods are. Well, some of them. What we're working towards in this series on web components is building up our understanding of what the web platform has to offer, so we can start to build a muscle for when it makes sense to reach for web components and what problems they're really well suited to solve. To get there, let's first dive back into where we left off in &lt;a href="https://dev.to/hasanhaja/web-fundamentals-web-components-part-1-3nlh"&gt;Part 1&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. More with Custom Elements
&lt;/h2&gt;

&lt;p&gt;Our &lt;code&gt;x-timer&lt;/code&gt; component so far was capable of connecting to the document and cleaning up any resources when it disconnected. However, we have no way of customizing it without delving into source markup. This was fine for that example, but this would be very limiting if this was a component you created to be downloaded and used somewhere else. We've already seen how other HTML elements like &lt;code&gt;div&lt;/code&gt; and &lt;code&gt;button&lt;/code&gt; take on additional data in the form of attributes, and we can do the same thing with custom elements. What we want to achieve is let the consumer of &lt;code&gt;x-timer&lt;/code&gt; set any interval for their timer:&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;x-timer&lt;/span&gt; &lt;span class="na"&gt;x-interval=&lt;/span&gt;&lt;span class="s"&gt;"3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/x-timer&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This doesn't do anything yet but this will be the API we expose. Let's start wiring it up:&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="c1"&gt;// timer.js&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Timer&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-interval&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;// --snip--&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This doesn't do anything yet either, but I've found this to be a simple but useful approach to define element attributes in one place [1]. The &lt;code&gt;attrs&lt;/code&gt; object acts as a map that holds the key we'd like to reference the HTML attribute by. I found this pattern useful because conventionally HTML attributes are hyphenated if more than one word (also known as kebab-case), and I found it easier to work with camelcase object properties because you can rely on tooling like autocomplete in your editor. To actually wire it up:&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;class&lt;/span&gt; &lt;span class="nc"&gt;Timer&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-interval&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="cm"&gt;/**
    * @type { number }
    */&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="cm"&gt;/**
    * @type { HTMLElement }
    */&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;countSpan&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="cm"&gt;/**
    * @type { number }
    */&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;timerId&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="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;connectedCallback&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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-timer connected&lt;/span&gt;&lt;span class="dl"&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="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;count&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;countSpan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;span&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;interval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&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;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Timer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;interval&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="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&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="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;timerId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setInterval&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="s2"&gt;Timer called&lt;/span&gt;&lt;span class="dl"&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="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&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="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;countSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&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="err"&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;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;interval&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&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;countParagraph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;p&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;countParagraph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Count: &lt;/span&gt;&lt;span class="dl"&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="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;countSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&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="err"&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;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nx"&gt;countParagraph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&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="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;countSpan&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;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;countParagraph&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;disconnectedCallback&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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-timer disconnected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;clearInterval&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="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;timerId&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;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Aside&lt;/strong&gt;: the code is no longer in TypeScript, but typed with JSDoc. I've done this refactor so you can actually copy and paste the code, and run it directly without a build step. As an aside, I've been enjoying this approach to both authoring web components and JavaScript in general.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A few things to note here with the base code. The timer declaration has moved from the &lt;code&gt;constructor&lt;/code&gt; to the &lt;code&gt;connectedCallback&lt;/code&gt;. This was both a correction and a way to prevent awkward side effects from occurring, like if the component was disconnected and connected back into the document the timer wouldn't restart because the constructor wouldn't be called.&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;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&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="s2"&gt;x-timer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// constructor called&lt;/span&gt;
&lt;span class="nb"&gt;document&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="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// connectedCallback called&lt;/span&gt;

&lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;//disconnectedCallback called&lt;/span&gt;
&lt;span class="nb"&gt;document&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="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// connectedCallback called&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most of that should look identical to Part 1. The way you handle attributes passed into a custom element is by just reading it using &lt;code&gt;this.getAttribute&lt;/code&gt;, and since &lt;code&gt;getAttribute&lt;/code&gt; could return &lt;code&gt;null&lt;/code&gt;, we set a default value too before parsing it into an integer.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The code you see above could very easily have been &lt;code&gt;this.getAttribute("x-interval")&lt;/code&gt; instead of using the static attribute map pattern that we've done.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Moreover, the query-ability of a component depends on two thing: the loading of the script and the parsing of the HTML [2]. If the HTML was parsed before the script was loaded, then when the script loads and the custom element is defined, the constructor will have access to the attributes and the children nodes [3]. However, if the script and the custom element definition loaded first, then the constructor would not be able to access the document nodes because it might not be fully parsed yet. To simplify this for our example, we can move this query logic to the &lt;code&gt;connectedCallback&lt;/code&gt;, which will only run when the element is connected to the document.&lt;/p&gt;

&lt;p&gt;By querying the attribute in the &lt;code&gt;connectedCallback&lt;/code&gt;, we'd will only set the interval with the attribute once for when the component connects to the document. That means the timer wouldn't update if you change the attribute from the outside like so:&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;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&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="s2"&gt;x-timer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-interval&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;10&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To do that, we'll need to tell the component to observe changes to the attribute and use another lifecycle method called &lt;code&gt;attributeChangedCallback&lt;/code&gt; to manage the changes. To observe attributes, you can declare a static property called &lt;code&gt;observedAttributes&lt;/code&gt; that the platform recognizes and list the names of the attributes to be tracked. That would change our code like so:&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;class&lt;/span&gt; &lt;span class="nc"&gt;Timer&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-interval&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="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;observedAttributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Timer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;  
  &lt;span class="c1"&gt;// --snip--&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: This is equivalent &lt;code&gt;static observedAttributes = ["x-interval"];&lt;/code&gt;, and if you had more than one attribute to track, you could either comma-separate them in that array individually, or use &lt;code&gt;Object.values&lt;/code&gt; like this &lt;code&gt;static observedAttributes = Object.values(attrs);&lt;/code&gt; [3].&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Another equally valid approach to define &lt;code&gt;observedAttributes&lt;/code&gt; is to make it a static getter 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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Timer&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-interval&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="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;observedAttributes&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;Timer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;  
  &lt;span class="c1"&gt;// --snip--&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As far as I can tell, they work exactly the same way.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. React, not overreact
&lt;/h2&gt;

&lt;p&gt;Now that we've told the custom element to track the changes to our attributes, we're ready to handle them in the &lt;code&gt;attributeChangedCallback&lt;/code&gt;. This is the signature of the method [4]:&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="cm"&gt;/**
  * @param { string } name
  * @param { string } oldValue
  * @param { string } newValue
  */&lt;/span&gt;
&lt;span class="nf"&gt;attributeChangedCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;oldValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newValue&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 callback gets called when an attribute changes. It is the same callback that gets called for every attribute change. If you have many attributes changing all at once, this callback would get called for each attribute change, respectively, for the number of times they've changed. This means that you will need to ensure in the callback that the updates you make only pertain to the attribute responsible for it, and that there's an actual change in value; you can set the attribute with the same value multiple times, and this would trigger the callback for each change. The way you know what attribute triggered the callback is by checking the &lt;code&gt;name&lt;/code&gt; parameter, and to check if the value has changed, you can compare the &lt;code&gt;oldValue&lt;/code&gt; with the &lt;code&gt;newValue&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Timer&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-interval&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="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;observedAttributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Timer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;  
  &lt;span class="c1"&gt;// --snip--&lt;/span&gt;
  &lt;span class="nf"&gt;attributeChangedCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;oldValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newValue&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;Before we flesh this logic out, let's take a step back and think about the order of events from the browser's perspective. First, the browser instantiates a custom element after encountering it in the parsing phase. Then it calls a few lifecycle methods in the layout and painting phase, before finally connecting the component to the screen in the composite phase. A quick shortcut I use to remember this flow is by thinking about how I would create an HTML element in JavaScript, configure it and then insert it into the document [5]. The steps I'd follow are:&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;newTimer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-timer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// constructor called&lt;/span&gt;
&lt;span class="nx"&gt;newTimer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-interval&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;2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// attributeChangedCallback called&lt;/span&gt;
&lt;span class="nb"&gt;document&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="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newTimer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// connectedCallback called&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you squint a bit, this is essentially what the browser does when you declaratively use custom elements in HTML. The &lt;code&gt;attributeChangedCallback&lt;/code&gt; gets called too because the setting of the initial value is also considered a change. This makes sense from the signature because the &lt;code&gt;oldValue&lt;/code&gt; would've been &lt;code&gt;undefined&lt;/code&gt; and the &lt;code&gt;newValue&lt;/code&gt; is the value you've just set.&lt;/p&gt;

&lt;p&gt;With that context, you'll need to reason about the order of events when checking or manipulating member properties from within the different methods. For example, &lt;code&gt;this.#timerId&lt;/code&gt; gets set in the &lt;code&gt;connectedCallback&lt;/code&gt; and since that gets called after &lt;code&gt;attributeChangedCallback&lt;/code&gt;, we'll need to ensure we check if the element is connected before creating a new timer (or conversely do nothing if the element is disconnected) [3]. If we wanted to change the timer interval after putting the element on the document, then we would need to ensure our update logic doesn't accidentally run before the update. To do this, we can simply check if the element is connected using the &lt;code&gt;isConnected&lt;/code&gt; property that's available to every document node, and return early from the callback if not.&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;class&lt;/span&gt; &lt;span class="nc"&gt;Timer&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// --snip--&lt;/span&gt;
  &lt;span class="nf"&gt;attributeChangedCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;oldValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newValue&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isConnected&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="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 also only want to do this if the &lt;code&gt;oldValue&lt;/code&gt; and the &lt;code&gt;newValue&lt;/code&gt; for any tracked attribute is different, so we can wrap our conditional with that first:&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;class&lt;/span&gt; &lt;span class="nc"&gt;Timer&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// --snip--&lt;/span&gt;
  &lt;span class="nf"&gt;attributeChangedCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;oldValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newValue&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;oldValue&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;newValue&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isConnected&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* --snip-- */&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;Checking for the value difference first before the attribute name means that we will only look to do work for an attribute when we know that there has been a change at all. Since we only want our timer update logic to depend on the interval attribute, we can put all of the logic related to it in another conditional that compares the interval attribute name with the &lt;code&gt;name&lt;/code&gt; input parameter in the callback:&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;class&lt;/span&gt; &lt;span class="nc"&gt;Timer&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// --snip--&lt;/span&gt;
  &lt;span class="nf"&gt;attributeChangedCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;oldValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newValue&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;oldValue&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;newValue&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;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;Timer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;interval&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isConnected&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* --snip-- */&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;Now we're ready to add the logic to replace our existing timer with a new timer when the interval attribute is updated:&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;class&lt;/span&gt; &lt;span class="nc"&gt;Timer&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// --snip--&lt;/span&gt;
  &lt;span class="nf"&gt;attributeChangedCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;oldValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newValue&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;oldValue&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;newValue&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;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;Timer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;interval&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isConnected&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* --snip-- */&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;parsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newValue&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;interval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nf"&gt;clearInterval&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="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;timerId&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="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;timerId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setInterval&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="s2"&gt;New timer called&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;interval&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="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&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="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;countSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&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="err"&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;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;interval&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&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;This logic should look very similar to what we previously saw in the &lt;code&gt;connectedCallback&lt;/code&gt;. Note that we performed some clean up using &lt;code&gt;clearInterval&lt;/code&gt; to manage our resources before reassigning it. We do this because, the reassignment of &lt;code&gt;this.#timerId&lt;/code&gt; only mutates the ID that it has stored, and doesn't delete the timer from the scope it lives in. If we don't perform this cleanup, then the previous timer would continue to run in the background and continue to update the counter on its interval alongside the new one, which as you'd imagine would be very confusing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F11b4zm8ma7osabjtfttj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F11b4zm8ma7osabjtfttj.png" alt="Spider-Man pointing meme where Spider-Man points to another Spider-Man (source: https://www.cbr.com/best-spiderman-pointing-memes/)" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is what the final HTML looks like with the logic that updates the timer, which you'd also be able to find on my &lt;a href="https://github.com/hasanhaja/web-fundamentals/tree/main/packages/vanilla-web-components" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&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="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Web Components Part 2&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"styles.css"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"timer.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Web components demo&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;main&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;x-timer&amp;gt;&amp;lt;/x-timer&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;x-timer&lt;/span&gt; &lt;span class="na"&gt;x-interval=&lt;/span&gt;&lt;span class="s"&gt;"5"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/x-timer&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;x-timer&lt;/span&gt; &lt;span class="na"&gt;x-interval=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/x-timer&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Update timer 2 to 10 seconds&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;timerTwo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&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="s2"&gt;x-timer[x-interval='2']&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;button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&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="s2"&gt;x-timer[x-interval='2'] + 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;button&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="s2"&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;timerTwo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-interval&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;10&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="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;This covers all but one of the lifecycle methods that you get access to with custom elements, and in actuality, these are what you'd use most of the time. The last one is &lt;code&gt;adoptedCallback&lt;/code&gt; and you'd most likely encounter it in the context of &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; elements [6]. Even though I don't have plans to go through it in the series, the concepts and the way we've covered them so far should give you a good idea on how to begin unpicking it if you do. Other than that, we have enough under our belt to delve even deeper into what web components have to offer.    &lt;/p&gt;

&lt;p&gt;All of this might feel like a lot of work to put some HTML on the screen and update a few attributes, and it is. Even though we've achieved fine-grained updates in our component, it took a fair bit of reasoning about the lifecycle methods to get there. The reason the custom elements API feels a little cumbersome is because it's low level by design to give you the most control. This means that you can do anything with it, including build your own abstractions on top of it.&lt;/p&gt;

&lt;p&gt;There are other component-based abstractions that will give you the same effect with a lot less work, and some even give you the same level of fine-grained control. Though we'll look at a few UI frameworks alongside web components, the ultimate aim of this series is to demonstrate what the web platform is capable of, and get you to start thinking about the different tradeoffs you make when picking different tools. My hypothesis is that by understanding what the platform has to offer, you'll be in a much better position to evaluate the complexity you choose to take on when building experiences. Lastly, I'll leave you with a little spoiler of what's to come: web components are cool and they are here to stay.&lt;/p&gt;

&lt;p&gt;If you think of anything I've missed or just wanted to get in touch, you can reach me through a comment or &lt;a href="https://fosstodon.org/@hasanhaja" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Zach Leatherman's &lt;code&gt;&amp;lt;browser-window&amp;gt;&lt;/code&gt; component [&lt;a href="https://github.com/zachleat/browser-window" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;Danny Engelman's comment on loading order [&lt;a href="https://dev.to/dannyengelman/comment/29kah"&gt;Comment&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;𒎏Wii's comment on loading order [&lt;a href="https://dev.to/darkwiiplayer/comment/2cn2h"&gt;Comment&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;MDN Web Components [&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#responding_to_attribute_changes" rel="noopener noreferrer"&gt;Website&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;Component Lifecycle Reference Diagram [&lt;a href="https://andyogo.github.io/custom-element-reactions-diagram/" rel="noopener noreferrer"&gt;Website&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;Component Lifecycle Reference [&lt;a href="https://webcomponents.guide/learn/components/lifecycle-reference/" rel="noopener noreferrer"&gt;Website&lt;/a&gt;]&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>webcomponents</category>
      <category>html</category>
    </item>
    <item>
      <title>Web Fundamentals: The HTML-JSX Confusion</title>
      <dc:creator>Hasan Ali</dc:creator>
      <pubDate>Fri, 08 Sep 2023 21:55:00 +0000</pubDate>
      <link>https://forem.com/hasanhaja/web-fundamentals-the-html-jsx-confusion-438p</link>
      <guid>https://forem.com/hasanhaja/web-fundamentals-the-html-jsx-confusion-438p</guid>
      <description>&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;The mistake&lt;/li&gt;
&lt;li&gt;Parsers&lt;/li&gt;
&lt;li&gt;XHTML &amp;amp; JSX&lt;/li&gt;
&lt;li&gt;Why it matters&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  1. The mistake
&lt;/h2&gt;

&lt;p&gt;This post was originally going to be a short correction on &lt;a href="https://dev.to/hasanhaja/web-fundamentals-web-components-part-1-3nlh"&gt;Web Fundamentals: Web Components Part 1&lt;/a&gt; about a false claim I made. I claimed that you can follow along with a simple HTML file by copy and pasting the code snippets that were originally written in Astro. However, that isn't entirely true unless you tweaked some of the markup first. I used Astro's markup syntax [1] which is very similar to HTML, and since I didn't use any of its templating features in my snippets, I thought it'd be identical to HTML. It turns out that it would only be identical to the incorrect HTML I would've written because I wasn't aware of how browsers parsed it. The mistake I made was not put closing tags on my custom elements. I wrote &lt;code&gt;&amp;lt;x-timer /&amp;gt;&lt;/code&gt; instead of &lt;code&gt;&amp;lt;x-timer&amp;gt;&amp;lt;/x-timer&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I suppose this is a small enough error that you could either ignore or accept a quick correction for, but I thought it'd be more valuable to take this opportunity to peak behind the scenes of how browsers parse HTML. My intention with this snippet was to declare an empty element in the document, and then add children to it using JavaScript. It worked for me in Astro because Astro's templating syntax is closer to JSX than it is HTML. If you were to do the same thing in a plain &lt;code&gt;.html&lt;/code&gt; file and then view it in the browser, there's a chance it might not look right depending on the markup that followed the declaration. What's the problem then? And why is it nuanced?&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Parsers
&lt;/h2&gt;

&lt;p&gt;Syntax is an agreement. It’s an agreement between you and the tool that parses it. When we follow the rules, we expect to see the same results every time because that’s what we’ve agreed to. If the rules were strict, it'd be harder to break but it might also feel too rigid. If the rules were lenient, it'd offer greater flexibility but it can also be harder to avoid ambiguity in the code.&lt;/p&gt;

&lt;p&gt;The HTML parser is very flexible by design. It’s built in such a way that even if you break the rules a little, it’ll figure out how to simplify things and display something on the screen. It’s very tolerant. This means if you view the following incomplete markup in the browser, it'll still render a page:&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;div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;HTML is tolerant
&lt;span class="nt"&gt;&amp;lt;br&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Unusally tolerant if you've used a programming language
&lt;span class="nt"&gt;&amp;lt;section&amp;gt;&lt;/span&gt;
Why does it work without any closing tags?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you inspect this page, you'll see that the browser has insert some basic tags you've missed and also figure out where to put the closing tags for the elements you’ve declared:&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;html&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;HTML is tolerant&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;br&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Unusally tolerant if you've used a programming language&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;section&amp;gt;&lt;/span&gt;
        Why does it work without any closing tags?
      &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This might've been exactly what you wanted. Or you might've intended for the &lt;code&gt;section&lt;/code&gt; to be outside of the &lt;code&gt;div&lt;/code&gt;. Since you've omitted the closing tags, there is no way for the HTML parser to deduce your intent any further [2]. It simplifies it and just does what it does because that’s the agreement. This flexibility means that the burden of enforcing the correctness of the markup is on you. Are there other parsers with different rules that will yell at you when you're being ambiguous like this? Yes.&lt;/p&gt;

&lt;p&gt;The flexibility is a feature of the HTML parser, but there are other parsers out there that are stricter, namely XML parsers. XML is a data format like JSON. In XML, there are no predefined tags and you can use it to create whatever shape you want to describe your data. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;person&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;Hasan&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;occupation&amp;gt;&lt;/span&gt;Software Engineer&lt;span class="nt"&gt;&amp;lt;/occupation&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;hobbies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;hobby&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;Writing&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;reason&amp;gt;&lt;/span&gt;It helps me shape my thinking&lt;span class="nt"&gt;&amp;lt;/reason&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;frequency&amp;gt;&lt;/span&gt;Few times a week&lt;span class="nt"&gt;&amp;lt;/frequency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/hobby&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;hobby&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;Video games&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;reason&amp;gt;&lt;/span&gt;It helps me relax and unwind&lt;span class="nt"&gt;&amp;lt;/reason&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;frequency&amp;gt;&lt;/span&gt;Few times a week&lt;span class="nt"&gt;&amp;lt;/frequency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/hobby&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;hobby&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;Cooking&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;reason&amp;gt;&lt;/span&gt;It helps me try new things and be creative&lt;span class="nt"&gt;&amp;lt;/reason&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;frequency&amp;gt;&lt;/span&gt;Almost everday&lt;span class="nt"&gt;&amp;lt;/frequency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/hobby&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/hobbies&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/person&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're more familiar with JSON, the equivalent is:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hasan"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"occupation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Software Engineer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hobbies"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Writing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"It helps me shape my thinking"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"frequency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Few times a week"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Video games"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"It helps me relax and unwind"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"frequency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Few times a week"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Cooking"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"It helps me try new things and be creative"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"frequency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Almost everyday"&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="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;There are no predefined tags in XML and if you notice, every tag that we created has a closing tag and this isn't optional [3]. It's because without it there would be no way to know where boundaries of different elements would be. With HTML, the parser uses rules of predefined elements to decide where the closing tags could go if it's not specified. If there aren't any predefined tags, the only way to reliably parse XML would be to expect explicit closing tags. So, what if you wrote HTML like it were XML so you gain all the benefits of XML? This would remove all ambiguity in your markup at the time of authoring it.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. XHTML &amp;amp; JSX
&lt;/h2&gt;

&lt;p&gt;This was the motivation behind XHTML, or the XML syntax for HTML [4]. You can write your markup strictly in the XML syntax, but it does have the drawback of not rendering anything if you get the markup wrong. With it, you’ve traded the flexibility of the HTML syntax for correctness and depending on the problems you’re facing, this might be worth it. The actual history of how the standards evolved is a little murky to me, but today you can have the better parts of both approaches and write your HTML markup using a mixture of traditional HTML syntax and an XML-like one. This compatibility was added in HTML5 to be more friendly to parser-agnostic markup, so it can be parsed by both HTML and XML parsers. The XML syntax inspired the JSX syntax with the advent of React [5], and even though strict XHTML is not in fashion anymore, it's impact can be felt by the templating syntaxes that it inspired.  &lt;/p&gt;

&lt;p&gt;With that context established, how would you define elements that can't have children according to the HTML specification, like &lt;code&gt;&amp;lt;br&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;, using the XML syntax? In the specification, these elements are called "void elements" [6]. XML syntax requires you to have closing tags, so how can you write this and remain parser agnostic? The XML syntax supports "self-closing" tags for this reason, so that would mean &lt;code&gt;&amp;lt;br&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; would become &lt;code&gt;&amp;lt;br /&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;input /&amp;gt;&lt;/code&gt;, respectively (on the other hand, the HTML parser is also smart enough to ignore the erroneous closing tags for void elements, but don't do this). This was added to HTML5, but there are still strict rules around it. There's a short list of predefined void elements and that's it. You cannot define any other elements as if they were void elements (for example, you can't do &lt;code&gt;&amp;lt;div /&amp;gt;&lt;/code&gt; instead of &lt;code&gt;&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt; for an empty &lt;code&gt;div&lt;/code&gt;), and this is the reason why my code snippet wasn't valid HTML.&lt;/p&gt;

&lt;p&gt;When the HTML parser encounters a non-void element with a self-closing tag, it treats it as if it was just an opening tag [7]. The parser then figures out where to put the closing tag like it normally would when you omit it, and that's why my snippet would've worked only some of the time if it were used directly in HTML. It worked reliably for me in Astro because Astro uses a JSX-like syntax, which would translate &lt;code&gt;&amp;lt;div /&amp;gt;&lt;/code&gt; to &lt;code&gt;&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;x-timer /&amp;gt;&lt;/code&gt; to &lt;code&gt;&amp;lt;x-timer&amp;gt;&amp;lt;/x-timer&amp;gt;&lt;/code&gt;. The more granular reason this syntax isn't supported for custom elements is because the parser cannot ensure that it can be a void element in the parsing phase [8] without running the &lt;code&gt;connectedCallback&lt;/code&gt;, and that doesn't happen until the layout and painting phase [9].&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Why it matters
&lt;/h2&gt;

&lt;p&gt;Does it really matter? I think when learning the fundamentals, it's important to be shown the right thing and then the alternatives for the very specific problems they solve. That's quite challenging today because of how ubiquitous non-standard technologies are, and how much they influence our understanding of the web platform. When I learnt HTML 15 years ago, I didn't spend long enough with it to notice things like void elements and optional tags. Having written JSX in React, Solid and Astro for years, my brain couldn't even understand why my mistake was a mistake; until I saw it pointed out in a video about WebC [8], which is a templating syntax that is much closer to native HTML than JSX is. &lt;/p&gt;

&lt;p&gt;A part of understanding the web is understanding how the browser handles what you throw at it. In &lt;a href="https://dev.to/hasanhaja/web-fundamentals-html-forms-3i5c"&gt;Web Fundamentals: HTML Forms&lt;/a&gt; we looked at how the browser takes your declarative HTML form and submits an HTTP request to the server when you submit it. In &lt;a href="https://dev.to/hasanhaja/web-fundamentals-web-components-part-1-3nlh"&gt;Web Fundamentals: Web Components Part 1&lt;/a&gt; we looked at how the browser parses and executes your custom element logic. This approach gives us a stronger foundation when trying to understand and evaluate some of the modern innovations in web development, because framework authors are building on top of the strengths and weaknesses of the platform. &lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;HTML tags are so fundamental to web development that the rules around it can often be overlooked, especially when you work with various templating languages regularly. By looking at how the browser parses HTML tags, we saw that the source of my error was not understanding the difference between HTML and JSX syntaxes. We built up to it by looking at the different considerations the different parsers have had to make and how all of that culminated in my misunderstanding of when it's okay to use self-closing tags. This was an example of the importance of questioning your most fundamental assumptions because that is how you keep growing and stay up-to-date.&lt;/p&gt;

&lt;p&gt;If you think of anything I've missed or just wanted to get in touch, you can reach me through a comment or &lt;a href="https://fosstodon.org/@hasanhaja" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Astro templating syntax [&lt;a href="https://docs.astro.build/en/core-concepts/astro-syntax/" rel="noopener noreferrer"&gt;Website&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;Where HTML beats C [&lt;a href="https://youtu.be/Gj5Q-x3OrWo" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;XML Tags [&lt;a href="https://www.w3.org/TR/REC-xml/#sec-starttags" rel="noopener noreferrer"&gt;Website&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;XHTML [&lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/XHTML" rel="noopener noreferrer"&gt;MDN&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;React JSX closing tags [&lt;a href="https://react.dev/learn/writing-markup-with-jsx#2-close-all-the-tags" rel="noopener noreferrer"&gt;Website&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;HTML spec on void elements [&lt;a href="https://html.spec.whatwg.org/#void-elements" rel="noopener noreferrer"&gt;Website&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;HTML spec on HTML parsing [&lt;a href="https://html.spec.whatwg.org/#parse-errors" rel="noopener noreferrer"&gt;Website&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;WebC is neat! with Zach Leatherman, Web Developer [&lt;a href="https://youtu.be/UqfU0L4KqRM?t=2241" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;Web Fundamentals: Web Components Part 1 [&lt;a href="https://dev.to/hasanhaja/web-fundamentals-web-components-part-1-3nlh"&gt;Website&lt;/a&gt;]&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>beginners</category>
      <category>webdev</category>
      <category>html</category>
      <category>webcomponents</category>
    </item>
    <item>
      <title>Web Fundamentals: Web Components Part 1</title>
      <dc:creator>Hasan Ali</dc:creator>
      <pubDate>Mon, 14 Aug 2023 10:08:00 +0000</pubDate>
      <link>https://forem.com/hasanhaja/web-fundamentals-web-components-part-1-3nlh</link>
      <guid>https://forem.com/hasanhaja/web-fundamentals-web-components-part-1-3nlh</guid>
      <description>&lt;p&gt;Liquid syntax error: Unknown tag 'endraw'&lt;/p&gt;
</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>webcomponents</category>
      <category>html</category>
    </item>
    <item>
      <title>Web Fundamentals: HTML Forms</title>
      <dc:creator>Hasan Ali</dc:creator>
      <pubDate>Tue, 11 Jul 2023 17:00:00 +0000</pubDate>
      <link>https://forem.com/hasanhaja/web-fundamentals-html-forms-3i5c</link>
      <guid>https://forem.com/hasanhaja/web-fundamentals-html-forms-3i5c</guid>
      <description>&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;What are forms?&lt;/li&gt;
&lt;li&gt;How do they work?&lt;/li&gt;
&lt;li&gt;How do we build it?&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  1. What are forms?
&lt;/h2&gt;

&lt;p&gt;Forms are one of the fundamental tools through which we interact with the web everyday [1]. It enables us to sign into apps, signup for newsletters, purchase things online and post comments to list a few. They're a key mechanism through which we send information from our browser to the server. &lt;/p&gt;

&lt;p&gt;In this tutorial, we're going to start with the simplest incarnation of forms and start to gain some appreciation for just how much functionality is built into the web platform. We'll go over how they work and how to build it. To follow along, you'll need a text editor, and a web server that can serve HTML files and handle requests. For my web server, I'm going to use Deno [2] and a web framework called Oak [3] to handle requests. On the frontend, it'll be plain HTML.&lt;/p&gt;

&lt;p&gt;Prerequisites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Basic understanding of how servers work [&lt;a href="https://developer.mozilla.org/en-US/docs/Learn/Common_questions/Web_mechanics/What_is_a_web_server" rel="noopener noreferrer"&gt;MDN&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;HTTP verbs [&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages#http_requests" rel="noopener noreferrer"&gt;MDN&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;An installation of Deno [&lt;a href="https://deno.land/manual@v1.35.0/getting_started/installation" rel="noopener noreferrer"&gt;Website&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. How do they work?
&lt;/h2&gt;

&lt;p&gt;Before we dive into forms, let's start with what happens when you visit a website:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You request for a website through the address bar or a link&lt;/li&gt;
&lt;li&gt;The server receives your request&lt;/li&gt;
&lt;li&gt;The server responds to your request by sending HTML as text&lt;/li&gt;
&lt;li&gt;Your browser renders that HTML&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When you visit a site, the request that you make to the server is a GET request. The server needs to be configured to handle requests based on the path of the website being accessed and also what type of request is being made (i.e. GET, POST or any of the other HTTP verbs [4]). For example, when you access the site &lt;code&gt;https://hasanhaja.com/picks&lt;/code&gt; then you make a GET request to the path &lt;code&gt;/picks&lt;/code&gt;. Where as if you go to &lt;code&gt;https://hasanhaja.com&lt;/code&gt; then you make a GET request to the path &lt;code&gt;/&lt;/code&gt; .&lt;/p&gt;

&lt;p&gt;With that context, let's follow an example where the HTML that is returned contains a form.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flfcxrw8pcgsh77cvjr9t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flfcxrw8pcgsh77cvjr9t.png" alt="A diagram showing the client and server relationship when the client requests for a web page. There are two boxes containing the text " width="800" height="241"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The form is denoted by the HTML tag &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; and nested within it are fields for the data we want to collect along with a submit button. The default behavior of forms is the construct a search query with the form data and make another GET request to the same path.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvoh7uvh0g1ycqptlwm30.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvoh7uvh0g1ycqptlwm30.png" alt="A color-coded diagram showing the client and server relationship when the client sends form data to the server. The HTML on the right hand side of the client box has been color-coded with orange to represent what will become the form data and blue to represent the submit action. Consequently, the arrow and the GET request label is blue with only the form data " width="800" height="125"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The server can be configured to identify the search query parameters so that it can respond as instructed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftge43yumdwbc28rnxis3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftge43yumdwbc28rnxis3.png" alt="The same diagram but with a response arrow from the server to the client with the text " width="800" height="188"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's also another way forms can be configured on the frontend. If we'd like the form to submit data by making a POST request to the same path with the form data in the body of the request, we can add the method attribute to the HTML like this &lt;code&gt;&amp;lt;form method="post"&amp;gt;&lt;/code&gt;. The server can be configured very similarly to handle this request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fanzrxp2z4p4oouuu9ku0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fanzrxp2z4p4oouuu9ku0.png" alt="For the form's POST version of the same client-server relationship diagram, the text above the arrow going from " width="800" height="148"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fli1wydxayccwe6if9o16.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fli1wydxayccwe6if9o16.png" alt="Similar response diagram with a response arrow from the server to the client with the text " width="800" height="277"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the above example, we only see the server return HTML as a response to the request, but in actuality it can be configured to perform any arbitrary action with the data. If this were a form to subscribe to a newsletter, then the server will perform the logic to add them to the email list and then send an HTML response to inform the user that the request was handled successfully. &lt;/p&gt;

&lt;p&gt;If the form field were a search filter in an e-commerce website, then the form data can be used to query the database for all the products related to the search and then the response can be constructed with these results. The applications for forms are endless, and that's precisely why they're everywhere. &lt;/p&gt;

&lt;h2&gt;
  
  
  3. How do we build it?
&lt;/h2&gt;

&lt;p&gt;Okay, let's build a form and start tinkering!&lt;/p&gt;

&lt;p&gt;To keep things as simple as possible, our form will contain the minimum number of fields required for it to qualify as a form. Here's a form with one text field [5] called "name":&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="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Form&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Our form&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;form&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Your details&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Name&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;button&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;Submit&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is what the HTTP request looks like when you click submit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;GET&lt;/span&gt; &lt;span class="nn"&gt;/?name=Hasan&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localhost:8000&lt;/span&gt;
&lt;span class="na"&gt;User-Agent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/114.0&lt;/span&gt;
&lt;span class="na"&gt;Accept&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8&lt;/span&gt;
&lt;span class="na"&gt;Accept-Language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;en-GB,en;q=0.5&lt;/span&gt;
&lt;span class="na"&gt;Accept-Encoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gzip, deflate, br&lt;/span&gt;
&lt;span class="na"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keep-alive&lt;/span&gt;
&lt;span class="na"&gt;Referer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://localhost:8000/&lt;/span&gt;
&lt;span class="na"&gt;Upgrade-Insecure-Requests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1&lt;/span&gt;
&lt;span class="na"&gt;Sec-Fetch-Dest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;document&lt;/span&gt;
&lt;span class="na"&gt;Sec-Fetch-Mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;navigate&lt;/span&gt;
&lt;span class="na"&gt;Sec-Fetch-Site&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;same-origin&lt;/span&gt;
&lt;span class="na"&gt;Sec-Fetch-User&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;?1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The HTML form constructed this entire HTTP request for us, and we only need to consume this on the server and send a response back. Let's look at the server code that can handle this form submission:&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;Application&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Router&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="s2"&gt;oak&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;router&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;Router&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&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="s2"&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;context&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;search&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&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;url&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;search&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;root&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;Deno&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;/static`&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;index.html&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="k"&gt;return&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;params&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;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;search&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;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&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="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;context&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;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang="en"&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset="utf-8" /&amp;gt;
    &amp;lt;meta name="viewport" content="width=device-width" /&amp;gt;
    &amp;lt;title&amp;gt;Form&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;Our form&amp;lt;/h1&amp;gt;
    &amp;lt;p&amp;gt;Hi there, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;!&amp;lt;/p&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
      `&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;app&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;Application&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&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="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&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="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;allowedMethods&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&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="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8000&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;.get&lt;/code&gt; handler for the path &lt;code&gt;"/"&lt;/code&gt; handles both the request for the page and the form submission. The conditional check &lt;code&gt;if (!search)&lt;/code&gt; is to distinguish between the form being requested or the form being submitted. When the page is requested without the search query parameters, it means the user is requesting the form, and when it's requested with the query parameters it means the form was submitted. In our implementation when we encounter a form submission, we construct HTML inline and respond with the name in the body of the HTML.&lt;/p&gt;

&lt;p&gt;The functionality of &lt;code&gt;context.send&lt;/code&gt; is to read the static HTML file from the server's filesystem and send it as text in the response. The Oak framework constructs an HTTP response with the appropriate headers to tell the browser what kind of content it will be rendering. You can debug the response headers in the Network tab of the browser's developer tools, and this is what it looks like for our form submission response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt; &lt;span class="ne"&gt;OK&lt;/span&gt;
&lt;span class="na"&gt;content-type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;text/html; charset=UTF-8&lt;/span&gt;
&lt;span class="na"&gt;vary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Accept-Encoding&lt;/span&gt;
&lt;span class="na"&gt;content-encoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gzip&lt;/span&gt;
&lt;span class="na"&gt;content-length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;187&lt;/span&gt;
&lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Mon, 10 Jul 2023 22:05:42 GMT&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Aside:&lt;/strong&gt; The &lt;code&gt;content-type: text/html&lt;/code&gt; is why responding with an inlined HTML string by setting the &lt;code&gt;context.response.body&lt;/code&gt; is both valid and equivalent to the &lt;code&gt;context.send&lt;/code&gt; approach. The &lt;code&gt;context&lt;/code&gt; is an implementation detail specific to the Oak framework, and your mileage may vary depending on how you handle HTTP requests.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This can get a little confusing on the server. It's confusing because when the form data is &lt;em&gt;sent&lt;/em&gt; to the server, the underlying HTTP verb is still &lt;em&gt;GET&lt;/em&gt;. That means when the server receives a GET request, it also needs to check if form data is being sent to it. Using GET with data is a perfectly valid pattern, but we can make our lives a little easier when dealing with forms to separate the logic for &lt;em&gt;getting&lt;/em&gt; forms and &lt;em&gt;posting&lt;/em&gt; data.&lt;/p&gt;

&lt;p&gt;To switch this form to send the form data via POST, we can add the "method" attribute with the value "post" to the &lt;code&gt;form&lt;/code&gt; tag.&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="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Form&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Our form&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"post"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Your details&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Name&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;button&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;Submit&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This seems too trivial and it is. The web platform is incredibly powerful and feature-rich. This little declaration entirely changed the communication mechanism. This is the HTTP request that gets sent to the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;POST&lt;/span&gt; &lt;span class="nn"&gt;/&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localhost:8000&lt;/span&gt;
&lt;span class="na"&gt;User-Agent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/114.0&lt;/span&gt;
&lt;span class="na"&gt;Accept&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8&lt;/span&gt;
&lt;span class="na"&gt;Accept-Language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;en-GB,en;q=0.5&lt;/span&gt;
&lt;span class="na"&gt;Accept-Encoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gzip, deflate, br&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/x-www-form-urlencoded&lt;/span&gt;
&lt;span class="na"&gt;Content-Length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10&lt;/span&gt;
&lt;span class="na"&gt;Origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://localhost:8000&lt;/span&gt;
&lt;span class="na"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keep-alive&lt;/span&gt;
&lt;span class="na"&gt;Referer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://localhost:8000/&lt;/span&gt;
&lt;span class="na"&gt;Upgrade-Insecure-Requests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1&lt;/span&gt;
&lt;span class="na"&gt;Sec-Fetch-Dest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;document&lt;/span&gt;
&lt;span class="na"&gt;Sec-Fetch-Mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;navigate&lt;/span&gt;
&lt;span class="na"&gt;Sec-Fetch-Site&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;same-origin&lt;/span&gt;
&lt;span class="na"&gt;Sec-Fetch-User&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;?1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this is the payload that goes with the request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;name=Hasan
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the server, we can handle this by adding another handler to our &lt;code&gt;router&lt;/code&gt;:&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="cm"&gt;/* --snip-- */&lt;/span&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="s2"&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;context&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;search&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&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;url&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;search&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;root&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;Deno&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;/static`&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;index.html&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="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="cm"&gt;/* --snip-- */&lt;/span&gt;
  &lt;span class="p"&gt;})&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="s2"&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;context&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;formBody&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;context&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="nf"&gt;body&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form&lt;/span&gt;&lt;span class="dl"&gt;"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;formBody&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="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;context&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;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang="en"&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;meta charset="utf-8" /&amp;gt;
        &amp;lt;meta name="viewport" content="width=device-width" /&amp;gt;
        &amp;lt;title&amp;gt;Form&amp;lt;/title&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;h1&amp;gt;Our form&amp;lt;/h1&amp;gt;
    &amp;lt;p&amp;gt;Hi there, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;!&amp;lt;/p&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;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="cm"&gt;/* --snip-- */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll leave the &lt;code&gt;.get&lt;/code&gt; handler as is so it can still serve the form to us, but when we submit it our &lt;code&gt;.post&lt;/code&gt; handler will handle that request. And that's how simple forms are!&lt;/p&gt;

&lt;p&gt;This is our entire server code that handles form submissions through the GET and POST method:&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;Application&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Router&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="s2"&gt;oak&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;router&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;Router&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&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="s2"&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;context&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;search&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&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;url&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;search&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;root&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;Deno&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;/static`&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;index.html&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="k"&gt;return&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;params&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;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;search&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;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&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="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;context&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;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang="en"&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset="utf-8" /&amp;gt;
    &amp;lt;meta name="viewport" content="width=device-width" /&amp;gt;
    &amp;lt;title&amp;gt;Form&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;Our form&amp;lt;/h1&amp;gt;
    &amp;lt;p&amp;gt;Hi there, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;!&amp;lt;/p&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;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;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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;context&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;formBody&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;context&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="nf"&gt;body&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form&lt;/span&gt;&lt;span class="dl"&gt;"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;formBody&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="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;context&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;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang="en"&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;meta charset="utf-8" /&amp;gt;
        &amp;lt;meta name="viewport" content="width=device-width" /&amp;gt;
        &amp;lt;title&amp;gt;Form&amp;lt;/title&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;h1&amp;gt;Our form&amp;lt;/h1&amp;gt;
    &amp;lt;p&amp;gt;Hi there, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;!&amp;lt;/p&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&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;Application&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&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="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&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="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;allowedMethods&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&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="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8000&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All the source code for this post can also be found on my &lt;a href="https://github.com/hasanhaja/web-fundamentals" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Forms are simple and they are deeply supported in the web platform. They enable us to capture data from users and this provides an interactive web experience. In this post, we looked at the anatomy of HTML form and how to build them. We saw two methods of submitting form data to the server and we saw that although the difference in code is very minimal, the communication of intent is far stronger with the POST method.&lt;/p&gt;

&lt;p&gt;HTML forms have a lot of features packed into them. There are a lot more fun and complex situations that require delving into them deeply. Examples of this can be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validating the input to ensure it's in the correct format before sending it to the server&lt;/li&gt;
&lt;li&gt;Validating the data on the server and responding with an error message if the validations fail &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stay tuned for follow up posts in this web fundamentals series.&lt;/p&gt;

&lt;p&gt;If you think of anything I've missed or just wanted to get in touch, you can reach me through a comment or &lt;a href="https://fosstodon.org/@hasanhaja" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Web forms — Working with user data [&lt;a href="https://developer.mozilla.org/en-US/docs/Learn/Forms" rel="noopener noreferrer"&gt;MDN&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;Deno [&lt;a href="https://deno.land/" rel="noopener noreferrer"&gt;Website&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;Oak framework [&lt;a href="https://oakserver.github.io/oak/" rel="noopener noreferrer"&gt;Website&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;HTTP verbs [&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages#http_requests" rel="noopener noreferrer"&gt;MDN&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;Form Input element [&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input" rel="noopener noreferrer"&gt;MDN&lt;/a&gt;]&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>beginners</category>
      <category>webdev</category>
      <category>deno</category>
      <category>html</category>
    </item>
    <item>
      <title>TDD isn't Agile</title>
      <dc:creator>Hasan Ali</dc:creator>
      <pubDate>Fri, 28 Apr 2023 22:49:48 +0000</pubDate>
      <link>https://forem.com/hasanhaja/tdd-isnt-agile-3i8p</link>
      <guid>https://forem.com/hasanhaja/tdd-isnt-agile-3i8p</guid>
      <description>&lt;p&gt;Tech Twitter would have me believe that test-driven development is the worst thing ever. Maybe it’s just trendy to hate it, or maybe it’s a historical thing. I don’t have that context. I’m an advocate for well-tested code and I work in an environment where our tests give us a lot of confidence. I’ve also worked in a place that only did manual testing when new features were added. Those weeks of hunting down bugs weren’t fun especially when the behavior of the new features were completely intertwined with existing features. Those were definitely places where automated tests could’ve saved us hundreds of hours of blood, sweat and tears. So, why do I still think test-driven development isn’t the answer?&lt;/p&gt;

&lt;p&gt;Before we start, let’s outline the test-driven development framework [1]:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read, understand, and process the feature or bug request&lt;/li&gt;
&lt;li&gt;Translate the requirement into a failing unit test&lt;/li&gt;
&lt;li&gt;Implement the code that makes that test pass with the simplest solution&lt;/li&gt;
&lt;li&gt;Refactor code&lt;/li&gt;
&lt;li&gt;Repeat&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;My first take to seeing this was this should be interpreted and not be regarded as a framework. It’s imperative for developers to understand the benefits of tests, even when in my experience the suggestion of writing tests causes them to shift uncomfortably in their seats. When you regard the framework loosely it encourages thinking of the testability of code when developing. Prescribing a stringent framework has put and will put people off from writing any tests in the first place. It’s important to try and understand the sentiment rather than brush it off with, “don’t knock it till you’ve tried it”. I think the reason is the framework itself isn’t congruent with the way people think through problems.&lt;/p&gt;

&lt;p&gt;In my experience, when faced with a problem, bug or feature the response usually isn’t:&lt;/p&gt;

&lt;p&gt;“What should I tackle next? Should I write another failing test or refactor?”&lt;/p&gt;

&lt;p&gt;It’s often:&lt;/p&gt;

&lt;p&gt;“Why is this happening? Will it work if I try this?”&lt;/p&gt;

&lt;p&gt;TDD isn’t the way we encourage anyone to solve problems, let alone engineers. We encourage them to experiment, tinker and try different things. We try and foster curiosity and also critical thinking. You could argue that that is what TDD posits, but it isn’t really; not if you adopt it strictly. “Read, understand and process” sounds like critical thinking, but what does “understand” and “process” even mean? Without experimentation, you might not be able to ask the right questions in the first place to properly understand the problem. Being tied to writing a test before trying something out introduces hurdles before it facilitates problem solving.&lt;/p&gt;

&lt;p&gt;We should definitely encourage engineers to write tests. It’s a valuable tool to document weird behaviors and edge cases, and to ensure engineers don’t accidentally refactor those considerations away. It’s a tool to introduce confidence into areas of your codebase that may inherently be more fragile. However, I don’t think it should be the primary mechanism through which we think about a problem. Even though writing tests first &lt;em&gt;might&lt;/em&gt; encourage exploratory questions that &lt;em&gt;may&lt;/em&gt; catch something, it’s often the case that you don’t think of all the edge cases. This isn’t a fault of TDD but this not something TDD can mitigate either [2], so why do twice the work? I think you’ll be in a good place if all you got out of TDD is “well-tested code is good”. If you’re being force-fed the TDD framework, then that by definition isn’t agile [3] because it puts process over people.&lt;/p&gt;

&lt;p&gt;If you think of anything I have missed or just wanted to get in touch, you can reach me through a comment or &lt;a href="https://fosstodon.org/@hasanhaja" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;5 Steps of test-driven development [&lt;a href="https://developer.ibm.com/articles/5-steps-of-test-driven-development/" rel="noopener noreferrer"&gt;Website&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;Difficulties in using TDD [&lt;a href="https://medium.com/@learnstuff.io/difficulties-in-using-tdd-41429cf1e6e3" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;Agile Manifesto [&lt;a href="https://agilemanifesto.org/" rel="noopener noreferrer"&gt;Website&lt;/a&gt;]&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@jkoblitz?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Julia Koblitz&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/RlOAwXt2fEA?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>agile</category>
      <category>testing</category>
      <category>productivity</category>
      <category>discuss</category>
    </item>
    <item>
      <title>React Server Components: The future of getting started with the web</title>
      <dc:creator>Hasan Ali</dc:creator>
      <pubDate>Sun, 23 Apr 2023 22:27:24 +0000</pubDate>
      <link>https://forem.com/hasanhaja/react-server-components-the-future-of-getting-started-with-the-web-573o</link>
      <guid>https://forem.com/hasanhaja/react-server-components-the-future-of-getting-started-with-the-web-573o</guid>
      <description>&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Where it began&lt;/li&gt;
&lt;li&gt;What Astro means for the web&lt;/li&gt;
&lt;li&gt;Scalable complexity&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Where it began
&lt;/h2&gt;

&lt;p&gt;If I were to start in web development today and I already knew some HTML, CSS and JavaScript, I’d be grateful to the person that convinces me to pick Astro over anything else.&lt;/p&gt;

&lt;p&gt;I didn’t learn web development this way. I knew the very basics of HTML and CSS when I started programming in Java. And even though I fell in love with the web for how accessible it is, I didn’t want to be the person that wrote markup, changed the color of buttons or thought about what feature works in what browser. If I were to come onto the scene, I’d do it in a sophisticated way. I’d do it in Java.&lt;/p&gt;

&lt;p&gt;I professionally built apps in Vaadin for a little over a year and as good as it was, it’s no longer for me and the things I’m building. What it did for me was it let me skip a whole lot of web fundamentals. I didn’t learn how forms worked. I didn’t know how to properly structure my markup to best keep things accessible. I didn’t even understand how to center a button component within a layout container because the docs assumed I understood enough of the web. I didn’t.&lt;/p&gt;

&lt;p&gt;When I started taking web development seriously, I tried to go back to the fundamentals but there was a popular new kid on the block causing a racket. React. I tried avoiding it so I could learn some JavaScript and semantic HTML, but I kept hearing, “it’s just JavaScript”. Fine, I guess this is a two in one offer. Little did I realize, I was adding newer and newer abstractions over what I had, and what I had was absolutely nothing. React felt amazing. I felt like I was close to the fundamentals again but I wasn’t really. This isn’t a critique or an anti-React piece. I’m grateful for the doors it opened for me, and to be fair, it’s still paying the bills. If I already had a good foundation, I would’ve been in a better position to reason about the decisions I’m accepting and endorsing in my applications when using React.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Astro means for the web
&lt;/h2&gt;

&lt;p&gt;This is the reason why I think Astro is a great starting point. If I had come into web development, and been in a situation where I had to build and publish a website quickly, Astro would have me covered. Sure, I ended up doing the same thing with Next.js but I see that as another layer of abstraction over React. For me, this entire house of cards was build on feeble web fundamentals that were derived from the shortcomings of the abstractions Vaadin proposed. I needed fewer abstractions, not more. The exact combination of stacks might be unique, but I don’t think I’m alone in this experience. I think like me, a lot of us ventured into web development with varying levels of exposure to the web, and got given abstraction over abstraction to churn out features and entire applications with very little room to breathe or think.&lt;/p&gt;

&lt;p&gt;If I had Astro, I could’ve had my component model abstraction. I could’ve learnt the benefits of scoped styling. I could’ve naturally sprinkled in the JavaScript exactly where I needed the interactivity. These are good and reasonable abstractions that empower you to build websites better than if you hand rolled them yourself. I strongly believe I would’ve spent more time learning about the web and all of the features it had to offer, while enjoying the benefits we’ve distilled from years of experimentation with tools like Angular, Gatsby, Next.js and Remix. As a novice, I would’ve had the best of both worlds with a package so simple that it would empower me to learn the web platform better.&lt;/p&gt;

&lt;p&gt;This is why I love Astro. I see Astro as the baseline for anyone coming into web development. If Astro is the starting point, we don’t have to do our awkward explanation of the “MPA” acronym: “we only call them multi-page applications because of single-page applications, which were the new kids on the block. Multi-page applications are how websites have always worked.” That’s a lot of indirection. Now, it’s back to being simple: “Astro is how websites have always worked.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Scalable complexity
&lt;/h2&gt;

&lt;p&gt;Once we’re rolling with Astro, we can sprinkle in some more complexity, because let’s be honest, we can’t help ourselves. The model lends itself to incrementally adding this only where you need it. This won’t safeguard you from doing something completely unreasonable, but it does give you sufficiently simple building blocks to keep the magic to a minimum, and this ensures you can always reason about your code.&lt;/p&gt;

&lt;p&gt;When you feel more comfortable at this level, the point now becomes less about Astro and more about the mental model of the Islands Architecture. Astro does the Islands Architecture well, but it isn’t unique to it. You can take this abstraction with you to other metaframewoks that support it and generally keep all of the benefits. We now know how the web works, serve different pages of your website, organize your code into components so you can reuse them, style these components without accidentally changing the style of another and add interactivity to different parts of the page. What more could you need?&lt;/p&gt;

&lt;p&gt;That depends on the complexity of what you’re building. The model we have with something like Astro is very much suitable for a website, and you can push that quite far. By being an MPA, each page has its own markup, styles and interactive bits. If we were map out an end-to-end of this, we’d have the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User requests a page&lt;/li&gt;
&lt;li&gt;Server responds with HTML, CSS and JavaScript&lt;/li&gt;
&lt;li&gt;The full page loads in the browser&lt;/li&gt;
&lt;li&gt;User clicks a link to another page&lt;/li&gt;
&lt;li&gt;Server responds with the HTML, CSS and JavaScript for that page&lt;/li&gt;
&lt;li&gt;The full page loads in the browser&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Even if you reuse the same interactive component on both pages, they’ll have no way of knowing they are the same component. You’ll need this if you’re building something that’s more of an SPA. A workaround would be to store the state of the component from the first page in the client’s browser (using session or local storage) and then resume that state when the same component loads on the second page. This still doesn’t give you a proper SPA-like experience and for that we’ll need to persist those islands. How do we do this and just load the HTML around it from the server?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Enter Server Components.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;React Server Components, or more generally, the Server Component architecture is what I think is the next iterative step in this journey. They encompass the Islands Architecture and extend it by persisting islands on page navigation [1] [2]. Notice, we’ve built up this complexity in stages just for what we’ve needed. At any point, we can drop all the way back down to output simple markup for any route within the same project. This model enables you to scale complexity up and all the way down when appropriate. There’s no magic in the mental model. There’s some implementation magic in how only the updated parts of the HTML from the server are patched in, but conceptually it’s dead simple. If you want markup, just use server components. If you want interactivity, then add client components for islands. And server components also have you covered when you want to persist those islands with SPA-like navigation.&lt;/p&gt;

&lt;p&gt;In terms of tools, Next.js 13 currently supports RSCs but because Server Components are an architecture [3], other metaframeworks could adopt it. For example, SolidStart has been exploring it since before RSCs dropped [3] and even the Astro team is exploring what client-side routing could look like in Astro [4].&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Coming into web development can be a daunting ordeal. If you focus on and build up your web fundamentals, you’ll be in a very good place to keep up with all the advances that happen in this space. Astro is a great piece of technology and it will instil in you a model of the web that is congruent with the web. The Islands Architecture lets you slowly scale up complexity only where you need it and it also makes understanding Server Components, the next step in this evolution, easier to understand and appreciate.&lt;/p&gt;

&lt;p&gt;If you think of anything I have missed or just wanted to get in touch, you can reach me through a comment or &lt;a href="https://fosstodon.org/@hasanhaja" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Dan Abramov’s explanation of the difference between Islands Architecture and Server Components [&lt;a href="https://twitter.com/dan_abramov/status/1649781019069784065?s=20" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;Dan Shappir’s explanation of the difference between Islands Architecture and Server Components [&lt;a href="https://twitter.com/DanShappir/status/1649784399540502529?s=20" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;Ryan Carniato: Into the Depths with Server Components and Functions [&lt;a href="https://www.youtube.com/watch?v=QS9yAsv1czg" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;Astro’s Roadmap: Client-side Routing [&lt;a href="https://github.com/withastro/roadmap/issues/532" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;]&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@sigmund?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Sigmund&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/8ts_MPjebEk?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>astro</category>
      <category>react</category>
      <category>webdev</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Why I need Zig</title>
      <dc:creator>Hasan Ali</dc:creator>
      <pubDate>Wed, 12 Apr 2023 13:45:30 +0000</pubDate>
      <link>https://forem.com/hasanhaja/why-i-need-zig-4cbc</link>
      <guid>https://forem.com/hasanhaja/why-i-need-zig-4cbc</guid>
      <description>&lt;p&gt;I've been thinking about why I feel more productive in some programming languages over others and this article by &lt;a href="https://twitter.com/chriskrycho" rel="noopener noreferrer"&gt;Chris Krycho&lt;/a&gt; of the &lt;a href="https://newrustacean.com/" rel="noopener noreferrer"&gt;New Rustacean Podcast&lt;/a&gt; made it click for me: &lt;a href="https://v5.chriskrycho.com/journal/some-thoughts-on-zig/" rel="noopener noreferrer"&gt;https://v5.chriskrycho.com/journal/some-thoughts-on-zig/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've been a programming language enthusiast since I started coding. A few years ago I decided to stop language hopping so much and work on building real things with a few of them. This helped me get through my masters with C++ and Python, and helped me start my career with Java. Now I spend a vast majority of my time writing TypeScript for work and personal projects, and this has been the most productive I've ever felt. Some of it comes down to experience for sure, but I think a big part of it is the size of the language.&lt;/p&gt;

&lt;p&gt;I struggled to move as quickly as I can in TypeScript when working in Java or C++, and I think that made each "learning rep" longer so I couldn't do as many reps. This made the learning feedback loop feel not as rewarding. With the experience I've accumulated now, I feel more confident that I'll have better luck with those languages for the same domain. Which brings me to Zig.&lt;/p&gt;

&lt;p&gt;I did a very narrow subset of Systems Programming in C++ and when I tried to get into it with Rust, I kept hitting a wall. I underestimated how steep the "steep learning curve" was. I've been playing with Zig for a couple days now and it's giving me the same productivity wins as TypeScript did in the beginning. I hope this will better build my confidence in the systems domain, so I can appreciate Rust more.&lt;/p&gt;

&lt;p&gt;With all that said, this isn't a "Language A is better than B"; I'm a technology pluralist. And as much as I love Rust, I think I need a bit of time with Zig first to help me grow.&lt;/p&gt;

&lt;p&gt;If you think of anything I have missed or just wanted to get in touch, you can reach me through a comment or &lt;a href="https://fosstodon.org/@hasanhaja" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@johncobb?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;John Cobb&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/ssAcdlJRsI4?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>zig</category>
      <category>rust</category>
      <category>productivity</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
