<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Thomas Broyer</title>
    <description>The latest articles on DEV Community by Thomas Broyer (@tbroyer).</description>
    <link>https://dev.to/tbroyer</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%2F2020%2Feb534b5b-b568-4287-af19-e76c88d34ce3.png</url>
      <title>DEV Community: Thomas Broyer</title>
      <link>https://dev.to/tbroyer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tbroyer"/>
    <language>en</language>
    <item>
      <title>How do HTML event handlers work?</title>
      <dc:creator>Thomas Broyer</dc:creator>
      <pubDate>Sun, 10 Nov 2024 22:54:02 +0000</pubDate>
      <link>https://dev.to/tbroyer/how-do-html-event-handlers-work-nok</link>
      <guid>https://dev.to/tbroyer/how-do-html-event-handlers-work-nok</guid>
      <description>&lt;p&gt;&lt;a href="https://html.spec.whatwg.org/multipage/webappapis.html#eventhandler" rel="noopener noreferrer"&gt;HTML event handlers&lt;/a&gt; are those &lt;code&gt;onxxx&lt;/code&gt; attributes and properties many of us are used to, but do you know how they actually work? If you're writing custom elements and would like them to have such event handlers, what would you have to do? And what would you possibly be unable to implement? What differences would there be from &lt;em&gt;native&lt;/em&gt; event handlers?&lt;/p&gt;

&lt;p&gt;Before diving in: if you just want something usable, I wrote &lt;a href="https://github.com/tbroyer/webfeet" rel="noopener noreferrer"&gt;a library&lt;/a&gt; that implements all this (&lt;a href="https://dev.to/tbroyer/making-web-component-properties-behave-closer-to-the-platform-c1n"&gt;and more&lt;/a&gt;) but first jump to the conclusion for the limitations; otherwise, read on.&lt;/p&gt;

&lt;h2&gt;
  
  
  High-level overview
&lt;/h2&gt;

&lt;p&gt;Before all, an event handler is a property on an object whose name starts with &lt;code&gt;on&lt;/code&gt; (followed by the event type) and whose value is a JS function (or null). When that object is an element, the element also has a similarly-named attribute whose value will be parsed as JavaScript, with a variable named &lt;code&gt;event&lt;/code&gt; whose value will be the current event being handled, and that can return &lt;code&gt;false&lt;/code&gt; to cancel the event (how many times have we seen those infamous &lt;code&gt;oncontextmenu="return false"&lt;/code&gt; to &lt;em&gt;disable right click&lt;/em&gt;?)&lt;/p&gt;

&lt;p&gt;Setting an event handler is equivalent to adding a listener (removing the previous one if any) for the corresponding event type.&lt;/p&gt;

&lt;p&gt;Quite simple, right? but the devil lies in the details!&lt;/p&gt;

&lt;p&gt;(fwiw, there are two special kinds of event handlers, &lt;code&gt;onerror&lt;/code&gt; and &lt;code&gt;onbeforeunload&lt;/code&gt;, that I won't talk about the here.)&lt;/p&gt;

&lt;h2&gt;
  
  
  In details
&lt;/h2&gt;

&lt;p&gt;Let's go through those details the devil hides in (in no particular order).&lt;/p&gt;

&lt;h3&gt;
  
  
  Globality
&lt;/h3&gt;

&lt;p&gt;All built-in event handlers on elements are &lt;em&gt;global&lt;/em&gt;, and available on every element (actually, every &lt;code&gt;HTMLElement&lt;/code&gt;; that excludes SVG and MathML elements). This include custom elements so you won't need to implement, e.g., an &lt;code&gt;onclick&lt;/code&gt; yourself, it's built into every element. This also implies that as new event handlers are added to HTML in the future, they might conflict with your own event handlers for a &lt;em&gt;custom event&lt;/em&gt; (this is also true of properties and methods that could later be added to the &lt;code&gt;Node&lt;/code&gt;, &lt;code&gt;Element&lt;/code&gt; and &lt;code&gt;HTMLElement&lt;/code&gt; interfaces though).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Custom elements already have all "native" event handlers built-in.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Conversely, this &lt;em&gt;globality&lt;/em&gt; isn't something you'll be able to implement for a &lt;em&gt;custom event&lt;/em&gt;: you can create an &lt;code&gt;onfoo&lt;/code&gt; event handler on your custom element, but you won't be able to put an &lt;code&gt;onfoo&lt;/code&gt; on a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; element and expect it to do anything useful.&lt;br&gt;
(Technically, you possibly could &lt;em&gt;monkey-patch&lt;/em&gt; the &lt;code&gt;HTMLElement.prototype&lt;/code&gt; and use a &lt;code&gt;MutationObserver&lt;/code&gt; to detect the attribute, but you'll still miss attributes on detached elements and, well, &lt;em&gt;monkey-patching&lt;/em&gt;… do I need to say more?)&lt;/p&gt;

&lt;p&gt;To avoid forward-incompatibility (be future-proof) you might want to name your event handler with a dash or other non-ASCII character in its attribute name, and maybe an uppercase character in its property name. When &lt;a href="https://github.com/WICG/webcomponents/issues/1029" rel="noopener noreferrer"&gt;custom attributes&lt;/a&gt; are a thing, then maybe this will also allow having such an attribute globally available on all elements. Not sure it's a good idea, if you ask me I think I'd just use a &lt;em&gt;simple&lt;/em&gt; name and hope HTML won't add a conflicting one in the future.&lt;/p&gt;
&lt;h3&gt;
  
  
  Return value
&lt;/h3&gt;

&lt;p&gt;We briefly talked about the return value of the event handler function above: if it returns &lt;code&gt;false&lt;/code&gt; then the event will be cancelled.&lt;/p&gt;

&lt;p&gt;It happens that we're talking about the exact &lt;code&gt;false&lt;/code&gt; value here, not just any &lt;em&gt;falsy&lt;/em&gt; value.&lt;/p&gt;

&lt;p&gt;Fwiw, by &lt;em&gt;cancelled&lt;/em&gt; here, we mean just as if the event handler function had called &lt;code&gt;event.preventDefault()&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Listener ordering
&lt;/h3&gt;

&lt;p&gt;When you set an event handler, it adds an event listener for the corresponding event, so if you set it in between two &lt;code&gt;element.addEventListener()&lt;/code&gt;, it'll be called in between the event listeners.&lt;/p&gt;

&lt;p&gt;Now if you set it to another value later on, it won't actually remove the listener for the previous value and add one for the new value; it will actually reuse the existing listener!&lt;br&gt;
This was likely some kind of optimization in browser engines in the past (from the time of Internet Explorer or even Netscape I suppose), but as websites relied on it it's now part of the spec.&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;events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="nx"&gt;element&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="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&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 1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onclick&lt;/span&gt; &lt;span class="o"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;replaced below&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// starts listening&lt;/span&gt;
&lt;span class="nx"&gt;element&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="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&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 3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onclick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&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 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;// doesn't reorder the listeners&lt;/span&gt;
&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&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="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// → ["click 1", "click 2", "click 3"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you remove an event handler (set the property to &lt;code&gt;null&lt;/code&gt; –wait, there's more about it, see below– or remove the attribute), the listener will be removed though.&lt;br&gt;
So if for any reason you want to make sure an event handler is added to the end of the listeners list, then first remove any previous value then set your own.&lt;/p&gt;
&lt;h3&gt;
  
  
  Non-function property values
&lt;/h3&gt;

&lt;p&gt;We talked about &lt;em&gt;setting an event handler&lt;/em&gt; and &lt;em&gt;removing an event handler&lt;/em&gt; already, but even there there are small details to account for.&lt;/p&gt;

&lt;p&gt;When you set an event handler's property, any object value (which include functions) will &lt;em&gt;set&lt;/em&gt; the event handler (and possibly add an event listener). When an event is dispatched, only function values will have any useful effect, but any object can be used to activate the corresponding event listener (and possibly later be replaced with a function value without reordering the listeners).&lt;/p&gt;

&lt;p&gt;Conversely, any non-object, non-function value will be &lt;a href="https://dev.to/tbroyer/making-web-component-properties-behave-closer-to-the-platform-c1n#type-coercion"&gt;&lt;em&gt;coerced&lt;/em&gt;&lt;/a&gt; to &lt;code&gt;null&lt;/code&gt; and will &lt;em&gt;remove&lt;/em&gt; the event handler.&lt;/p&gt;

&lt;p&gt;This means that &lt;code&gt;element.onclick = new Number(42)&lt;/code&gt; &lt;em&gt;sets&lt;/em&gt; the event handler (to some &lt;em&gt;useless&lt;/em&gt; value, but still starts listening to the event), and &lt;code&gt;element.onclick = 42&lt;/code&gt; &lt;em&gt;removes&lt;/em&gt; it (and &lt;code&gt;element.onclick&lt;/code&gt; then returns &lt;code&gt;null&lt;/code&gt;).&lt;/p&gt;
&lt;h3&gt;
  
  
  Invalid attribute values, lazy evaluation
&lt;/h3&gt;

&lt;p&gt;Attribute values are never &lt;code&gt;null&lt;/code&gt;, so they always &lt;em&gt;set&lt;/em&gt; an event handler (to &lt;em&gt;remove&lt;/em&gt; it, remove the attribute). They're also evaluated lazily: invalid values (that can't be parsed as JavaScript) will be stored internally until they're needed (either the property is read, or an event is dispatched that should execute the event handler), at which point they'll be tentatively evaluated.&lt;/p&gt;

&lt;p&gt;When the value cannot be parsed as JavaScript, an error is reported (to &lt;code&gt;window.onerror&lt;/code&gt; among others) and the event handler is replaced with &lt;code&gt;null&lt;/code&gt; but &lt;em&gt;won't&lt;/em&gt; remove the event handler!&lt;br&gt;
(so yes, you can have an event handler property returning &lt;code&gt;null&lt;/code&gt; while having it listen to the event, and not have the listener be reordered when set to another value)&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;events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="nx"&gt;element&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="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&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 1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;element&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;onclick&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;}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// invalid, but starts listening&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="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onclick&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// reports an error and logs null, but doesn't stop listening&lt;/span&gt;
&lt;span class="nx"&gt;element&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="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&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 3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onclick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&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 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;// doesn't reorder the listeners&lt;/span&gt;
&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&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="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// → ["click 1", "click 2", "click 3"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The error reports the original location of the value, that is the &lt;code&gt;setAttribute()&lt;/code&gt; call in a script, or even the attribute in the HTML, even though the value is actually evaluated much later.&lt;br&gt;
This is something that I don't think could be implemented in userland.&lt;/p&gt;
&lt;h3&gt;
  
  
  Scope
&lt;/h3&gt;

&lt;p&gt;We've said above that an &lt;code&gt;event&lt;/code&gt; variable is available in the script set as an attribute value, but that's not the only &lt;em&gt;variable&lt;/em&gt; in scope: every property of the current element is directly readable as a variable as well. Also in scope are properties of the associated &lt;code&gt;form&lt;/code&gt; element if the element is &lt;em&gt;form-associated&lt;/em&gt;, and properties of the &lt;code&gt;document&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This means that &lt;code&gt;&amp;lt;a onclick="alert(href)"&lt;/code&gt; will show the link's target URL, &lt;code&gt;&amp;lt;button onclick="alert(action)"&amp;gt;&lt;/code&gt; will show the form's target URL (as a side effect, you can also refer to other form elements by name), and &lt;code&gt;&amp;lt;span onclick="alert(location)"&amp;gt;&lt;/code&gt; will show the document's URL.&lt;/p&gt;

&lt;p&gt;This is more or less equivalent to evaluating the attribute value inside this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;with &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;with &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;with &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// evaluate attribute value here&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;Related to scope too is the script's &lt;em&gt;base URL&lt;/em&gt; that would be used when &lt;code&gt;import()&lt;/code&gt;ing modules with a relative URL.&lt;br&gt;
Browsers seem to behave differently already on that: Firefox resolves the path relative to the document URL, whereas Chrome and Safari fail to resolve the path to a URL (as if there was no base URL at all). I don't think anything can be done here in a userland implementation.&lt;/p&gt;
&lt;h3&gt;
  
  
  Function source text
&lt;/h3&gt;

&lt;p&gt;When the event handler has been set through an attribute, the function returned by the event handler property has a very specific &lt;em&gt;source text&lt;/em&gt; (which is exposed by its &lt;code&gt;.toString()&lt;/code&gt;), which is close to, but not exactly the same as what &lt;code&gt;new Function("event", attrValue)&lt;/code&gt; would do (declaring a function with an &lt;code&gt;event&lt;/code&gt; argument and the attribute's value as its body).&lt;/p&gt;

&lt;p&gt;You couldn't directly use &lt;code&gt;new Function("event", attrValue)&lt;/code&gt; anyway due to the scope you need to setup, but there's a trick to control the exact source text of a function so this isn't insurmoutable:&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;handlerName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onclick&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;attrValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;return false;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fn&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;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`return function &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;handlerName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;(event) {\n&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;attrValue&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n}`&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="nx"&gt;fn&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="c1"&gt;// → "function onclick(event) {\nreturn false;\n}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Content Security Policy
&lt;/h3&gt;

&lt;p&gt;Last, but not least, event handler attribute values are rejected early by a Content Security Policy (CSP): the violation will be reported as soon as the attribute is tentatively set, and this won't have any effect on the state of the event handler (that could have been set through the property).&lt;/p&gt;

&lt;p&gt;The CSP directive that controls event handler attributes is &lt;code&gt;script-src-attr&lt;/code&gt; (which falls back to &lt;code&gt;script-src&lt;/code&gt; if not set, or to &lt;code&gt;default-src&lt;/code&gt;). When implementing an event handler for a &lt;em&gt;custom event&lt;/em&gt; in a custom element, the attribute value will have to be evaluated by scripting though (through &lt;code&gt;new Function()&lt;/code&gt; most likely) so it will be controlled by &lt;code&gt;script-src&lt;/code&gt; that will have to include either an appropriate hash source, or &lt;code&gt;'unsafe-eval'&lt;/code&gt; (notice the difference from native event handlers that would use &lt;code&gt;'unsafe-inline'&lt;/code&gt;, not &lt;code&gt;'unsafe-eval'&lt;/code&gt;). Hash sources will be a problem though, because you'll have to evaluate not just the attribute's value, but a script that embeds the attribute's value (to set up the scope and source text). And you'd have to actually evaluate both to make sure the attribute value doesn't mess with your evaluated script (think SQL injection but on JavaScript syntax). This would mean that each event handler attribute would have to have two hash sources allowed in the &lt;code&gt;script-src&lt;/code&gt; CSP directive, one of them being dependent on the custom element's implementation of the event handler.&lt;/p&gt;

&lt;p&gt;An alternative would be to use a native event handler for parsing, but then the function would have that native event handler as its function name, and you'd have to make sure to use an element associated with the same form (if not using the custom element directly because e.g. you don't want to trigger mutation observers) to get the appropriate variables in scope.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recap: What does it mean for custom event handlers? &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;As seen above, it's not possible to fully implement event handlers for a custom event in a way that would make it indistinguishable from &lt;em&gt;native&lt;/em&gt; event handlers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;they won't be globally available on every element (except maybe in the future with &lt;em&gt;custom attributes&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;a Content Security Policy won't be able to use &lt;code&gt;script-src-attr&lt;/code&gt; on those custom event handlers, and if it uses hash sources, chances are that 2 hash sources will be need for each attribute value (one of them being dependent on the custom event handler implementation details)&lt;/li&gt;
&lt;li&gt;errors emitted by the scripts used as event handler attribute values won't point to the source of the attribute value&lt;/li&gt;
&lt;li&gt;an &lt;code&gt;import()&lt;/code&gt; with a relative URL, inside an event handler attribute value, won't behave the same as in a &lt;em&gt;native&lt;/em&gt; event handler&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first point alone (or the first two) might make one reevaluate the need for adding such event handlers at all.&lt;br&gt;
And if you're thinking about only implementing the property, think about what it brings compared to &lt;em&gt;just&lt;/em&gt; having users call &lt;code&gt;addEventListener()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That being said, &lt;a href="https://github.com/tbroyer/webfeet" rel="noopener noreferrer"&gt;I did the work&lt;/a&gt; (more as an exercise than anything else), so feel free to go ahead a implement event handlers for your custom elements.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>webcomponents</category>
      <category>eventhandler</category>
      <category>eventlistener</category>
    </item>
    <item>
      <title>Making Web Component properties behave closer to the platform</title>
      <dc:creator>Thomas Broyer</dc:creator>
      <pubDate>Sun, 21 Jan 2024 23:16:59 +0000</pubDate>
      <link>https://dev.to/tbroyer/making-web-component-properties-behave-closer-to-the-platform-c1n</link>
      <guid>https://dev.to/tbroyer/making-web-component-properties-behave-closer-to-the-platform-c1n</guid>
      <description>&lt;p&gt;Built-in HTML elements' properties all share similar behaviors, that don't come &lt;em&gt;for free&lt;/em&gt; when you write your own custom elements. Let's see what those behaviors are, why you'd want to implement them in your web components, and how to do it, including how some web component libraries actually don't allow you to mimic those behaviors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Built-in elements' behaviors &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I said it already: built-in elements' properties all share similar behaviors, but there are actually several different such shared behaviors. First, there are properties (known as &lt;em&gt;IDL attributes&lt;/em&gt; in the HTML specification) that &lt;em&gt;reflect&lt;/em&gt; attributes (also known as &lt;em&gt;content attributes&lt;/em&gt;); then there are other properties that are unrelated to attributes. One thing you won't find in built-in elements are properties whose value will change if an attribute change, but that &lt;em&gt;won't&lt;/em&gt; update the attribute value when they are changed themselves (in case you immediately thought of &lt;code&gt;value&lt;/code&gt; or &lt;code&gt;checked&lt;/code&gt; as counter-examples, the situation is actually a bit more complex: those attributes are reflected by the &lt;code&gt;defaultValue&lt;/code&gt; and &lt;code&gt;defaultChecked&lt;/code&gt; properties respectively, and the &lt;code&gt;value&lt;/code&gt; and &lt;code&gt;checked&lt;/code&gt; properties are based on an internal state and behave differently depending on whether the user already interacted with the element or not).&lt;/p&gt;

&lt;h3&gt;
  
  
  Type coercion
&lt;/h3&gt;

&lt;p&gt;But I'll start with another aspect that is shared by all of them, whether reflected or not: typing. DOM interfaces are defined using &lt;a href="https://webidl.spec.whatwg.org" rel="noopener noreferrer"&gt;WebIDL&lt;/a&gt;, that has types and &lt;em&gt;extended annotations&lt;/em&gt;, and defines mapping of those to JavaScript. &lt;a href="https://tc39.es/ecma262/#sec-ecmascript-language-types" rel="noopener noreferrer"&gt;Types in JavaScript&lt;/a&gt; are rather limited: null, undefined, booleans, IEEE-754 floating-point numbers, big integers, strings, symbols, and objects (including errors, functions, promises, arrays, and typed arrays). WebIDL on the other hand defines, among others, 13 different numeric types (9 integer types and 4 floating point ones) that can be further annotated to change their overflowing behavior, and several string types (including enumerations).&lt;/p&gt;

&lt;p&gt;The way those types are experienced by developers is that getting the property will always return a value of the defined type (that's easy, the element &lt;em&gt;owns&lt;/em&gt; the value), and setting it (if not read-only) will coerce the assigned value to the defined type. So if you want your custom element to &lt;em&gt;feel&lt;/em&gt; like a built-in one, you'll have to define a setter to coerce the value to some specific type. The underlying question is what should happen if someone assigns a value of an unexpected type or outside the expected value space?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Convert and validate the new value in a property custom setter.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You probably don't want to use the exact WebIDL coercion rules though, but similar, approximated, rules that will behave the same most of the time and only diverge on some edge cases. The reason is that WebIDL is really weird: for instance, by default, numeric values overflow by wrapping around, so assigning 130 to a &lt;code&gt;byte&lt;/code&gt; (whose value space ranges from -128 to 127) will coerce it to… -126! (128 wraps to -128, 129 to -127, and 130 to -126; and by the way 256 wraps to 0; for the curious, &lt;code&gt;BigInt.asIntN&lt;/code&gt; and &lt;code&gt;BigInt.asUintN&lt;/code&gt; will do such wrapping in JS, but you'll have to convert numbers to &lt;code&gt;BigInt&lt;/code&gt; and back); non-integer values assigned to integer types are truncated by default, except when the type is annotated with &lt;code&gt;[Clamp]&lt;/code&gt;, in which case they're rounded, with half-way values rounded towards even values (something that only happens &lt;em&gt;natively&lt;/em&gt; in JS when setting such non-integer values to typed arrays: &lt;code&gt;Math.round(2.5)&lt;/code&gt; is 3, but &lt;code&gt;Int8Array.of(2.5)[0]&lt;/code&gt; is 2).&lt;/p&gt;

&lt;p&gt;Overall, I feel like, as far as primitive/simple types are concerned, boolean, integers, double (not float), string (WebIDL's &lt;code&gt;DOMString&lt;/code&gt;), and enumerations are all that's needed; truncating (or rounding, but with JavaScript rules), and clamping or enforcing ranges for integers. In other words, wrapping integers around is just weird, and what matters is coercing to the appropriate type and value space. Regarding enumerations, they're probably best handled by the reflection rules though (see below), and treated only as strings: no single built-in element has a property of a type that's a WebIDL &lt;code&gt;enum&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reflected properties
&lt;/h3&gt;

&lt;p&gt;Now let's get back to reflected properties: most properties of built-in elements &lt;a href="https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#reflecting-content-attributes-in-idl-attributes" rel="noopener noreferrer"&gt;reflect attributes&lt;/a&gt; or similarly (but with specific rules) correspond to an attribute and change its value when set; non-reflected properties are those that either expose some internal state (e.g. the current value or validation state of a form field), computed value (from the DOM, such as the &lt;code&gt;selectedIndex&lt;/code&gt; of a &lt;code&gt;select&lt;/code&gt;, or the &lt;code&gt;cellIndex&lt;/code&gt; of a table cell) or direct access to DOM elements (elements of a form, rows of a table, etc.), or that access other reflected properties with a transformed value (such as the &lt;code&gt;valueAsDate&lt;/code&gt; and &lt;code&gt;valueAsNumber&lt;/code&gt; of &lt;code&gt;input&lt;/code&gt;). So if you want your custom element to &lt;em&gt;feel&lt;/em&gt; like a built-in one, you'll want to use similar &lt;em&gt;reflection&lt;/em&gt; wherever appropriate.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Have your properties reflect attributes by default.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The way reflection is defined is that the source of truth is the attribute value: getting the property will actually parse the attribute value, and setting the property will &lt;em&gt;stringify&lt;/em&gt; the value into the attribute. Note that this means possibly setting the attribute to an &lt;em&gt;invalid&lt;/em&gt; value that will be &lt;em&gt;corrected&lt;/em&gt; by the getter. An example of this is setting the &lt;code&gt;type&lt;/code&gt; property of an &lt;code&gt;input&lt;/code&gt; element to an unknown value: it will be reflected in the attribute as-is, but the getter will correct it &lt;code&gt;text&lt;/code&gt;. Another example where this is required behavior is with dependent attributes like those of &lt;code&gt;progress&lt;/code&gt; or &lt;code&gt;meter&lt;/code&gt; elements: without this you'd have to be very careful setting properties in the &lt;em&gt;right order&lt;/em&gt; to avoid invalid combinations and having your set value immediately rewritten, but this behavior makes it possible to update properties in any order as the interaction between them are resolved internally and exposed by the getters: you can for example set the &lt;code&gt;value&lt;/code&gt; to a value upper than &lt;code&gt;max&lt;/code&gt; (on getting, &lt;code&gt;value&lt;/code&gt; would be normalized to its default value) and then update the &lt;code&gt;max&lt;/code&gt; (on getting, value could now return the value you previously set, because it wasn't actually rewritten on setting). Actually, these are not &lt;em&gt;technically&lt;/em&gt; reflected then as they have specific rules, but at least they're consistent with &lt;em&gt;actual&lt;/em&gt; reflected properties; for the purpose of this article, I'll consider them as reflected properties though.&lt;/p&gt;

&lt;p&gt;This is at least how it &lt;em&gt;theoretically&lt;/em&gt; works; in practice, the parsed value can be &lt;em&gt;cached&lt;/em&gt; to avoid parsing every time the property is read; but note that there can be several properties reflecting the same attribute (the most known one probably being &lt;code&gt;className&lt;/code&gt; and &lt;code&gt;classList&lt;/code&gt; both reflecting the &lt;code&gt;class&lt;/code&gt; attribute). Reflected properties can also have additional options, depending on their type, that will change the behavior of the getter and setter, not unlike WebIDL extended attributes.&lt;/p&gt;

&lt;p&gt;Also note that HTML only defines reflection for a limited set of types (if looking only at primitive/simple types, only non-nullable and nullable strings and enumerations, &lt;code&gt;long&lt;/code&gt;, &lt;code&gt;unsigned long&lt;/code&gt;, and &lt;code&gt;double&lt;/code&gt; are covered, and none of the narrower integer types, big integers, or the &lt;code&gt;unrestricted double&lt;/code&gt; that allows &lt;code&gt;NaN&lt;/code&gt; and infinity).&lt;/p&gt;

&lt;p&gt;You can see how Mozilla tests the compliance of their built-in elements&lt;br&gt;
&lt;a href="https://github.com/mozilla/gecko-dev/blob/master/dom/html/test/reflect.js" rel="noopener noreferrer"&gt;in the Gecko repository&lt;/a&gt; (the &lt;code&gt;ok&lt;/code&gt; and &lt;code&gt;is&lt;/code&gt; assertions are defined in their &lt;a href="https://github.com/mozilla/gecko-dev/blob/master/testing/mochitest/tests/SimpleTest/SimpleTest.js" rel="noopener noreferrer"&gt;&lt;code&gt;SimpleTest&lt;/code&gt;&lt;/a&gt; testing framework). And here's the Web Platform Tests' &lt;a href="https://github.com/web-platform-tests/wpt/blob/master/html/dom/reflection.js" rel="noopener noreferrer"&gt;reflection harness&lt;/a&gt;, with data for each built-in element in sibling files, that &lt;a href="https://wpt.fyi/results/html/dom" rel="noopener noreferrer"&gt;almost every browser pass&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Events
&lt;/h3&gt;

&lt;p&gt;Most direct changes to properties and attributes don't fire events: user actions or method calls will both update a property &lt;strong&gt;and&lt;/strong&gt; fire an event, but changing a property programmatically generally won't fire any event. There are a few exceptions though: the events of type &lt;code&gt;ToggleEvent&lt;/code&gt; fired by changes to &lt;a href="https://html.spec.whatwg.org/multipage/popover.html#the-popover-attribute%3Aconcept-element-attributes-change-ext" rel="noopener noreferrer"&gt;the &lt;code&gt;popover&lt;/code&gt; attribute&lt;/a&gt; or &lt;a href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-details-element%3Aconcept-element-attributes-change-ext" rel="noopener noreferrer"&gt;the &lt;code&gt;open&lt;/code&gt; attribute of &lt;code&gt;details&lt;/code&gt; elements&lt;/a&gt;, or the &lt;code&gt;select&lt;/code&gt; event when changing the &lt;a href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#textFieldSelection%3Adom-textarea%2Finput-selectionstart-2" rel="noopener noreferrer"&gt;&lt;code&gt;selectionStart&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#textFieldSelection%3Adom-textarea%2Finput-selectionend-3" rel="noopener noreferrer"&gt;&lt;code&gt;selectionEnd&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#textFieldSelection:dom-textarea/input-selectiondirection-4" rel="noopener noreferrer"&gt;&lt;code&gt;selectionDirection&lt;/code&gt;&lt;/a&gt; properties of &lt;code&gt;input&lt;/code&gt; and &lt;code&gt;textarea&lt;/code&gt; elements (if you know of others, let me know); but notably changing the value of a form element programmatically won't fire a &lt;code&gt;change&lt;/code&gt; or &lt;code&gt;input&lt;/code&gt; event. So if you want your custom element to &lt;em&gt;feel&lt;/em&gt; like a built-in one, don't fire events from your property setters or other attribute changed callbacks, but fire an event &lt;em&gt;when&lt;/em&gt; (just after) you programmatically change them.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Don't fire events from your property setters or other attribute changed callbacks.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Why you'd want to implement those &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;If you're you (your team, your company) are the only users of the web components (e.g. building an application out of web components, or an &lt;em&gt;internal&lt;/em&gt; library of reusable components), then OK, don't use reflection if you don't need it, you'll be the only user anyway so nobody will complain. If you're publicly sharing those components, then my opinion is that, following the principle of least astonishment, you should aim at behaving more like built-in elements, and reflect attributes.&lt;/p&gt;

&lt;p&gt;Similarly, for type coercions, if you're the only users of the web components, it's ok to only rely on TypeScript (or Flow or whichever type-checker) to make sure you always pass values of the appropriate type to your properties (and methods), but if you share them publicly then you should in my opinion coerce or validate inputs, in which case you'd want to follow the principe of least astonishment as well, and thus use rules similar to WebIDL and reflection behaviors. This is particularly true for a library that can be used without specific tooling, which is generally the case for custom elements.&lt;/p&gt;

&lt;p&gt;For example, all the following design systems can be used without tooling (some of them provide ready-to-use bundles, others can be used through import maps): Google's &lt;a href="https://github.com/material-components/material-web/discussions/5239" rel="noopener noreferrer"&gt;Material Web&lt;/a&gt;, Microsoft's &lt;a href="https://github.com/microsoft/fluentui/tree/master/packages/web-components" rel="noopener noreferrer"&gt;Fluent UI&lt;/a&gt;, IBM's &lt;a href="https://carbondesignsystem.com/developing/frameworks/web-components/" rel="noopener noreferrer"&gt;Carbon&lt;/a&gt;, Adobe's &lt;a href="https://opensource.adobe.com/spectrum-web-components/" rel="noopener noreferrer"&gt;Spectrum&lt;/a&gt;, Nordhealth's &lt;a href="https://nordhealth.design/web-components/" rel="noopener noreferrer"&gt;Nord&lt;/a&gt;, &lt;a href="https://shoelace.style/" rel="noopener noreferrer"&gt;Shoelace&lt;/a&gt;, etc.&lt;/p&gt;
&lt;h2&gt;
  
  
  How to implement them &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Now that we've seen what we'd want to implement, and why we'd want to implement it, let's see &lt;em&gt;how&lt;/em&gt; to do it. First without, and then with libraries.&lt;/p&gt;

&lt;p&gt;I started collecting implementations that &lt;em&gt;strictly&lt;/em&gt; follow (as an exercise, not as a goal) the above rules in &lt;a href="https://github.com/tbroyer/custom-elements-reflection-tests" rel="noopener noreferrer"&gt;a GitHub repository&lt;/a&gt; (strictly because it directly reuses the above-mentioned Gecko and Web Platform Tests harnesses).&lt;/p&gt;
&lt;h3&gt;
  
  
  Vanilla implementation &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;In a &lt;em&gt;vanilla&lt;/em&gt; custom element, things are rather straightforward:&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;MyElement&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;get&lt;/span&gt; &lt;span class="nf"&gt;reflected&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;strVal&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="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reflected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;parseValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;strVal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;reflected&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="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;coerceType&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="c1"&gt;// …there might be additional validations here…&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;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;reflected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;stringifyValue&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;or with intermediate caching (note that the setter is identical, setting the attribute will trigger the &lt;code&gt;attributeChangedCallack&lt;/code&gt; which will close the loop):&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;MyElement&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="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;reflected&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;reflected&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="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;reflected&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;reflected&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="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;coerceType&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="c1"&gt;// …there might be additional validations here…&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;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;reflected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;stringifyValue&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="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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reflected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;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="c1"&gt;// Note: in this case, we know it can only be the attribute named "reflected"&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;reflected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseValue&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;And for a non-reflected property (here, a read-write property representing an internal state):&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;MyElement&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="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;nonReflected&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;nonReflected&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="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;nonReflected&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;reflected&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="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;coerceType&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="c1"&gt;// …there might be additional validations here…&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;nonReflected&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because many rules are common to many attributes (the &lt;code&gt;coerceType&lt;/code&gt; operation is defined by WebIDL, or using similar rules, and the HTML specification defines a handful of &lt;em&gt;microsyntaxes&lt;/em&gt; for the &lt;code&gt;parseValue&lt;/code&gt; and &lt;code&gt;stringifyValue&lt;/code&gt; operations), those could be packaged up in a helper library. And with decorators &lt;a href="https://github.com/tc39/proposal-decorators" rel="noopener noreferrer"&gt;coming to ECMAScript&lt;/a&gt; (and already available in TypeScript), those could be greatly simplified:&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;MyElement&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="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;reflectInt&lt;/span&gt; &lt;span class="nx"&gt;accessor&lt;/span&gt; &lt;span class="nx"&gt;reflected&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;int&lt;/span&gt; &lt;span class="nx"&gt;accessor&lt;/span&gt; &lt;span class="nx"&gt;nonReflected&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;I actually built such a library, mostly as an exercise (and I already learned a lot, most of the above details actually). It's currently not published on NPM but you can find it &lt;a href="https://github.com/tbroyer/webfeet" rel="noopener noreferrer"&gt;on Github&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  With a library &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Surprisingly, web component libraries don't really help us here.&lt;/p&gt;

&lt;p&gt;First, like many libraries nowadays, most expect people to just pass values of the appropriate types (relying on type checking through TypeScript) and basically leave you handling everything including how to behave in the presence of unexpected values. While it's OK, as we've seen above, in a range of situations, there are limits to this approach and it's unfortunate that they don't provide tools to make it easier at least coercing types.&lt;/p&gt;

&lt;p&gt;Regarding reflected properties, most libraries tend to discourage you from doing it, while (fortunately!) supporting it, if only minimally.&lt;/p&gt;

&lt;p&gt;All libraries (that I've looked at) support observed attributes though (changing the attribute value updates the property, but not the other way around), and most default to this behavior.&lt;/p&gt;

&lt;p&gt;Now let's dive into the &lt;em&gt;how-to&lt;/em&gt; with Lit, FAST, and then Stencil (other libraries left as a so-called exercise for the reader).&lt;/p&gt;

&lt;h4&gt;
  
  
  With Lit &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;By default, &lt;a href="https://lit.dev/docs/components/properties/" rel="noopener noreferrer"&gt;Lit reactive properties&lt;/a&gt; (annotated with &lt;code&gt;@property()&lt;/code&gt;) observe the attribute of the same (or configured) name, using a converter to parse the value if needed (by default only handling numbers through a plain JavaScript number coercion, booleans, strings, or possibly objects or arrays through &lt;code&gt;JSON.parse()&lt;/code&gt;; but a custom converter can be given). If your property is not associated to any attribute (but needs to be reactive to trigger a render when changed), then you can annotate it with &lt;code&gt;@property({ attribute: false })&lt;/code&gt; or &lt;code&gt;@state()&lt;/code&gt; (the latter is meant for internal state though, i.e. private properties).&lt;/p&gt;

&lt;p&gt;To make a reactive property &lt;a href="https://lit.dev/docs/components/properties/#reflected-attributes" rel="noopener noreferrer"&gt;reflect an attribute&lt;/a&gt;, you'll add &lt;code&gt;reflect: true&lt;/code&gt; to the &lt;code&gt;@property()&lt;/code&gt; options, and Lit will use the converter to stringify the value too. This won't be done immediately though, but only as part of Lit's reactive update cycle. This timing is a slight deviation compared to built-in elements that's probably acceptable, but it makes it harder to implement some reflection rules (those that set the attribute to a different value than the one returned by the getter) as the converter will always be called with the property value (returned by the getter, so after normalization). For a component similar to &lt;code&gt;progress&lt;/code&gt; or &lt;code&gt;meter&lt;/code&gt; with dependent properties, Lit recommends correcting the values in a &lt;code&gt;willUpdate&lt;/code&gt; callback (this is where you'd check whether the &lt;code&gt;value&lt;/code&gt; is valid with respect to the &lt;code&gt;max&lt;/code&gt; for instance, and possibly overwrite its value to bring it in-range); this means that attributes will have the corrected value, and this requires users to update all properties in the same &lt;em&gt;event loop&lt;/em&gt; (which will most likely be the case anyway).&lt;/p&gt;

&lt;p&gt;It should be noted that, surprisingly, Lit &lt;em&gt;actively&lt;/em&gt; discourages reflecting attributes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Attributes should generally be considered input to the element from its owner, rather than under control of the element itself, so reflecting properties to attributes should be done sparingly. It's necessary today for cases like styling and accessibility, but this is likely to change as the platform adds features like the &lt;code&gt;:state&lt;/code&gt; pseudo selector and the Accessibility Object Model, which fill these gaps.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No need to say I disagree.&lt;/p&gt;

&lt;p&gt;For type coercion and validation, Lit allows you to have &lt;a href="https://lit.dev/docs/components/properties/#accessors-custom" rel="noopener noreferrer"&gt;your own accessors&lt;/a&gt; (and version 3 makes it &lt;a href="https://lit.dev/docs/v3/releases/upgrade/#updates-to-lit-decorators" rel="noopener noreferrer"&gt;even easier&lt;/a&gt;), so everything's ok here, particularly for non-reflected properties:&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;MyElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;nonReflected&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;nonReflected&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="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;nonReflected&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="nd"&gt;state&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;nonReflected&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="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;coerceType&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="c1"&gt;// …there might be additional validations here…&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;nonReflected&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For those cases where you'd want the attribute to possibly have an &lt;em&gt;invalid&lt;/em&gt; value (to be corrected by the property getter), it would mean using a non-reactive property wrapping a private reactive property (this assumes Lit won't flag them as errors in future versions), and parsing the value in its getter:&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;MyElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;property&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reflected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;reflect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;accessor&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;reflected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;reflected&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="nf"&gt;parseValue&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;reflected&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;reflected&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="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;coerceType&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="c1"&gt;// …there might be additional validations here…&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;reflected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;stringifyValue&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;or with intermediate caching (note that the setter is identical):&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;MyElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;property&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reflected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;reflect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;accessor&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;reflected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;parsedReflected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;reflected&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="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;parsedReflected&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;reflected&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="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;coerceType&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="c1"&gt;// …there might be additional validations here…&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;reflected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;stringifyValue&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="nf"&gt;willUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changedProperties&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;changedProperties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#reflected&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;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;parsedReflected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseValue&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;reflected&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;It might actually be easier to directly set the attribute from the setter (and as a bonus behaving closer to built-in elements) and only rely on an &lt;em&gt;observed property&lt;/em&gt; from Lit's point of view (setting the attribute will trigger &lt;code&gt;attributeChangedCallback&lt;/code&gt; and thus Lit's &lt;em&gt;observation&lt;/em&gt; code that will use the converter and then set the property):&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;MyElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;property&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reflected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;converter&lt;/span&gt;&lt;span class="p"&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;parseValue&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="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;accessor&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;reflected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;reflected&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="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;reflected&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;reflected&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="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;coerceType&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="c1"&gt;// …there might be additional validations here…&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;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;reflected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;stringifyValue&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;Note that this is actually very similar to the approach in the vanilla implementation above but using Lit's own lifecycle hooks. It should also be noted that for a &lt;code&gt;USVString&lt;/code&gt; that contains a URL (where the attribute value is resolved to a URL relative to the document base URI) the value needs to be processed in the getter (as it depends on an external state –the document base URI– that could change independently from the element).&lt;/p&gt;

&lt;p&gt;A previous version of this article contained a different implementation that happened to be broken.&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;MyElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;reflected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;reflected&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="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;reflected&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="nd"&gt;property&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;reflected&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="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;coerceType&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="c1"&gt;// …there might be additional validations here…&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stringValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;stringifyValue&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="c1"&gt;// XXX: there might be a more optimized way&lt;/span&gt;
    &lt;span class="c1"&gt;// than stringifying and then parsing&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;reflected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stringValue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Avoid unnecessarily triggering attributeChangedCallback&lt;/span&gt;
    &lt;span class="c1"&gt;// that would reenter that setter.&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reflected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;stringValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;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;reflected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stringValue&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 implementation would for instance have the setter called with &lt;code&gt;null&lt;/code&gt; when the attribute is removed, which actually needs to behave differently than user code calling the setter with &lt;code&gt;null&lt;/code&gt;: in the former case the property should revert to its default value, in the latter case that &lt;code&gt;null&lt;/code&gt; would be coerced to the string &lt;code&gt;"null"&lt;/code&gt; or the numeric value &lt;code&gt;0&lt;/code&gt; and the attribute would be added back with that value.&lt;/p&gt;

&lt;p&gt;If we're OK only reflecting valid values to attributes, then we can fully use converters but things aren't necessarily simpler (we still need the custom setter for type coercion and validation, and marking the internal property as reactive to avoid triggering the custom setter when the attribute changes; we don't directly deal with the attribute but we now have to &lt;em&gt;normalize&lt;/em&gt; the value in the setter in the same way as stringifying it to the attribute and parsing it back, to have the getter return the appropriate value):&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;customConverter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;fromAttribute&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="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;parseValue&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="p"&gt;},&lt;/span&gt;
  &lt;span class="nf"&gt;toAttribute&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="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;stringifyValue&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="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;property&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;reflect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;customConverter&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;accessor&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;reflected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;reflected&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="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;reflected&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;reflected&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="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;coerceType&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="c1"&gt;// …there might be additional validations here…&lt;/span&gt;
    &lt;span class="c1"&gt;// XXX: this should use a more optimized conversion/validation&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;reflected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;stringifyValue&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;h4&gt;
  
  
  With FAST &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;I know &lt;a href="https://www.fast.design/" rel="noopener noreferrer"&gt;FAST&lt;/a&gt; is not used that much but I wanted to cover it as it seems to be the only library that &lt;a href="https://www.fast.design/docs/fast-element/defining-elements#customizing-attributes" rel="noopener noreferrer"&gt;reflects attributes by default&lt;/a&gt;. By default it won't do any type coercion unless you use the &lt;code&gt;mode: "boolean"&lt;/code&gt;, which works &lt;em&gt;almost&lt;/em&gt; like an HTML boolean attribute, except an attribute present but with the value &lt;code&gt;"false"&lt;/code&gt; will coerce to a property value of &lt;code&gt;false&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;Otherwise, it works more or less like Lit, with one big difference: the converter's &lt;code&gt;fromView&lt;/code&gt; is &lt;em&gt;also&lt;/em&gt; called when setting the property (this means that &lt;code&gt;fromView&lt;/code&gt; receives any &lt;em&gt;external&lt;/em&gt; value, not just string values from the attribute). But unfortunately this doesn't really help us as most coercion rules need to throw at one point and we want to do it only in the property setters, never when parsing attribute values; and those rules that don't throw will have possibly different values between the attribute and the property getter (push invalid value to the attribute, sanitize it on the property getter), or just behave differently between the property (e.g. turning a &lt;code&gt;null&lt;/code&gt; into &lt;code&gt;0&lt;/code&gt; or &lt;code&gt;"null"&lt;/code&gt;) and the attribute (where &lt;code&gt;null&lt;/code&gt; means the attribute is not set, and the property should then have its default value which could be different from &lt;code&gt;0&lt;/code&gt;, and will likely be different from &lt;code&gt;"null"&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;This means that in the end the solutions are almost identical to the Lit ones (here using TypeScript's &lt;em&gt;legacy&lt;/em&gt; decorators though; and applying the annotation on the &lt;em&gt;private&lt;/em&gt; property to avoid triggering the custom setter on attribute change):&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;FASTElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reflected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;_reflected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;reflected&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="nf"&gt;parseValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_reflected&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;reflected&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="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;coerceType&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="c1"&gt;// …there might be additional validations here…&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;_reflected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;stringifyValue&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;or with intermediate caching (note that the setter is identical):&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;FASTElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reflected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;_reflected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;_reflectedChanged&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_parsedReflected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseValue&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;private&lt;/span&gt; &lt;span class="nx"&gt;_parsedReflected&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;reflected&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_parsedReflected&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;reflected&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="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;coerceType&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="c1"&gt;// …there might be additional validations here…&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;__reflected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;stringifyValue&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;Or if you want immediate reflection to the attribute (the internal property can now be used to store the parsed value):&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;FASTElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reflected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fromView&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;fromView&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="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;parseValue&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="p"&gt;},&lt;/span&gt;
      &lt;span class="nf"&gt;toView&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="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// mandatory in the converter type&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should never be called&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;_reflected&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;reflected&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_reflected&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;reflected&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="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;coerceType&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="c1"&gt;// …there might be additional validations here…&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;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;reflected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;stringifyValue&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;Note that the internal property is not initialized, to avoid calling the converter's &lt;code&gt;fromView&lt;/code&gt;, and handled in the getter instead (our &lt;code&gt;fromView&lt;/code&gt; expects a string or null coming from the attribute, so we'd have to initialize the property with such a string value which would hurt readability of the code as that could be a value different from the one actually stored in the property and returned by the pblic property getter).&lt;/p&gt;

&lt;p&gt;If we're OK only reflecting valid values to attributes, then we can fully use converters but things aren't necessarily simpler (we still need the custom setter for type coercion and validation, and marking the internal property as reactive to avoid triggering the custom setter when the attribute changes; we don't directly deal with the attribute but we still need to call &lt;code&gt;stringifyValue&lt;/code&gt; as we know the converter's &lt;code&gt;fromView&lt;/code&gt; will receive the new value):&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customConverter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;fromView&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="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;parseValue&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="p"&gt;},&lt;/span&gt;
  &lt;span class="nf"&gt;toView&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="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;stringifyValue&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="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;FASTElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reflected &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;customConverter&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;_reflected&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;reflected&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_reflected&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;reflected&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="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;coerceType&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="c1"&gt;// …there might be additional validations here…&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;_reflected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;stringifyValue&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;For non-reflected properties, you'd want to use &lt;code&gt;@observable&lt;/code&gt; instead of &lt;code&gt;@attr&lt;/code&gt;, except that it doesn't work on custom accessors, so you'd have to &lt;a href="https://www.fast.design/docs/fast-element/observables-and-state#access-tracking" rel="noopener noreferrer"&gt;do it manually&lt;/a&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;FASTElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;_nonReflected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;nonReflected&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;track&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nonReflected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&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;_nonReflected&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;nonReflected&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="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;coerceType&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="c1"&gt;// …there might be additional validations here…&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;_nonReflected&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="nx"&gt;Observable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;notify&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nonReflected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  With Stencil &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;First a disclosure: I never actually used &lt;a href="https://stenciljs.com/" rel="noopener noreferrer"&gt;Stencil&lt;/a&gt;, only played with it a bit locally in a hello-world project while writing this post.&lt;/p&gt;

&lt;p&gt;Stencil is kind of special. It supports observable attributes through the &lt;code&gt;@Prop()&lt;/code&gt; decorator, and reflected ones through &lt;code&gt;@Prop({ reflect: true })&lt;/code&gt;. It will however reflect default values to attributes when the component initializes, doesn't support custom converters, and like FAST will convert an attribute value of &lt;code&gt;"false"&lt;/code&gt; to a boolean &lt;code&gt;false&lt;/code&gt;. You also have to add &lt;code&gt;mutable: true&lt;/code&gt; to the &lt;code&gt;@Prop()&lt;/code&gt; if the component modifies its value (Stencil assumes properties and attributes are inputs to the component, not state of the component).&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;@Prop()&lt;/code&gt; must be public too, and cannot have custom accessors. You can use a &lt;code&gt;@Watch()&lt;/code&gt; method to do some validation, but throwing from there won't prevent the property value from being updated; you can revert the property to the old value from the watch method, but other watch methods for the same property will then be called twice, and not necessarily in the correct order (depending on declaration order).&lt;/p&gt;

&lt;p&gt;You cannot expose properties on the element's API if they are not annotated with &lt;code&gt;@Prop()&lt;/code&gt;, making them at a minimum observe an attribute.&lt;/p&gt;

&lt;p&gt;In other words, a Stencil component &lt;strong&gt;cannot&lt;/strong&gt;, by design, &lt;em&gt;feel&lt;/em&gt; like a built-in custom element (another thing specific to Stencil: besides &lt;code&gt;@Prop()&lt;/code&gt; properties, you can expose methods through &lt;code&gt;@Method&lt;/code&gt; but they must be &lt;code&gt;async&lt;/code&gt;).&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>webcomponents</category>
      <category>lit</category>
      <category>stencil</category>
    </item>
    <item>
      <title>Improving a web component, one step at a time</title>
      <dc:creator>Thomas Broyer</dc:creator>
      <pubDate>Sat, 16 Dec 2023 19:12:52 +0000</pubDate>
      <link>https://dev.to/tbroyer/improving-a-web-component-one-step-at-a-time-2673</link>
      <guid>https://dev.to/tbroyer/improving-a-web-component-one-step-at-a-time-2673</guid>
      <description>&lt;p&gt;Earlier this month, &lt;a href="https://www.stefanjudis.com/"&gt;Stefan Judis&lt;/a&gt; published a small &lt;a href="https://www.stefanjudis.com/blog/a-web-component-to-make-your-text-sparkle/"&gt;web component that makes your text sparkle&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the spirit of so-called &lt;a href="https://blog.jim-nielsen.com/2023/html-web-components/"&gt;HTML web components&lt;/a&gt; which apparently often comes with some sort of aversion for the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM"&gt;shadow DOM&lt;/a&gt;, the element directly manipulates the light DOM. As a developer of web apps with heavy DOM manipulations, and lover of &lt;em&gt;the platform&lt;/em&gt;, this feels weird to me as it could possibly break so many things: other code that manipulates the DOM and now sees new elements and could also change them, handling of disconnection and reconnection of the element (as most such elements modify their children in the &lt;code&gt;connectedCallback&lt;/code&gt; without checking whether it had already been done), &lt;code&gt;MutationObserver&lt;/code&gt;, etc.&lt;/p&gt;

&lt;p&gt;The first thing that came to my mind was that shadow DOM, for all its drawbacks and bugs, was the perfect fit for such an element, and I wanted to update Stefan's element to use the shadow DOM instead. Then a couple days ago, &lt;a href="https://www.zachleat.com"&gt;Zach Leatherman&lt;/a&gt; published &lt;a href="https://www.zachleat.com/web/snow-fall/"&gt;a similar element&lt;/a&gt; that makes it snow on its content, and &lt;a href="https://piaille.fr/@tbroyer/111585454702562025"&gt;I was pleased&lt;/a&gt; to see he used shadow DOM to encapsulate (hide) the snowflakes. That was the trigger for me to actually take the time to revisit Stefan's &lt;code&gt;&amp;lt;sparkle-text&amp;gt;&lt;/code&gt; element, so here's a step by step of various improvements (in my opinion) I made.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclaimer before I begin: this not in any way a criticism of Stefan's work! On the contrary actually, it wouldn't have been possible without this prior work. I just want to show things that &lt;em&gt;I&lt;/em&gt; think could be improved, and this is all very much subjective.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I'll link to commits in &lt;a href="https://github.com/tbroyer/sparkly-text"&gt;my fork&lt;/a&gt; without any (intermediate) demo, as all those changes don't have much impact on the element's behavior, as seen by a reader of the web page (if you're interested in what it changes when looked at through the DevTools, then clone the repository, run &lt;code&gt;npm install&lt;/code&gt;, &lt;code&gt;npm run start&lt;/code&gt;, then checkout each commit in turn), except in some specific situations. The final state is available &lt;a href="https://tbroyer.github.io/sparkly-text/"&gt;here&lt;/a&gt; if you want to play with it in your DevTools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using shadow DOM
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/tbroyer/sparkly-text/commit/57ef19f625ce886e876a597e198cd4089152a99d"&gt;first step&lt;/a&gt; was moving the sparkles to shadow DOM, to avoid touching the light DOM. This involves of course attaching shadow DOM, with a &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; to let the light DOM show, and then changing where the sparkles are added, but also changing how CSS is handled!&lt;/p&gt;
Abridged diff of the changes (notably excluding CSS)


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;@@ -66,16 +62,21 @@&lt;/span&gt; class SparklyText extends HTMLElement {
 `;
     let sheet = new CSSStyleSheet();
     sheet.replaceSync(css);
&lt;span class="gd"&gt;-    document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
-    _needsStyles = false;
&lt;/span&gt;&lt;span class="gi"&gt;+    this.shadowRoot.adoptedStyleSheets = [sheet];
&lt;/span&gt;   }
&lt;span class="err"&gt;
&lt;/span&gt;   connectedCallback() {
&lt;span class="gi"&gt;+    if (this.shadowRoot) {
+      return;
+    }
+
&lt;/span&gt;     this.#numberOfSparkles = parseInt(
       this.getAttribute("number-of-sparkles") || `${this.#numberOfSparkles}`,
       10
     );
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+    this.attachShadow({ mode: "open" });
+    this.shadowRoot.append(document.createElement("slot"));
&lt;/span&gt;     this.generateCss();
     this.addSparkles();
   }
&lt;span class="p"&gt;@@ -99,7 +100,7 @@&lt;/span&gt; class SparklyText extends HTMLElement {
       Math.random() * 110 - 5
     }% - var(--_sparkle-base-size) / 2)`;
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;-    this.appendChild(sparkleWrapper);
&lt;/span&gt;&lt;span class="gi"&gt;+    this.shadowRoot.appendChild(sparkleWrapper);
&lt;/span&gt;     sparkleWrapper.addEventListener("animationend", () =&amp;amp;gt; {
       sparkleWrapper.remove();
     });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;In Stefan's version, CSS is injected to the document, with a boolean to make sure it's done only once, and styles are &lt;em&gt;scoped&lt;/em&gt; to &lt;code&gt;.sparkle-wrapper&lt;/code&gt; descendants of the &lt;code&gt;sparkle-text&lt;/code&gt; elements. With shadow DOM, we gain style encapsulation, so no need for that scoping, we can directly target &lt;code&gt;.sparkle-wrapper&lt;/code&gt; and &lt;code&gt;svg&lt;/code&gt; as they're in the shadow DOM, clearly separate from the HTML that had been authored. We need to do it for each element though (we'll improve that later), but we now need to make sure we initialize the shadow DOM only once instead (I'm going step by step, so leaving this in the &lt;code&gt;connectedCallback&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;As a side effect, this also fixes some edge-case bug where the CSS would apply styles to any descendant SVG of the element, whether a sparkle or not (this could have been fixed by only targetting SVG inside &lt;code&gt;.sparkle-wrapper&lt;/code&gt; actually); and of course with shadow DOM encapsulation, page author styles won't affect the sparkles either.&lt;/p&gt;

&lt;h2&gt;
  
  
  Small performance improvements
&lt;/h2&gt;

&lt;p&gt;Those are really small, and probably negligible, but I feel like they're good practice anyway so I didn't even bother measuring actually.&lt;/p&gt;

&lt;p&gt;First, as said above, the CSS needs to be somehow &lt;em&gt;injected&lt;/em&gt; into each element's shadow DOM, but the constructible stylesheet can actually be shared between all of them. I've thus split construction of the stylesheet with its adoption in the shadow DOM, and made sure construction was only made once. Again, to limit &lt;a href="https://github.com/tbroyer/sparkly-text/commit/783d76d4766b70d7ca2d7767d1950f27f6a20d24"&gt;the changes&lt;/a&gt;, everything's still in the same method, just move inside an &lt;code&gt;if&lt;/code&gt; (I think I would have personally constructed the stylesheet early, as soon as the script is loaded, rather than waiting for the element to actually be used; it probably doesn't make a huge difference).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;   generateCss() {
&lt;span class="gd"&gt;-    const css = `…`;
-    let sheet = new CSSStyleSheet();
-    sheet.replaceSync(css);
&lt;/span&gt;&lt;span class="gi"&gt;+    if (!sheet) {
+      const css = `…`;
+      sheet = new CSSStyleSheet();
+      sheet.replaceSync(css);
+    }
&lt;/span&gt;     this.shadowRoot.adoptedStyleSheets = [sheet];
   }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similarly, sparkles were created by &lt;code&gt;innerHTML&lt;/code&gt; the SVG into each. I &lt;a href="https://github.com/tbroyer/sparkly-text/commit/887cdeafd58807bb7d96104178a806f45c109353"&gt;changed that&lt;/a&gt; to using &lt;code&gt;cloneNode(true)&lt;/code&gt; on an element &lt;em&gt;prepared&lt;/em&gt; only once.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;   addSparkle() {
&lt;span class="gd"&gt;-    const sparkleWrapper = document.createElement("span");
-    sparkleWrapper.classList.add("sparkle-wrapper");
-    sparkleWrapper.innerHTML = this.#sparkleSvg;
&lt;/span&gt;&lt;span class="gi"&gt;+    if (!sparkleTemplate) {
+      sparkleTemplate = document.createElement("span");
+      sparkleTemplate.classList.add("sparkle-wrapper");
+      sparkleTemplate.innerHTML = this.#sparkleSvg;
+    }
+
+    const sparkleWrapper = sparkleTemplate.cloneNode(true);
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We actually don't even need the wrapper element, we could directly use the SVG &lt;a href="https://github.com/tbroyer/sparkly-text/commit/9daa0df820ad113a745d1be7ae01ce9b6cf00711"&gt;without wrapper&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling disconnection
&lt;/h2&gt;

&lt;p&gt;The element uses chained timers (a &lt;code&gt;setTimeout&lt;/code&gt; callback that itself ends up calling &lt;code&gt;setTimeout&lt;/code&gt; with the same callback, again and again) to re-add sparkles at random intervals (removing the sparkles is done as soon as the animation ends; and all of this is done only if the user didn't configure their browser to prefer reduced motion).&lt;/p&gt;

&lt;p&gt;If the element is removed from the DOM, this unnecessarily continues in the background and could create memory leaks (in addition to just doing unnecessary work). &lt;a href="https://github.com/tbroyer/sparkly-text/commit/dc4b731a3f33e5164b5f4d8cc867d76207069405"&gt;I started&lt;/a&gt; with a very small change: check whether the element is still connected to the DOM before calling adding the sparkle (and calling &lt;code&gt;setTimeout&lt;/code&gt; again). It could have been better (for some definition of better) to track the timer IDs so we could call &lt;code&gt;clearTimeout&lt;/code&gt; in &lt;code&gt;disconnectedCallback&lt;/code&gt;, but I feel like that would be unnecessarily complex.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;       const {matches:motionOK} = window.matchMedia('(prefers-reduced-motion: no-preference)');
&lt;span class="gd"&gt;-      if (motionOK) this.addSparkle();
&lt;/span&gt;&lt;span class="gi"&gt;+      if (motionOK &amp;amp;&amp;amp; this.isConnected) this.addSparkle();
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This handles disconnection (as could be done by any &lt;em&gt;destructive&lt;/em&gt; change to the DOM, like navigating with &lt;a href="https://turbo.hotwired.dev/"&gt;Turbo&lt;/a&gt; or &lt;a href="https://htmx.org/"&gt;htmx&lt;/a&gt;, I'm not even talking about using the element in a JavaScript-heavy web app) but not reconnection though, and we've exited early from the &lt;code&gt;connectedCallback&lt;/code&gt; to avoid initializing the element twice, so this change actually broke our component in these situations where it's moved around, or stashed and then reinserted. To fix that, we need to always call &lt;code&gt;addSparkles&lt;/code&gt; in &lt;code&gt;connectedCallback&lt;/code&gt;, so move all the rest into an &lt;code&gt;if&lt;/code&gt;, that's actually as simple as that… except that when the user prefers reduced motion, sparkles are never removed, so they keep piling in each time the element is connected again. One way to handle that, without introducing our housekeeping of individual timers, is to just remove all sparkles on disconnection. Either that or conditionally add them in &lt;code&gt;connectedCallback&lt;/code&gt; if either we're initializing the element (including attaching the shadow DOM) or the user doesn't prefer reduced motion. The difference between both approaches is in whether we want the small animation when the sparkles appear (and appearing at new random locations). &lt;a href="https://github.com/tbroyer/sparkly-text/commit/ba8652eb490c41940fd531e2e87c6711cb1cc8d9"&gt;I went with the latter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This still doesn't handle the situation where &lt;code&gt;prefers-reduced-motion&lt;/code&gt; changes while the element is displayed though: if it turns to &lt;code&gt;no-preference&lt;/code&gt;, then sparkles will start animating (due to CSS) then disappear at the end of their animation (due to JS listening to the &lt;code&gt;animationend&lt;/code&gt; event), and no other sparkle will be added (because the &lt;code&gt;setTimeout&lt;/code&gt; chain would have been broken earlier). I don't feel like it's worthy enough of a fix for such an element but it's also rather easy to handle so &lt;a href="https://github.com/tbroyer/sparkly-text/commit/e0412236e1e5d8870cee14d044368eed46a060b1"&gt;let's do it&lt;/a&gt;: listen to the media query change and start the timers whenever the user no longer prefers reduced motion.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;@@ -94,6 +94,19 @@&lt;/span&gt; connectedCallback() {
       );
       this.addSparkles();
     }
&lt;span class="gi"&gt;+
+    motionOK.addEventListener("change", this.motionOkChange);
+  }
+
+  disconnectedCallback() {
+    motionOK.removeEventListener("change", this.motionOkChange);
+  }
+
+  // Declare as an arrow function to get the appropriate 'this'
+  motionOkChange = () =&amp;gt; {
+    if (motionOK.matches) {
+      this.addSparkles();
+    }
&lt;/span&gt;   }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Browser compatibility
&lt;/h2&gt;

&lt;p&gt;Constructible stylesheets aren't supported in Safari 16.3 and earlier (and possibly other browsers). To avoid the code failing and strange things (probably, I haven't tested) happening, I started by &lt;a href="https://github.com/tbroyer/sparkly-text/commit/59062d60e228111a9d00e5dd47695d0855cb937f"&gt;bailing out early&lt;/a&gt; if the browser doesn't support constructible stylesheets (the element would then just do nothing; I could have actually even avoided registering it at all). Fwiw, I borrowed the check from Zach's &lt;code&gt;&amp;lt;snow-fall&amp;gt;&lt;/code&gt; which works this way already (thanks Zach). As an aside, it's a bit strange that the code assumed construtible stylesheets were available, but tested for the availability of the custom element registry 🤷&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;   connectedCallback() {
&lt;span class="gd"&gt;-    if (this.shadowRoot) {
&lt;/span&gt;&lt;span class="gi"&gt;+    // https://caniuse.com/mdn-api_cssstylesheet_replacesync
+    if (this.shadowRoot || !("replaceSync" in CSSStyleSheet.prototype)) {
&lt;/span&gt;       return;
     }
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But Safari 16.3 and earlier still represent more than a third of users on macOS, and more than a quarter of users on iOS! (according to &lt;a href="https://caniuse.com/"&gt;CanIUse&lt;/a&gt;) To widen browser support, I therefore added &lt;a href="https://github.com/tbroyer/sparkly-text/commit/e5785ef938678e55c9b039dad518f69bd40075ea"&gt;a workaround&lt;/a&gt;, which consists of injecting a &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; element in the shadow DOM. Contrary to the constructible stylesheet, styles cannot be shared by all elements though, as we've seen above, so we only conditionally fallback to that approach, and continue using a constructible stylesheet everywhere it's supported.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;-      sheet = new CSSStyleSheet();
-      sheet.replaceSync(css);
&lt;/span&gt;&lt;span class="gi"&gt;+      if (supportsConstructibleStylesheets) {
+        sheet = new CSSStyleSheet();
+        sheet.replaceSync(css);
+      } else {
+        sheet = document.createElement("style");
+        sheet.textContent = css;
+      }
&lt;/span&gt;     }
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;-    this.shadowRoot.adoptedStyleSheets = [sheet];```
&lt;/span&gt;
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+    if (supportsConstructibleStylesheets) {
+      this.shadowRoot.adoptedStyleSheets = [sheet];
+    } else {
+      this.shadowRoot.append(sheet.cloneNode(true));
+    }
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Other possible improvements
&lt;/h2&gt;

&lt;p&gt;I stopped there but there's still room for improvement.&lt;/p&gt;

&lt;p&gt;For instance, the &lt;code&gt;number-of-sparkles&lt;/code&gt; attribute is read once when the element is connected, so changing the attribute afterwards won't have any effect (but will have if you disconnect and then reconnect the element). To handle that situation (if only because you don't control the order of initialization when that element is used within a JavaScript-heavy application with frameworks like React, Vue or Angular), one would have to listen to the attribute change and update the number of sparkles dynamically. This could be done either by removing all sparkles and recreating the correct number of them (with &lt;code&gt;addSparkles()&lt;/code&gt;), but this would be a bit &lt;em&gt;abrupt&lt;/em&gt;, or by reworking entirely how sparkles are managed so they could adapt dynamically (don't recreate a sparkle, let it &lt;em&gt;expire&lt;/em&gt;, when changing the number of sparkles down, or create just as many sparkles as necessary when changing it up). I feel like this would bump complexity by an order of magnitude, so it's probably not worth it for such an element.&lt;/p&gt;

&lt;p&gt;The number of sparkles could also be controlled by a property &lt;a href="https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#reflecting-content-attributes-in-idl-attributes"&gt;reflecting&lt;/a&gt; the attribute; that would make the element more similar to built-in elements. Once the above is in place, this hopefully shouldn't be too hard.&lt;/p&gt;

&lt;p&gt;That number of sparkles is expected to be, well, a number, and is currently parsed with &lt;code&gt;parseInt&lt;/code&gt;, but the code doesn't handle parsing errors and could set the number of sparkles to &lt;code&gt;NaN&lt;/code&gt;. Maybe we'd prefer using the default value in this case, and similarly for a zero or negative value; basically defining the attribute as a &lt;a href="https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#limited-to-only-non-negative-numbers-greater-than-zero-with-fallback"&gt;number limited  to only positive numbers with fallback&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;All this added complexity is, to me, what separates so-called &lt;em&gt;HTML web components&lt;/em&gt; from others: they're designed to be used from HTML markup and not (or rarely) manipulated afterwards, so shortcuts can be taken to keep them simple.&lt;/p&gt;

&lt;p&gt;Still speaking of that number of sparkles, the timers that create new sparkles are entirely disconnected from the animation that also makes them disappear. The animation length is actually configurable through the &lt;code&gt;--sparkly-text-animation-length&lt;/code&gt; CSS custom property, but the timers delay is not configurable (a random value between 2 and 3 seconds). This means that if we set the animation length to a higher value than 3 seconds, there will actually be more sparkles than the configured number, as new sparkles will be added before the previous one has disappeared. There are several ways to &lt;em&gt;fix&lt;/em&gt; this (&lt;strong&gt;if&lt;/strong&gt; we think it's a bug –this is debatable!– and is worth fixing): for instance we could use &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API"&gt;the Web Animations API&lt;/a&gt; to read the computed timing of the animation and compute the timer's delay based on this value. Or we could let the animation repeat and move the element on &lt;code&gt;animationiteration&lt;/code&gt;, rather than remove it and add another, and to add some randomness it could be temporarily paused and then restarted if we wanted (with a timer of some random delay). The code would be much different, but not necessarily more complex.&lt;/p&gt;

&lt;p&gt;Regarding the animation events (whether &lt;code&gt;animationend&lt;/code&gt; like it is now, or possibly &lt;code&gt;animationiteration&lt;/code&gt;), given that they bubble, they could be listened to on a single parent (the element itself –filtering out possible animations on light DOM children– or an intermediate element inserted to contain all sparkles). This could hopefully simplify the code handling each sparkle.&lt;/p&gt;

&lt;p&gt;Last, but not least, the &lt;code&gt;addSparkles&lt;/code&gt; and &lt;code&gt;addSparkle&lt;/code&gt; methods could be made private, as there's no reason to expose them in the element's API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final words
&lt;/h2&gt;

&lt;p&gt;Had I started from scratch, I probably wouldn't have written the element the same way. I tried to keep the changes small, one step at a time, rather than doing a big refactoring, or starting from scratch and comparing the outcome to the original, as my goal was to specifically show what I think could be improved and how it wouldn't necessarily involve big changes. Going farther, and/or possibly using a helper library (&lt;a href="https://dev.to/tbroyer/the-benefits-of-web-component-libraries-527h"&gt;I have written earlier&lt;/a&gt; about their added value), is left as an exercise for the reader.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>webcomponents</category>
    </item>
    <item>
      <title>Beyond the login page</title>
      <dc:creator>Thomas Broyer</dc:creator>
      <pubDate>Wed, 29 Nov 2023 19:54:40 +0000</pubDate>
      <link>https://dev.to/tbroyer/beyond-the-login-page-4hjd</link>
      <guid>https://dev.to/tbroyer/beyond-the-login-page-4hjd</guid>
      <description>&lt;p&gt;There are many blog posts floating around about “adding authentication to your application”, be it written in Node.js, ASP.NET, Java with Spring Boot, JS in the browser talking to a JSON-based Web API on the server, etc. Most of them handle the login page and password storage, and sometimes logout and a user registration page. But authentication is actually much more than that!&lt;/p&gt;

&lt;p&gt;Don't get me wrong, it's great that we can describe in a single blog post how to do such things, but everyone should be aware that this is actually just the beginning of the journey, and most of the time those blog posts don't have any such warnings.&lt;/p&gt;

&lt;p&gt;So here are some things to think about when “adding authentication to your application”:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;are you sure you store passwords securely? and verify them securely?&lt;/li&gt;
&lt;li&gt;is your logout secure? (ideally cannot be abused by tricking you just clicking a link on a mail or random site)&lt;/li&gt;
&lt;li&gt;are passwords robust?

&lt;ul&gt;
&lt;li&gt;put a lower bound on password length (NIST &lt;a href="https://pages.nist.gov/800-63-3/sp800-63b.html#5-authenticator-and-verifier-requirements"&gt;recommends&lt;/a&gt; a minimum of 8 characters); don't set an upper bound, or if you really want to make sure it's high enough (NIST recommends accepting at least 64 characters)&lt;/li&gt;
&lt;li&gt;if possible, check passwords (at registration or change) against known compromised passwords (use &lt;a href="https://haveibeenpwned.com/Passwords"&gt;Pwned Passwords&lt;/a&gt; or similar)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;how well do you handle non-ASCII characters? For example, macOS and Windows encode diacritics differently, so make sure that someone who signed up on one device will be able to sign in on another (put differently, use &lt;a href="https://en.wikipedia.org/wiki/Unicode_equivalence"&gt;Unicode normalization&lt;/a&gt; on inputs; NIST recommends using NFKC or NFKD)&lt;/li&gt;
&lt;li&gt;do you have a form to securely change the password? (when already authenticated)&lt;/li&gt;
&lt;li&gt;are your forms actually compatible with password managers?&lt;/li&gt;
&lt;li&gt;do you protect against brute-force attacks? if you do (e.g. by locking out accounts, or even just throttling), do you somehow protect legitimate users against DDoS?&lt;/li&gt;
&lt;li&gt;once authenticated, how do you maintain the authenticated state (&lt;em&gt;sessions&lt;/em&gt;; btw don't use &lt;a href="https://dev.to/tbroyer/what-are-jwt-nm0"&gt;JWTs&lt;/a&gt;)? and is this secure? (in other words, do you protect against &lt;a href="https://en.wikipedia.org/wiki/Session_fixation"&gt;session fixation&lt;/a&gt;? &lt;a href="https://en.wikipedia.org/wiki/Cross-site_request_forgery"&gt;cross-site request forgery&lt;/a&gt;?)&lt;/li&gt;
&lt;li&gt;how long are your &lt;em&gt;sessions&lt;/em&gt;? There's a balance between short and long sessions regarding security and convenience, but a choice needs to be made.&lt;/li&gt;
&lt;li&gt;do you have a mechanism to ask for re-authentication before sensitive actions?&lt;/li&gt;
&lt;li&gt;what do you do if a user forgot their password? Password recovery generally requires an email address, do you have one? how can you make sure that the user didn't mistype it and you will actually be able to use it when they need it? Put differently: you need a secure email verification process before you can have a secure password reset process. Implementing those processes securely go beyond the scope of this post, but let's just say we've just come from one single blog post explaining how to “add authentication to your application” to a &lt;em&gt;series&lt;/em&gt; of blog posts.&lt;/li&gt;
&lt;li&gt;by the way, now that you store an email address for password reset purpose, how can the user securely update it? and by that I also mean, how do you handle the case where the account got breached and the attacker changes the email address? There's unfortunately no simple answer to that, because there are a handful of cases to handle: the user may have lost access to the previous email, an attacker may have gained access to the previous email, the user may still have access to the previous email but have mistyped the new email, etc.&lt;/li&gt;
&lt;li&gt;speaking of changing passwords, do you make it easier for password managers? (spoiler: through a &lt;a href="https://w3c.github.io/webappsec-change-password-url/"&gt;&lt;code&gt;/.well-known/change-password&lt;/code&gt;&lt;/a&gt; URL)&lt;/li&gt;
&lt;li&gt;do you handle multi-factor authentication? do you plan on handling it in the future? If you use SMS to send one-time codes, can the device &lt;a href="https://web.dev/articles/sms-otp-form?hl=en"&gt;autofill the form&lt;/a&gt;?&lt;/li&gt;
&lt;li&gt;how about &lt;a href="https://passkeys.dev/"&gt;passkeys&lt;/a&gt;?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That being said, I don't think I ever implemented &lt;strong&gt;all&lt;/strong&gt; of the above &lt;em&gt;perfectly&lt;/em&gt;. There are always tradeoffs. But these are things to think about and make choices, and sometimes deliberate choices to postpone things (or just not implement them, after pondering the risks). Unfortunately, I did however see big mistakes in implementations of the various processes hinted above.&lt;/p&gt;

&lt;p&gt;Most of the time nowadays, I prefer offloading this to an identity provider, using &lt;a href="https://openid.net/connect/"&gt;OpenID Connect&lt;/a&gt; or soon &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/FedCM_API"&gt;Federated Credential Management (FedCM)&lt;/a&gt;, even if that means shipping an identity provider as part of the deliverables (I generally go with &lt;a href="https://keycloak.org/"&gt;Keycloak&lt;/a&gt;, with &lt;a href="https://github.com/adorsys/keycloak-config-cli"&gt;keycloak-config-cli&lt;/a&gt; to provision its configuration). I'm obviously biased though as I work in IT services, developping software mainly for intranets/extranets, and companies now increasingly have their own identity providers or at a minimum have that in their roadmap. So YMMV.&lt;/p&gt;

&lt;p&gt;And we've only talked about authentication, not even authorization!&lt;/p&gt;

&lt;p&gt;Some resources to go farther:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NIST &lt;a href="https://pages.nist.gov/800-63-3/sp800-63b.html"&gt;SP 800-63-3&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;OWASP:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html"&gt;Authentication Cheat Sheet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html"&gt;Password Storage Cheat Sheet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Multifactor_Authentication_Cheat_Sheet.html"&gt;Multifactor Authentication Cheat Sheet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Forgot_Password_Cheat_Sheet.html"&gt;Forgot Password Cheat Sheet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Credential_Stuffing_Prevention_Cheat_Sheet.html"&gt;Credential Stuffing Prevention Cheat Sheet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html"&gt;Session Management Cheat Sheet&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Troy Hunt's &lt;a href="https://www.troyhunt.com/everything-you-ever-wanted-to-know/"&gt;Everything you ever wanted to know about building a secure password reset feature&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Google:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://web.dev/articles/sign-in-form-best-practices?hl=en"&gt;Sign-in form best practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.dev/articles/sign-up-form-best-practices?hl=en"&gt;Sign-up form best practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.dev/articles/change-password-url?hl=en"&gt;Help users change passwords easily by adding a well-known URL for changing passwords&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.dev/articles/sms-otp-form?hl=en"&gt;SMS OTP form best practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.google.com/identity/passkeys/"&gt;Passwordless login with passkeys&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>authentication</category>
      <category>security</category>
      <category>login</category>
    </item>
    <item>
      <title>What are JWT?</title>
      <dc:creator>Thomas Broyer</dc:creator>
      <pubDate>Wed, 29 Nov 2023 19:50:38 +0000</pubDate>
      <link>https://dev.to/tbroyer/what-are-jwt-nm0</link>
      <guid>https://dev.to/tbroyer/what-are-jwt-nm0</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a translation of an article I wrote for our internal knowledge base at work, and that we later decided to &lt;a href="https://blog.atolcd.com/json-web-token-jwt/"&gt;publish&lt;/a&gt; (in French).&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;This article's goal is to present what JWTs are, whenever you face them. As we'll see, you won't deliberately choose to use JWTs in a project, and more importantly: you won't use JWTs as &lt;em&gt;session tokens&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is it?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;JSON Web Token (JWT) is a compact, URL-safe means of representing data to be transferred between two parties. The data is encoded as a JSON object that can be signed and/or encrypted.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is, paraphrased, the definition from the IETF standard that defines it (&lt;a href="https://datatracker.ietf.org/doc/html/rfc7519"&gt;RFC 7519&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  What's the point? What's the use case?
&lt;/h2&gt;

&lt;p&gt;So the goal is to transfer data, with some guarantees (or none, by the way): authenticity, integrity, even possibly confidentiality (if the message is encrypted). There are therefore many possible uses.&lt;/p&gt;

&lt;p&gt;JWT is thus used in OpenID Connect to encode the &lt;a href="https://openid.net/specs/openid-connect-core-1_0.html#IDToken"&gt;ID Token&lt;/a&gt; that forwards to the application information on the authentication process that took place at the identity server. OpenID Connect also uses JWT to encode &lt;em&gt;aggregated claims&lt;/em&gt;: information from other identity servers, for which we'll want to verify the authenticity and integrity.&lt;/p&gt;

&lt;p&gt;A JWT &lt;em&gt;might&lt;/em&gt; be used to authenticate to a server, such as with the OAuth 2 JWT Bearer (&lt;a href="https://datatracker.ietf.org/doc/html/rfc7523"&gt;RFC 7523&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Still in OAuth 2 land, access tokens &lt;em&gt;could&lt;/em&gt; themselves be JWTs (&lt;a href="https://datatracker.ietf.org/doc/html/rfc9068"&gt;RFC 9068&lt;/a&gt;), authorization request parameters &lt;em&gt;could&lt;/em&gt; be encoded as a JWT (&lt;a href="https://datatracker.ietf.org/doc/html/rfc9101"&gt;RFC 9101&lt;/a&gt;), as well as token introspection responses (&lt;a href="https://tools.ietf.org/html/draft-ietf-oauth-jwt-introspection-response"&gt;IETF draft: JWT Response for OAuth Token Introspection&lt;/a&gt;), and finally dynamic client registration uses a JWT to identify the software of which an instance attempts to register (so-called &lt;em&gt;software statements&lt;/em&gt; of &lt;a href="https://datatracker.ietf.org/doc/html/rfc7591"&gt;RFC 7591&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  How does it work?
&lt;/h2&gt;

&lt;p&gt;A JWT is composed of at least 2 parts, separated with a &lt;code&gt;.&lt;/code&gt; (dot), the first one always being the header. Each part is always encoded as &lt;em&gt;base64url&lt;/em&gt;, a variant of Base 64 with the &lt;code&gt;+&lt;/code&gt; and &lt;code&gt;/&lt;/code&gt; characters (that have special meaning in URLs) replaced with &lt;code&gt;-&lt;/code&gt; and &lt;code&gt;_&lt;/code&gt; respectively, and without the trailing &lt;code&gt;=&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There are two types of JWTs: JSON Web Signature (JWS, defined by &lt;a href="https://datatracker.ietf.org/doc/html/rfc7515"&gt;RFC 7515&lt;/a&gt;), and JSON Web Encryption (JWE, defined by &lt;a href="https://datatracker.ietf.org/doc/html/rfc7516"&gt;RFC 7516&lt;/a&gt;). The most common case is the JWS, composed of 2 or 3 parts: the header, the payload, and optionally the signature. JWEs are rarer (and more complex) so I won't talk about them here.&lt;/p&gt;

&lt;p&gt;The header, common to both types, describes the type of JWT (JWS or JWE) as well as the different signature, MAC, or encryption algorithms being used (codified by &lt;a href="https://datatracker.ietf.org/doc/html/rfc7518"&gt;RFC 7518&lt;/a&gt;), along with other useful information, as a JSON object.&lt;br&gt;&lt;br&gt;
In the case of JWS, we'll find the signature or MAC algorithm, possibly a key identifier (whenever multiple keys can be used, e.g. to allow for key rotation), or even a URL pointing to information about the keys (in JWKS format, defined by &lt;a href="https://datatracker.ietf.org/doc/html/rfc7517"&gt;RFC 7517&lt;/a&gt;), etc.&lt;/p&gt;

&lt;p&gt;In the case of JWS, the payload will generally be a JSON object with the transfered data (but technically could be another JWT).&lt;/p&gt;

&lt;p&gt;The third part is the signature or MAC. This part is absent if the header says the JWT is unprotected (&lt;code&gt;"alg":"none"&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;For debugging, one can use the &lt;a href="https://jwt.io/#debugger-io"&gt;JWT Debugger&lt;/a&gt; by Auth0 to decode JWTs &lt;em&gt;(beware not to use it with sensitive data, only on JWTs coming from test servers)&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;⚠️ JWT being almost always used in security-related contexts, handle them with care, specifically when it comes to their cryptographical components.&lt;/p&gt;

&lt;p&gt;One &lt;strong&gt;MUST&lt;/strong&gt; use dedicated libraries to manipulate JWTs, and be careful to use them correctly to avoid introducing vulnerabilities.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://datatracker.ietf.org/doc/html/rfc8725"&gt;RFC 8725&lt;/a&gt; has a set of best practices when manipulating and using JWTs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Criticism
&lt;/h2&gt;

&lt;p&gt;Numerous security experts, among them cryptographers, vehemently criticize JWTs and advise against their use.&lt;/p&gt;

&lt;p&gt;The main criticism relates to its complexity, even though it could look &lt;em&gt;simple&lt;/em&gt; to developers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;first, you need to know how to decode UTF-8 and JSON ; that's as many sources of bugs (and potential vulnerabilities).&lt;/li&gt;
&lt;li&gt;and of course because it's a generic format capable of signing and/or encrypting, or even not protecting anything at all (&lt;code&gt;"alg":"none"&lt;/code&gt;), with a &lt;a href="https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms"&gt;list of supported algorithms&lt;/a&gt; as long as your arm, you have to handle many cases (even if only to reject them).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a result, &lt;a href="https://0xn3va.gitbook.io/cheat-sheets/web-application/json-web-token-vulnerabilities"&gt;a number of vulnerabilities&lt;/a&gt; have been identified; among them (&lt;a href="https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/"&gt;identified as soon as March 2015&lt;/a&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;As the JWT itself declares the algorithm used to sign or encrypt it, software that receives it needs to partly trust it, or correctly check the used algorithm against a list of authorized algorithms. Because of its apparent simplicity, many libraries came out that didn't do those necessary checks and readily accepted unprotected JWTs (&lt;code&gt;"alg":"none"&lt;/code&gt;), allowing an attacker to use any JWT, without authenticity or integrity check. And as incredible as it may seem, &lt;a href="https://www.howmanydayssinceajwtalgnonevuln.com/"&gt;we still find&lt;/a&gt; vulnerable applications nowadays!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note: in the same way, the header can directly include the public key to be used to verify the signature. Using it will prove the integrity of the JWT, but not its authenticity as the signature could have been generated by anyone.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Another attack involes using the public key intended to verify an asymmetric signature (&lt;code&gt;"alg":"RS256"&lt;/code&gt; or &lt;code&gt;"alg":"ES256"&lt;/code&gt;) as a MAC key (&lt;code&gt;"alg":"HS256"&lt;/code&gt;): the application receiving the JWT could then mistakenly validate the MAC and allow the JWT in. Anybody could then create a JWS that would be accepted by the application, when that one &lt;em&gt;thinks&lt;/em&gt; it's verifying an asymmetric signature.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This vulnerability could be due to a misuse of the library used to verify JWTs, but also in some cases directly to its API that cannot tell between a public key and a shared secret (generally for the sake of making it easy to use).&lt;/p&gt;

&lt;p&gt;Aside: despite ID Tokens in OpenID Connect being JWTs, you won't actually need to verify their signature as you generally get them through HTTPS, that already guarantees authenticity and integrity (and confidentiality), which saves us from a whole class of vulnerabilities.&lt;/p&gt;

&lt;p&gt;Another criticism is due to the misuse of JWT, most often by ignorance or lack of expertise in software security: validity of a JWT is directly verifiable, without the need for a database of valid tokens or a validation service (authenticity and integrity are verifiable, so the validity period contained within in the JWT are &lt;em&gt;reliable&lt;/em&gt;), but it makes the JWT &lt;strong&gt;impossible to revoke&lt;/strong&gt; (unless you add such a mecanism –possibly based on the &lt;code&gt;jti&lt;/code&gt; claim, initially designed to protect against replay attacks– going against the whole reason for which JWT was chosen in the first place). If a JWT is used as a &lt;em&gt;session token&lt;/em&gt; for example, it then becomes impossible to sign out or terminate a session. In most use cases (in the specifications), a JWT is validated and used as soon as it's received from the issuer, so revocation is not even an issue. It's when a JWT is stored by the receiver for a later use that the problem arises (such as with a &lt;em&gt;session token&lt;/em&gt; or an &lt;em&gt;access token&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;Some articles critical of JWT:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://evertpot.com/jwt-is-a-bad-default/"&gt;JWT should not be your default for sessions&lt;/a&gt; (by Evert Pot, developper)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.okta.com/blog/2017/08/17/why-jwts-suck-as-session-tokens"&gt;Why JWTs Suck as Session Tokens&lt;/a&gt; (by Okta, vendor of an identity management platform)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://fly.io/blog/api-tokens-a-tedious-survey/#jwt"&gt;section "JSON Web Tokens" of "API Tokens: A Tedious Survey"&lt;/a&gt; (by Thomas H. Ptacek, security researcher)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.scottbrady91.com/jose/alternatives-to-jwts"&gt;Alternatives to JWTs&lt;/a&gt; (by Scott Brady, engineering manager specializing in identity management systems)&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/"&gt;Stop using JWTs for sessions&lt;/a&gt; and &lt;a href="http://cryto.net/~joepie91/blog/2016/06/19/stop-using-jwt-for-sessions-part-2-why-your-solution-doesnt-work/"&gt;Stop using JWT for sessions, part 2: Why your solution doesn’t work&lt;/a&gt; (on a web site surprisingly without HTTPS)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://paragonie.com/blog/2017/03/jwt-json-web-tokens-is-bad-standard-that-everyone-should-avoid"&gt;No Way, JOSE! Javascript Object Signing and Encryption is a Bad Standard That Everyone Should Avoid&lt;/a&gt; (by Scott “CiPHPerCoder” Arciszewski, cryptographer)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://scottarc.blog/2023/09/06/how-to-write-a-secure-jwt-library-if-you-absolutely-must/"&gt;How to Write a Secure JWT Library If You Absolutely Must&lt;/a&gt; (by the same author)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>jwt</category>
      <category>security</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How I teach Git</title>
      <dc:creator>Thomas Broyer</dc:creator>
      <pubDate>Sun, 26 Nov 2023 19:29:47 +0000</pubDate>
      <link>https://dev.to/tbroyer/how-i-teach-git-3nj3</link>
      <guid>https://dev.to/tbroyer/how-i-teach-git-3nj3</guid>
      <description>&lt;p&gt;I've been using Git for a dozen years. Eight years ago, I had to give a training session on Git (and GitHub) to a partner company about to create an open source project, and I'm going to tell you here about the way I taught it. Incidentally, we created internal training sessions at work since then that use the same (or similar) approach. That being said, I didn't invent anything: this is heavily inspired by what others wrote before, including &lt;a href="https://git-scm.com/book/" rel="noopener noreferrer"&gt;the &lt;cite&gt;Pro Git&lt;/cite&gt; book&lt;/a&gt;, though not in the same order, and that IMO can make a difference.&lt;/p&gt;

&lt;p&gt;The reason I'm writing this post is because over the years, I've kept seeing people actually &lt;em&gt;use&lt;/em&gt; Git without really understanding what they're doing; they'd either be locked into a very specific workflow they were told to follow, and unable to adapt to another that, say, an open source project is using (this also applies to open source maintainers not really understanding how external contributors use Git themselves), or they'd be totally lost if anything doesn't behave the way they thought it would, or if they made a mistake invoking Git commands. I've been inspired to write it down by &lt;a href="https://jvns.ca" rel="noopener noreferrer"&gt;Julia Evans&lt;/a&gt;' (renewed) interest in Git, as she sometimes ask for comments on social networks.&lt;/p&gt;

&lt;p&gt;My goal is not to actually teach you about Git, but more about sharing my approach to teaching Git, for others who will teach to possibly take inspiration. So if you're learning Git, this post was not written with you in mind (sorry), and as such might not be self-sufficient, but hopefully the links to other learning resources will be enough to fill the blanks are make it a helpful learning resource as well. If you're a visual learner, those external learning resources are illustrated, or even oriented towards visual learning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mental model
&lt;/h2&gt;

&lt;p&gt;Once we're clear why we use a VCS (Version Control System) where we record changes inside &lt;em&gt;commits&lt;/em&gt; (or in other words we &lt;em&gt;commit our changes&lt;/em&gt; to the history; I'm assuming some familiarity with this terminology), let's look at Git more specifically.&lt;/p&gt;

&lt;p&gt;One thing I think is crucial to understand Git, is getting an accurate mental model of the concepts behind it.&lt;/p&gt;

&lt;p&gt;First, that's not really important, but Git doesn't actually record &lt;em&gt;changes&lt;/em&gt;, but rather &lt;em&gt;snapshots&lt;/em&gt; of our files (at least conceptually; it will use &lt;em&gt;packfiles&lt;/em&gt; to store things efficiently and will actually store &lt;em&gt;changes&lt;/em&gt; –diffs– in some cases), and will generate diffs on-demand. This sometimes shows in the result of some commands though (like why some commands show one file removed and another added, while other commands show a file being renamed).&lt;/p&gt;

&lt;p&gt;Now let's dive into some Git concepts, or how Git implements some common VCS concepts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Commit
&lt;/h3&gt;

&lt;p&gt;A Git &lt;em&gt;commit&lt;/em&gt; is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one or more parent commit(s), or none for the very first commit (&lt;em&gt;root&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;a commit message&lt;/li&gt;
&lt;li&gt;an author and an author date (actually a timestamp with timezone offset)&lt;/li&gt;
&lt;li&gt;a committer and commit date&lt;/li&gt;
&lt;li&gt;and our files: their pathname relative to the repository root, their &lt;em&gt;mode&lt;/em&gt; (UNIX file-system permissions), and their content&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each commit is given an identifier determined by computing the SHA1 hash of this information: change a comma and you get a different SHA1, a different &lt;em&gt;commit object&lt;/em&gt;. (Fwiw, Git is slowly &lt;a href="https://git-scm.com/docs/hash-function-transition" rel="noopener noreferrer"&gt;moving to SHA-256&lt;/a&gt; as the hashing function).&lt;/p&gt;

&lt;h4&gt;
  
  
  Aside: how's the SHA1 computed?
&lt;/h4&gt;

&lt;p&gt;Git's storage is &lt;em&gt;content-adressed&lt;/em&gt;, meaning that each &lt;em&gt;object&lt;/em&gt; is stored with a name that's directly derived from its content, in the form of its SHA1 hash.&lt;/p&gt;

&lt;p&gt;Historically, Git stored everything in files, and we can still reason that way. A file's content is store as a &lt;em&gt;blob&lt;/em&gt;, a directory is stored as &lt;em&gt;tree&lt;/em&gt; (a text file that lists files in the directory with their name, mode, and the SHA1 of the &lt;em&gt;blob&lt;/em&gt; representing their content, and their subdirectories with their name and the SHA1 their &lt;em&gt;tree&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;If you want the details, Julia Evans wrote an amazing (again) &lt;a href="https://jvns.ca/blog/2023/09/14/in-a-git-repository--where-do-your-files-live-/" rel="noopener noreferrer"&gt;blog post&lt;/a&gt;; or you can read it &lt;a href="https://git-scm.com/book/en/v2/Git-Internals-Git-Objects" rel="noopener noreferrer"&gt;from the &lt;cite&gt;Pro Git&lt;/cite&gt; book&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgit-scm.com%2Fbook%2Fen%2Fv2%2Fimages%2Fcommit-and-tree.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgit-scm.com%2Fbook%2Fen%2Fv2%2Fimages%2Fcommit-and-tree.png" alt='A graph with 5 boxes organized in 3 columns, each box labelled with a 5-digit SHA1 prefix; the one on the left is sub-labelled "commit" and includes metadata "tree" with the SHA1 of the box in the middle, and "author" and "committer" both with value "Scott", and text "The initial commit of my project"; the box in the middle is sub-labelled "tree" and includes three lines, each labelled "blob", with the SHA1 of the 3 remaining boxes and what looks like file names: "README", "LICENSE" and "test.rb"; the last 3 boxes, aligned vertically on the right are all sub-labelled "blob" and contain what looks like the beginning of a README, LICENSE, and Ruby source file content; there are arrows linking boxes: the commit points to the tree, which points to the blobs.'&gt;&lt;/a&gt;&lt;/p&gt;
A commit and its tree (source: &lt;a src="https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell"&gt;&lt;cite&gt;Pro Git&lt;/cite&gt;&lt;/a&gt;)



&lt;p&gt;The &lt;em&gt;parent commit(s)&lt;/em&gt; in a &lt;em&gt;commit&lt;/em&gt; create a &lt;a href="https://en.wikipedia.org/wiki/Directed_acyclic_graph" rel="noopener noreferrer"&gt;directed acyclic graph&lt;/a&gt; that represents our history: a &lt;em&gt;directed acyclic graph&lt;/em&gt; is made of nodes (our commits) linked together with directed edges (each commit links to its parent(s) commit(s), there's a direction, hence &lt;em&gt;directed&lt;/em&gt;) and cannot have loops/cycles (a commit will never be its own ancestor, none of its ancestor commits will link to it as a parent commit).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgit-scm.com%2Fbook%2Fen%2Fv2%2Fimages%2Fcommits-and-parents.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgit-scm.com%2Fbook%2Fen%2Fv2%2Fimages%2Fcommits-and-parents.png" alt="A graph with 6 boxes arranged in 2 lines and 3 columns; each box on the first line is labelled with a 5-digit SHA1 prefix, sub-labelled &amp;quot;commit&amp;quot; and with metadata &amp;quot;tree&amp;quot; and &amp;quot;parent&amp;quot; both with a 5-digit SHA1 prefix –different each time–, &amp;quot;author&amp;quot; and &amp;quot;committer&amp;quot; both with value &amp;quot;Scott&amp;quot;, and some text representing the commit message; the box on the left has no &amp;quot;parent&amp;quot; value, the two other boxes have as &amp;quot;parent&amp;quot; the SHA1 of the box on their left; there's an arrow between those boxes, pointing to the left representing the &amp;quot;parent&amp;quot;; incidentally, the box on the left has the same SHA1 and same content as the commit box from the above figure; finally, each commit box also points to a box beneath it each labelled &amp;quot;Snapshot A&amp;quot;, &amp;quot;Snapshot B&amp;quot;, etc. and possibly representing the &amp;quot;tree&amp;quot; object linked from each commit."&gt;&lt;/a&gt;&lt;/p&gt;
Commits and their parents (source: &lt;a src="https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell"&gt;&lt;cite&gt;Pro Git&lt;/cite&gt;&lt;/a&gt;)



&lt;h3&gt;
  
  
  References, branches and tags
&lt;/h3&gt;

&lt;p&gt;Now SHA1 hashes are impractical to work with as humans, and while Git allows us to work with unique SHA1 prefixes instead of the full SHA1 hash, we'd need simpler names to refer to our commits: enter &lt;em&gt;references&lt;/em&gt;. Those are &lt;em&gt;labels&lt;/em&gt; for our commits that &lt;em&gt;we&lt;/em&gt; chose (rather than Git).&lt;/p&gt;

&lt;p&gt;There are several kinds of &lt;em&gt;references&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;branches&lt;/em&gt; are &lt;em&gt;moving&lt;/em&gt; references (note that &lt;code&gt;main&lt;/code&gt; or &lt;code&gt;master&lt;/code&gt; aren't special in any way, their name is only a convention)&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;tags&lt;/em&gt; are &lt;em&gt;immutable&lt;/em&gt; references&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HEAD&lt;/code&gt; is a special reference that points to the &lt;em&gt;current commit&lt;/em&gt;. It generally points to a branch rather than directly to a commit (we'll see why later). When a reference points to another reference, this is called a &lt;a href="https://blog.ltgt.net/confusing-git-terminology/#reference-symbolic-reference" rel="noopener noreferrer"&gt;&lt;em&gt;symbolic reference&lt;/em&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;there are other special references (&lt;code&gt;FETCH_HEAD&lt;/code&gt;, &lt;code&gt;ORIG_HEAD&lt;/code&gt;, etc.) that Git will setup for you during some operations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgit-scm.com%2Fbook%2Fen%2Fv2%2Fimages%2Fbranch-and-history.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgit-scm.com%2Fbook%2Fen%2Fv2%2Fimages%2Fbranch-and-history.png" alt='A graph with 9 boxes; 6 boxes are arranged the same as the above figure, and are labelled the same (three commits and their 3 trees); two boxes above the right-most (latest) commit, with arrows pointing towards it, are labelled "v1.0" and "master" respectively; the last box is above the "master" box, with an arrow pointing towards it, and is labelled "HEAD".'&gt;&lt;/a&gt;&lt;/p&gt;
A branch and its commit history (source: &lt;a src="https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell"&gt;&lt;cite&gt;Pro Git&lt;/cite&gt;&lt;/a&gt;)



&lt;h3&gt;
  
  
  The three states
&lt;/h3&gt;

&lt;p&gt;When you work in a Git repository, the files that you manipulate and record in the Git history are in your &lt;em&gt;working directory&lt;/em&gt;. To create commits, you'll &lt;em&gt;stage&lt;/em&gt; files in the &lt;a href="https://blog.ltgt.net/confusing-git-terminology/#index-staged-cached" rel="noopener noreferrer"&gt;&lt;em&gt;index&lt;/em&gt;&lt;/a&gt; or &lt;em&gt;staging area&lt;/em&gt;. When that's done you attach a commit message and move your &lt;em&gt;staged&lt;/em&gt; files to the &lt;em&gt;history&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;And to close the loop, the &lt;em&gt;working directory&lt;/em&gt; is initialized from a given commit from your &lt;em&gt;history&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgit-scm.com%2Fbook%2Fen%2Fv2%2Fimages%2Fareas.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgit-scm.com%2Fbook%2Fen%2Fv2%2Fimages%2Fareas.png" alt="A sequence diagram with 3 participants: &amp;quot;Working Directory&amp;quot;, &amp;quot;Staging Area&amp;quot;, and &amp;quot;.git directpry (Repository)&amp;quot;; there's a &amp;quot;Checkout the project&amp;quot; message from the &amp;quot;.git directory&amp;quot; to the &amp;quot;Working Directory&amp;quot;, then &amp;quot;Stage Fixes&amp;quot; from the &amp;quot;Working Directory&amp;quot; to the &amp;quot;Staging Area&amp;quot;, and finally &amp;quot;Commit&amp;quot; from the &amp;quot;Staging Area&amp;quot; to the &amp;quot;.git directory&amp;quot;."&gt;&lt;/a&gt;&lt;/p&gt;
Working tree, staging area, and Git directory (source: &lt;a href="https://git-scm.com/book/en/v2/Getting-Started-What-is-Git%3F#_the_three_states" rel="noopener noreferrer"&gt;&lt;cite&gt;Pro Git&lt;/cite&gt;&lt;/a&gt;)



&lt;h3&gt;
  
  
  Aside: ignoring files
&lt;/h3&gt;

&lt;p&gt;Not all files need to have their history &lt;em&gt;tracked&lt;/em&gt;: those generated by your build system (if any), those specific to your editor, and those specific to your operating system or other work environment.&lt;/p&gt;

&lt;p&gt;Git allows defining naming patterns of files or directories to ignore. This does not actually mean that Git will ignore them and they cannot be &lt;em&gt;tracked&lt;/em&gt;, but that if they're not tracked, several Git operations won't show them to you or manipulate them (but you can manually add them to your history, and from then on they'll no longer be &lt;em&gt;ignored&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;Ignoring files is done by putting their pathname (possibly using globs) in ignore files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;.gitignore&lt;/code&gt; files anywhere in your repository define ignore patterns for the containing directory; those ignore files are tracked in history as a mean to share them between developers; this is where you'll ignore those files generated by your build system (&lt;code&gt;build/&lt;/code&gt; for Gradle projects, &lt;code&gt;_site/&lt;/code&gt; for an Eleventy website, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.git/info/excludes&lt;/code&gt; is local to the repository on your machine; rarely used but sometimes useful so good to know about&lt;/li&gt;
&lt;li&gt;and finally &lt;code&gt;~/.config/git/ignore&lt;/code&gt; is global to the machine (for your user); this is where you'll ignore files that are specific to your machine, such as those specific to the editors you use, or those specific to your operating system (e.g. the &lt;code&gt;.DS_Store&lt;/code&gt; on macOS, or &lt;code&gt;Thumbs.db&lt;/code&gt; on Windows)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Summing up
&lt;/h3&gt;

&lt;p&gt;Here's another representation of all those concepts:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmarklodato.github.io%2Fvisual-git-guide%2Fconventions.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmarklodato.github.io%2Fvisual-git-guide%2Fconventions.svg" alt="A graph with 10 boxes; 5 boxes are arranged as a line in the center, labelled with 5-digit SHA1 prefixes and with arrows between them pointing from right to left; a note describes them as &amp;quot;commit objects, identified by SHA-1 hash&amp;quot;, another note describes one of the arrows as &amp;quot;child points to a parent&amp;quot;; a pair of boxes (looking like a single box split horizontally in two boxes) is above the right-most (latest) commit, with an arrow pointing down towards it, the upper box of the pair is labelled &amp;quot;HEAD&amp;quot; and described as &amp;quot;reference to the current branch&amp;quot;; the  lower box is labelled &amp;quot;main&amp;quot; and described as &amp;quot;current branch&amp;quot;; a seventh box is above another commit, with an arrow pointing down towards it; it's labelled &amp;quot;stable&amp;quot; and described as &amp;quot;another branch&amp;quot;; the last two boxes are under the commit history, one above the other; the bottom-most box is labelled &amp;quot;Working Directory&amp;quot; and described as &amp;quot;files that you 'see'&amp;quot;, the other box, between it and the commit history, is labelled &amp;quot;Stage (Index)&amp;quot; and described as &amp;quot;files to go in the next commit&amp;quot;."&gt;&lt;/a&gt;&lt;/p&gt;
Commits, references, and areas (source: &lt;a href="https://marklodato.github.io/visual-git-guide/index-en.html#conventions" rel="noopener noreferrer"&gt;&lt;cite&gt;A Visual Git Reference&lt;/cite&gt;&lt;/a&gt;, Mark Lodato)



&lt;h2&gt;
  
  
  Basic operations
&lt;/h2&gt;

&lt;p&gt;This is where we start talking about Git commands, and how they interact with the graph:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git init&lt;/code&gt; to initialize a new repository&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git status&lt;/code&gt; to get a summary of your files' state&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git diff&lt;/code&gt; to show changes between any two of your working directory, the index, the &lt;code&gt;HEAD&lt;/code&gt;, or actually between any commit&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git log&lt;/code&gt; to show and search into your history&lt;/li&gt;
&lt;li&gt;creating commits

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git add&lt;/code&gt; to add files to the &lt;em&gt;index&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git commit&lt;/code&gt; to transform the &lt;em&gt;index&lt;/em&gt; into a &lt;em&gt;commit&lt;/em&gt; (with an added &lt;em&gt;commit message&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git add -p&lt;/code&gt; to add files interactively to the &lt;em&gt;index&lt;/em&gt;: pick which changes to add and which ones to leave only in your working directory, on a file-by-file, part-by-part (called &lt;em&gt;hunk&lt;/em&gt;) basis&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;managing branches

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git branch&lt;/code&gt; to show branches, or create a branch&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git switch&lt;/code&gt; (also &lt;code&gt;git checkout&lt;/code&gt;) to check out a branch (or any commit, any &lt;em&gt;tree&lt;/em&gt;, actually) to your working directory&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git switch -b&lt;/code&gt; (also &lt;code&gt;git checkout -b&lt;/code&gt;) as a shortcut for &lt;code&gt;git branch&lt;/code&gt; and &lt;code&gt;git switch&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;git grep&lt;/code&gt; to search into your working directory, index, or any commit; this is kind of an enhanced &lt;code&gt;grep -R&lt;/code&gt; that's aware of Git&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;git blame&lt;/code&gt; to know the last commit that changed each line of a given file (so, who to blame for a bug)&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;git stash&lt;/code&gt; to put uncommitted changes aside (this includes &lt;em&gt;staged&lt;/em&gt; files, as well as &lt;em&gt;tracked&lt;/em&gt; files from the working directory), and later &lt;em&gt;unstash&lt;/em&gt; them.&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Commit, branch switching, and HEAD
&lt;/h3&gt;

&lt;p&gt;When you create a commit (with &lt;code&gt;git commit&lt;/code&gt;), Git not only creates the &lt;em&gt;commit object&lt;/em&gt;, it also moves the &lt;code&gt;HEAD&lt;/code&gt; to point to it. If the &lt;code&gt;HEAD&lt;/code&gt; actually points to a branch, as is generally the case, Git will move that branch to the new commit (and &lt;code&gt;HEAD&lt;/code&gt; will continue to point to the branch). Whenever the current branch is an ancestor of another branch (the commit pointed by the branch is also part of another branch), committing will move &lt;code&gt;HEAD&lt;/code&gt; the same, and branches will &lt;em&gt;diverge&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;When you switch to another branch (with &lt;code&gt;git switch&lt;/code&gt; or &lt;code&gt;git checkout&lt;/code&gt;), &lt;code&gt;HEAD&lt;/code&gt; moves to the new current branch, and your working directory and index are setup to ressemble the state of that commit (uncommitted changes are tentatively kept; if Git is unable to do it, it will refuse the switch).&lt;/p&gt;

&lt;p&gt;For more details, and visual representations, see the &lt;a href="https://marklodato.github.io/visual-git-guide/index-en.html#commit" rel="noopener noreferrer"&gt;commit&lt;/a&gt; and &lt;a href="https://marklodato.github.io/visual-git-guide/index-en.html#checkout" rel="noopener noreferrer"&gt;checkout&lt;/a&gt; sections of Mark Lotato's &lt;cite&gt;A Visual Git Reference&lt;/cite&gt; (be aware that this reference was written years ago, when &lt;code&gt;git switch&lt;/code&gt; and &lt;code&gt;git restore&lt;/code&gt; didn't exist and &lt;code&gt;git checkout&lt;/code&gt; was all we had; so the &lt;em&gt;checkout&lt;/em&gt; section covers a bit more than &lt;code&gt;git switch&lt;/code&gt; as a result).&lt;br&gt;
Of course, the &lt;cite&gt;Pro Git&lt;/cite&gt; book is also a good reference with visual representations; &lt;a href="https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell" rel="noopener noreferrer"&gt;the &lt;cite&gt;Branches in a Nutshell&lt;/cite&gt; subchapter&lt;/a&gt; covers a big part of all of the above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Aside: Git is conservative
&lt;/h3&gt;

&lt;p&gt;As we've seen above, due to its &lt;em&gt;content-addressed storage&lt;/em&gt;, any “change” to a commit (with &lt;code&gt;git commit --amend&lt;/code&gt; for instance) will actually result in a different commit (different SHA1). The &lt;em&gt;old commit&lt;/em&gt; won't disappear immediately: Git uses &lt;em&gt;garbage collection&lt;/em&gt; to eventually delete commits that aren't reachable from any &lt;em&gt;reference&lt;/em&gt;. This means that many mistakes can be recovered if you manage to find the commit SHA1 back (&lt;code&gt;git reflog&lt;/code&gt; can help here, or the notation &lt;code&gt;&amp;lt;branch-name&amp;gt;@{&amp;lt;n&amp;gt;}&lt;/code&gt;, e.g. &lt;code&gt;main@{1}&lt;/code&gt; for the last commit that &lt;code&gt;main&lt;/code&gt; pointed to before it changed).&lt;/p&gt;

&lt;h3&gt;
  
  
  Working with branches
&lt;/h3&gt;

&lt;p&gt;We've seen above how branches can diverge.&lt;br&gt;
But diverging calls for eventually &lt;em&gt;merging&lt;/em&gt; changes back (with &lt;code&gt;git merge&lt;/code&gt;). Git is very good at that (as we'll see later).&lt;/p&gt;

&lt;p&gt;A special case of merging is when the current branch is an ancestor of the branch to merge into. In this case, Git can do a &lt;a href="https://blog.ltgt.net/confusing-git-terminology/#can-be-fast-forwarded" rel="noopener noreferrer"&gt;&lt;em&gt;fast-forward merge&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Because operations between two branches will likely always target the same pair of branches, Git allows you to setup a branch to &lt;em&gt;track&lt;/em&gt; another branch. That other branch with be called the &lt;em&gt;upstream&lt;/em&gt; of the branch that &lt;em&gt;tracks&lt;/em&gt; it. When setup, &lt;code&gt;git status&lt;/code&gt; will, for example, tell you how much the two branches have diverged from one another: is the current branch &lt;a href="https://blog.ltgt.net/confusing-git-terminology/#your-branch-is-up-to-date-with-originmain" rel="noopener noreferrer"&gt;&lt;em&gt;up to date&lt;/em&gt;&lt;/a&gt; with its upstream branch, &lt;em&gt;behind it&lt;/em&gt; and &lt;a href="https://blog.ltgt.net/confusing-git-terminology/#can-be-fast-forwarded" rel="noopener noreferrer"&gt;can be fast-forwarded&lt;/a&gt;, &lt;em&gt;ahead&lt;/em&gt; by a number of commits, or have they diverged, each by some number of commits. Other commands will use that information to provide good default values for parameters so they can be omitted.&lt;/p&gt;

&lt;p&gt;To integrate changes from another branch, rather than merging, another option is to &lt;em&gt;cherry-pick&lt;/em&gt; (with the same-named command) a single commit, without its history: Git will compute the changes brought in by that commit and apply the same changes to the current branch, creating a new commit similar to the original one (if you to know more about how Git actually does it, see Julia Evans' &lt;a href="https://jvns.ca/blog/2023/11/10/how-cherry-pick-and-revert-work/" rel="noopener noreferrer"&gt;&lt;cite&gt;How git cherry-pick and revert use 3-way merge&lt;/cite&gt;&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Finally, another command in your toolbelt is &lt;code&gt;rebase&lt;/code&gt;.&lt;br&gt;
You can see it as a way to do many cherry-picks at once but it's actually much more powerful (as we'll see below). In its basic use though, it's just that: you give it a range of commits (between any commit as the starting point and an existing branch as the end point, defaulting to the current one) and a target, and it cherry-picks all those commits on top of the target and finally updates the branch used as the end point. The command here is of the form &lt;code&gt;git rebase --onto=&amp;lt;target&amp;gt; &amp;lt;start&amp;gt; &amp;lt;end&amp;gt;&lt;/code&gt;. As with many Git commands, arguments can be omitted and will have default values and/or specific meanings: thus, &lt;code&gt;git rebase&lt;/code&gt; is a shorthand for &lt;code&gt;git rebase --fork-point upstream&lt;/code&gt; where &lt;code&gt;upstream&lt;/code&gt; is the &lt;a href="https://blog.ltgt.net/confusing-git-terminology/#untracked-files-remote-tracking-branch-track-remote-branch" rel="noopener noreferrer"&gt;upstream&lt;/a&gt; of the current branch (I'll ignore &lt;code&gt;--fork-point&lt;/code&gt; here, its effect is subtle and not that important in every-day use), which itself is a shorthand for &lt;code&gt;git rebase upstream HEAD&lt;/code&gt; (where &lt;code&gt;HEAD&lt;/code&gt; must point to a branch), itself a shorthand for &lt;code&gt;git rebase --onto=upstream upstream HEAD&lt;/code&gt;, a shorthand for &lt;code&gt;git rebase --onto=upstream $(git merge-base upstream HEAD) HEAD&lt;/code&gt;, and will rebase all commits between the last common ancestor of &lt;code&gt;upstream&lt;/code&gt; and the current branch on one hand and the current branch (i.e. all commits since they diverged) on the other hand, and will reapply them on top of &lt;code&gt;upstream&lt;/code&gt;, then update the current branch to point to the new commits. Explicit use of &lt;code&gt;--onto&lt;/code&gt; (with a value different from the starting point) is rare actually, see &lt;a href="https://blog.ltgt.net/confusing-git-terminology/#git-rebase---onto" rel="noopener noreferrer"&gt;my previous post&lt;/a&gt; for one use case.&lt;/p&gt;

&lt;p&gt;We cannot present &lt;code&gt;git rebase&lt;/code&gt; without its interactive variant &lt;code&gt;git rebase -i&lt;/code&gt;: it starts with exactly the same behavior as the non-interactive variant, but after computing what needs to be done, it'll allow you to edit it (as a text file in an editor, one action per line). By default, all selected commits are cherry-picked, but you'll be able to reorder them, to skip some commit(s), or even combine some into a single commit. You can actually cherry-pick a commit that was not initially selected, and even create merge commits, thus entirely rewriting the whole history! Finally, you can also stop on a commit to &lt;em&gt;edit&lt;/em&gt; it (using &lt;code&gt;git commit --amend&lt;/code&gt; then, and/or possibly create new commits before continuing with the rebase), and/or run a given command between two commits. This last option is so useful (to e.g. validate that you didn't break your project at each point of the history) that you can pass that command in an &lt;code&gt;--exec&lt;/code&gt; option and Git will execute it between each rebased commit (this works with non-interactive rebase too; in interactive mode you'll see execution lines inserted between each cherry-pick line when given the ability to edit the rebase scenario).&lt;/p&gt;

&lt;p&gt;For more details, and visual representations, see the &lt;a href="https://marklodato.github.io/visual-git-guide/index-en.html#merge" rel="noopener noreferrer"&gt;merge&lt;/a&gt;, &lt;a href="https://marklodato.github.io/visual-git-guide/index-en.html#cherry-pick" rel="noopener noreferrer"&gt;cherry pick&lt;/a&gt;, and &lt;a href="https://marklodato.github.io/visual-git-guide/index-en.html#rebase" rel="noopener noreferrer"&gt;rebase&lt;/a&gt; sections of Mark Lodato's &lt;cite&gt;A Visual Git Reference&lt;/cite&gt;, and the &lt;a href="https://git-scm.com/book/en/v2/Git-Branching-Basic-Branching-and-Merging" rel="noopener noreferrer"&gt;&lt;cite&gt;Basic Branching and Merging&lt;/cite&gt;&lt;/a&gt;, &lt;a href="https://git-scm.com/book/en/v2/Git-Branching-Rebasing" rel="noopener noreferrer"&gt;&lt;cite&gt;Rebasing&lt;/cite&gt;&lt;/a&gt;, and &lt;a href="https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History" rel="noopener noreferrer"&gt;&lt;cite&gt;Rewriting History&lt;/cite&gt;&lt;/a&gt; subchapters of the &lt;cite&gt;Pro Git&lt;/cite&gt; book.&lt;br&gt;
You can also look at the “branching and merging” diagrams from David Drysdale's &lt;a href="https://lurklurk.org/gitpix/gitpix.html" rel="noopener noreferrer"&gt;&lt;cite&gt;Git Visual Reference&lt;/cite&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working with others
&lt;/h2&gt;

&lt;p&gt;For now, we've only ever worked locally in our repository.&lt;br&gt;
But Git was specifically built to work with others.&lt;/p&gt;

&lt;p&gt;Let me introduce &lt;em&gt;remotes&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Remotes
&lt;/h3&gt;

&lt;p&gt;When you &lt;em&gt;clone&lt;/em&gt; a repository, that repository becomes a &lt;em&gt;remote&lt;/em&gt; of your local repository, named &lt;code&gt;origin&lt;/code&gt; (just like with the &lt;code&gt;main&lt;/code&gt; branch, this is just the default value and the name in itself has nothing special, besides sometimes being used as the default value when an command argument is omitted). You'll then start working, creating local commits and branches (therefore &lt;em&gt;forking&lt;/em&gt; from the remote), and the remote will probably get some more commits and branches from its author in the mean time. You'll thus want to synchronize those remote changes into your local repository, and want to quickly know what changes you made locally compared to the remote. The way Git handles this is by recording the state of the remote it knows about (the branches, mainly) in a special namespace: &lt;code&gt;refs/remote/&lt;/code&gt;. Those are known as &lt;a href="https://blog.ltgt.net/confusing-git-terminology/#untracked-files-remote-tracking-branch-track-remote-branch" rel="noopener noreferrer"&gt;&lt;em&gt;remote-tracking branches&lt;/em&gt;&lt;/a&gt;. Fwiw, local branches are stored in the &lt;code&gt;refs/heads/&lt;/code&gt; namespace, and tags in &lt;code&gt;refs/tags/&lt;/code&gt; (tags from remotes are generally &lt;em&gt;imported&lt;/em&gt; right into &lt;code&gt;refs/tags/&lt;/code&gt;, so for instance you lose the information of where they came from). You can have as many remotes as needed, each with a name. (Note that remotes don't necessarily live on other machines, they can actually be on the same machine, accessed directly from the filesystem, so you can play with remotes without having to setup anything.)&lt;/p&gt;

&lt;h3&gt;
  
  
  Fetching
&lt;/h3&gt;

&lt;p&gt;Whenever you &lt;em&gt;fetch&lt;/em&gt; from a remote (using &lt;code&gt;git fetch&lt;/code&gt;, &lt;code&gt;git pull&lt;/code&gt;, or &lt;code&gt;git remote update&lt;/code&gt;), Git will talk to it to download the commits it doesn't yet know about, and will update the &lt;em&gt;remote-tracking branches&lt;/em&gt; for the remote. The exact set of references to be fetched, and where they're fetched, is passed to the &lt;code&gt;git fetch&lt;/code&gt; command (as &lt;a href="https://blog.ltgt.net/confusing-git-terminology/#refspecs" rel="noopener noreferrer"&gt;refspecs&lt;/a&gt;) and the default value defined in your repository's &lt;code&gt;.git/config&lt;/code&gt;, and configured by default by &lt;code&gt;git clone&lt;/code&gt; or &lt;code&gt;git remote add&lt;/code&gt; to taking all branches (everything in &lt;code&gt;refs/heads/&lt;/code&gt; on the remote) and putting them in &lt;code&gt;refs/remote/&amp;lt;remote&amp;gt;&lt;/code&gt; (so &lt;code&gt;refs/remote/origin/&lt;/code&gt; for the &lt;code&gt;origin&lt;/code&gt; remote), with the same name (so &lt;code&gt;refs/heads/main&lt;/code&gt; on the remote becomes &lt;code&gt;refs/remote/origin/main&lt;/code&gt; locally).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgit-scm.com%2Fbook%2Fen%2Fv2%2Fimages%2Fremote-branches-5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgit-scm.com%2Fbook%2Fen%2Fv2%2Fimages%2Fremote-branches-5.png" alt='A diagram with 3 big boxes, representing machines or repositories, containing smaller boxes and arrows representing commit histories; one box is labelled "git.outcompany.com", sublabelled "origin", and includes commits in a branch named "master"; another box is labelled "git.team1.outcompany.com", sublabelled "teamone", and includes commits in a branch named "master"; the commit SHA1 hashes are the same in "origin" and "teamone" except "origin" has one more commit on its "master" branch, i.e. "teamone" is "behind"; the third box is labelled "My Computer", it includes the same commits as the other two boxes, but this time the branches are named "origin/master" and "teamone/master"; it also includes two more commits in a branch named "master", diverging from an earlier point of the remote branches.'&gt;&lt;/a&gt;&lt;/p&gt;
Remotes and remote-tracking branches (source: &lt;a href="https://git-scm.com/book/en/v2/Git-Branching-Remote-Branches" rel="noopener noreferrer"&gt;&lt;cite&gt;Pro Git&lt;/cite&gt;&lt;/a&gt;)



&lt;p&gt;You'll then use branch-related commands to get changes from a &lt;em&gt;remote-tracking branch&lt;/em&gt; to your local branch (&lt;code&gt;git merge&lt;/code&gt; or &lt;code&gt;git rebase&lt;/code&gt;), or &lt;code&gt;git pull&lt;/code&gt; which is hardly more than a shorthand for &lt;code&gt;git fetch&lt;/code&gt; followed by a &lt;code&gt;git merge&lt;/code&gt; or &lt;code&gt;git rebase&lt;/code&gt;. BTW, in a number of situations, Git will automatically setup a &lt;em&gt;remote-tracking branch&lt;/em&gt; to be the &lt;em&gt;upstream&lt;/em&gt; of a local branch when you create it (it will tell you about it when that happens).&lt;/p&gt;

&lt;h3&gt;
  
  
  Pushing
&lt;/h3&gt;

&lt;p&gt;To share your changes with others, they can either add your repository as a remote and &lt;em&gt;pull&lt;/em&gt; from it (implying accessing your machine across the network), or you can &lt;em&gt;push&lt;/em&gt; to a remote. (If you ask someone to pull changes from your remote, this is called a… &lt;em&gt;pull request&lt;/em&gt;, a term you'll have probably heard of from GitHub or similar services.)&lt;/p&gt;

&lt;p&gt;Pushing is similar to fetching, in reverse: you'll send your commits to the remote and update its branch to point to the new commits. As a safety measure, Git only allows remote branches to be &lt;em&gt;fast-forwarded&lt;/em&gt;; if you want to push changes that would update the remote branch in a non-fast-forward way, you'll have to &lt;em&gt;force&lt;/em&gt; it, using &lt;code&gt;git push --force-with-lease&lt;/code&gt; (or &lt;code&gt;git push --force&lt;/code&gt;, but be careful: &lt;code&gt;--force-with-lease&lt;/code&gt; will first ensure your &lt;em&gt;remote-tracking branch&lt;/em&gt; is up-to-date with the remote's branch, to make sure nobody pushed changes to the branch since the last time you &lt;em&gt;fetched&lt;/em&gt;; &lt;code&gt;--force&lt;/code&gt; won't do that check, doing what you're telling it to do, at your own risks).&lt;/p&gt;

&lt;p&gt;As with &lt;code&gt;git fetch&lt;/code&gt;, you pass the branches to update to the &lt;code&gt;git push&lt;/code&gt; command, but Git provides a good default behavior if you don't. If you don't specify anything, Git will infer the remote from the &lt;em&gt;upstream&lt;/em&gt; of the current branch, so most of the time &lt;code&gt;git push&lt;/code&gt; is equivalent to &lt;code&gt;git push origin&lt;/code&gt;. This actually is a shorthand to &lt;code&gt;git push origin main&lt;/code&gt; (assuming the current branch is &lt;code&gt;main&lt;/code&gt;), itself a shorthand for &lt;code&gt;git push origin main:main&lt;/code&gt;, shorthand for &lt;code&gt;git push origin refs/heads/main:refs/heads/main&lt;/code&gt;, meaning to push the local &lt;code&gt;refs/heads/main&lt;/code&gt; to the &lt;code&gt;origin&lt;/code&gt; remote's &lt;code&gt;refs/heads/main&lt;/code&gt;. See &lt;a href="https://blog.ltgt.net/confusing-git-terminology/#refspecs" rel="noopener noreferrer"&gt;my previous post&lt;/a&gt; for some use cases of specifying &lt;em&gt;refspecs&lt;/em&gt; with differing source and destination.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flurklurk.org%2Fgitpix%2Fpush2.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flurklurk.org%2Fgitpix%2Fpush2.svg" alt='A diagram representing a "git push" command, with four git graph diagrams (dots, some labelled, connected by lines) arranged in two lines and two columns; an arrow in between the columns implies that the left column is a "before" state and the right column an "after" state; graphs on the above line are inside a cloud, representing a remote repository, and have two branches, "master" and "other", that diverged from a common ancestor; the bottom left diagram has the same shape as the one above it except the labels are changed to "origin/master" and "origin/other" and each branch has more commits: the "master" branch has two additional commits compared to "origin/master", and "other" has one more commit thatn "origin/other"; the top right diagram has two more commits in its "master" branch compared to the top left diagram; the bottom right diagram is identical to the bottom left one except "origin/master" now points to the same commit as "master"; in other words, in the "before" state, the remote lacked three commits, and after the "git push" the two commits from the local "master" branch were copied to the remote while "other" was left untouched.'&gt;&lt;/a&gt;&lt;/p&gt;
&lt;code&gt;git push&lt;/code&gt; (source: &lt;a href="https://lurklurk.org/gitpix/gitpix.html" rel="noopener noreferrer"&gt;&lt;cite&gt;Git Visual Reference&lt;/cite&gt;&lt;/a&gt;, David Drysdale)



&lt;p&gt;For more details, and visual representations, see the &lt;a href="https://git-scm.com/book/en/v2/Git-Branching-Remote-Branches" rel="noopener noreferrer"&gt;&lt;cite&gt;Remote Branches&lt;/cite&gt;&lt;/a&gt;, &lt;a href="https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes" rel="noopener noreferrer"&gt;&lt;cite&gt;Working with Remotes&lt;/cite&gt;&lt;/a&gt;, and &lt;a href="https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project" rel="noopener noreferrer"&gt;&lt;cite&gt;Contributing to a Project&lt;/cite&gt;&lt;/a&gt; subchapters of the &lt;cite&gt;Pro Git&lt;/cite&gt; book, and the “dealing with remote repositories” diagrams from David Drysdale's &lt;a href="https://lurklurk.org/gitpix/gitpix.html" rel="noopener noreferrer"&gt;&lt;cite&gt;Git Visual Reference&lt;/cite&gt;&lt;/a&gt;.&lt;br&gt;
The &lt;cite&gt;Contributing to a Project&lt;/cite&gt; chapter of &lt;cite&gt;Pro Git&lt;/cite&gt; also touches about contributing to open source projects on platforms like GitHub, where you have to first &lt;em&gt;fork&lt;/em&gt; the repository, and contribute through &lt;em&gt;pull requests&lt;/em&gt; (or &lt;em&gt;merge requests&lt;/em&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Best practices
&lt;/h2&gt;

&lt;p&gt;Those are directed towards beginners, and hopefully not too controversial.&lt;/p&gt;

&lt;p&gt;Try to keep a &lt;em&gt;clean&lt;/em&gt; history:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;use merge commits wisely&lt;/li&gt;
&lt;li&gt;clear and high-quality commit messages (see the &lt;a href="https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project#_commit_guidelines" rel="noopener noreferrer"&gt;&lt;cite&gt;commit guidelines&lt;/cite&gt;&lt;/a&gt; in &lt;cite&gt;Pro Git&lt;/cite&gt;)&lt;/li&gt;
&lt;li&gt;make &lt;em&gt;atomic&lt;/em&gt; commits: each commit should be compile and run independently of the commits following it in the history&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This only applies to the history you share with others.&lt;br&gt;
Locally, do however you want. For beginners, I'd give the following advices though:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;don't work directly on &lt;code&gt;main&lt;/code&gt; (or &lt;code&gt;master&lt;/code&gt;, or any branch that you don't specifically &lt;em&gt;own&lt;/em&gt; on the remote as well), create local branches instead; it helps decoupling work on different tasks: about to start working on another bug or feature while waiting for additional details on instructions on the current one? switch to another branch, you'll get back to that later by switching back; it also makes it easier to update from the remote as you're sure you won't have conflicts if your local branches are simply copies of the remote ones of the same name, without any local change (except when you want to push those changes to that branch)&lt;/li&gt;
&lt;li&gt;don't hesitate to rewrite your commit history (&lt;code&gt;git commit --amend&lt;/code&gt; and/or &lt;code&gt;git rebase -i&lt;/code&gt;), but don't do it too early; its more than OK to stack many small commits while working, and only rewrite/cleanup the history before you share it&lt;/li&gt;
&lt;li&gt;similarly, don't hesitate to rebase your local branches to integrate upstream changes (until you shared that branch, at which point you'll follow the project's how branching workflow)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In case of any problem and you're lost, my advice is to use &lt;code&gt;gitk&lt;/code&gt; or &lt;code&gt;gitk HEAD @{1}&lt;/code&gt;, also possibly &lt;code&gt;gitk --all&lt;/code&gt; (I'm using &lt;code&gt;gitk&lt;/code&gt; here but use whichever tool you prefer), to visualize your Git history and try to understand what happened. From this, you can rollback to the previous state (&lt;code&gt;git reset @{1}&lt;/code&gt;) or try to fix things (cherry-picking a commit, etc.) And if you're in the middle of a rebase, or possibly a failed merge, you can abort and rollback to the previous state with commands like &lt;code&gt;git rebase --abort&lt;/code&gt; or &lt;code&gt;git merge --abort&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To make things even easier, don't hesitate, before any possibly destructive command (&lt;code&gt;git rebase&lt;/code&gt;), to create a branch or a tag as a "bookmark" you can easily reset to if things don't go as expected. And of course, inspect the history and files after such a command to make sure the outcome is the one you expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced concepts
&lt;/h2&gt;

&lt;p&gt;Only a few of them, there are many more to explore!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Detached &lt;code&gt;HEAD&lt;/code&gt;: the &lt;a href="https://git-scm.com/docs/git-checkout#_detached_head" rel="noopener noreferrer"&gt;&lt;code&gt;git checkout&lt;/code&gt; manpage&lt;/a&gt; has a good section on the topic, also see &lt;a href="https://blog.ltgt.net/confusing-git-terminology/#detached-head-state" rel="noopener noreferrer"&gt;my previous post&lt;/a&gt;, and for a good visual representation, see the &lt;a href="https://marklodato.github.io/visual-git-guide/index-en.html#detached" rel="noopener noreferrer"&gt;&lt;cite&gt;Committing with a Detached HEAD&lt;/cite&gt;&lt;/a&gt; section of Mark Lodato's &lt;cite&gt;A Visual Git Reference&lt;/cite&gt;.&lt;/li&gt;
&lt;li&gt;Hooks: those are executables (shell scripts most of the time) that Git will run in reaction to operations on a repository; people use them to lint the code before each commit (aborting the commit if that fails), generate or post-process commit messages, or trigger actions on the server after someone pushes to the repository (trigger builds and/or deployments).&lt;/li&gt;
&lt;li&gt;A couple rarely needed commands that can save you hours when you actually need them:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git bisect&lt;/code&gt;: an advanced command to help you pinpoint which commit introduced a bug, by testing several commits (manually or through scripting); with a linear history, this is using bisection and could be done manually, but as soon as you have many merge commits this becomes much more complex and it's good to have &lt;code&gt;git bisect&lt;/code&gt; do the heavy lifting.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git filter-repo&lt;/code&gt;: a &lt;a href="https://github.com/newren/git-filter-repo" rel="noopener noreferrer"&gt;third-party command&lt;/a&gt; actually, as a replacement to Git's own &lt;code&gt;filter-branch&lt;/code&gt;, that allows rewriting the whole history of a repository to remove a mistakenly added file, or help extract part of the repository to another.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;We're done.&lt;/p&gt;

&lt;p&gt;With this knowledge, one should be able to map any Git command to how it will modify the &lt;em&gt;directed acyclic graph&lt;/em&gt; of commits, and understand how to fix mistakes (ran a merge on the wrong branch? rebased on the wrong branch?) I'm not saying understanding such things will be &lt;em&gt;easy&lt;/em&gt;, but should at least be possible.&lt;/p&gt;

</description>
      <category>git</category>
      <category>learning</category>
    </item>
    <item>
      <title>Climate-friendly software: don't fight the wrong battle</title>
      <dc:creator>Thomas Broyer</dc:creator>
      <pubDate>Mon, 01 May 2023 00:05:08 +0000</pubDate>
      <link>https://dev.to/tbroyer/climate-friendly-software-dont-fight-the-wrong-battle-19h2</link>
      <guid>https://dev.to/tbroyer/climate-friendly-software-dont-fight-the-wrong-battle-19h2</guid>
      <description>&lt;p&gt;When talking about software ecodesign, green IT, climate-friendly software, the carbon footprint of software, or however you name it, most of the time people focus on energy efficiency and server-side code, sometimes going to great length measuring and monitoring it. But what if all this was misguided?&lt;/p&gt;

&lt;p&gt;Ok, this is a bit of a bold statement, but don't get me wrong:I'm not saying you shouldn't care about this. Let's look at one of the most recent examples I've seen: GitHub's &lt;a href="https://gh.io/AAjpnus"&gt;ReadME Project Q&amp;amp;A: Slash your code's carbon footprint&lt;/a&gt; newsletter issue. It's good and I agree with many things in there (go read it if you haven't already), but it talks almost exclusively about energy efficiency and server-side code, or in other words it limits actions to the scope 2 of the &lt;a href="https://ghgprotocol.org/"&gt;GHG Protocol&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So let's first understand which impacts we're talking about before I give you my opinion on the low-hanging fruits.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclaimer: people regarded as experts in green IT trusted me enough to have me contribute to &lt;a href="https://ecoconceptionweb.com/"&gt;a book on the subject&lt;/a&gt; but I'm not myself an expert in the field.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Note: this post is written for developers and software architects; there are other actions to lower the climate impact of the digital world that won't be covered here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stepping back
&lt;/h2&gt;

&lt;p&gt;Most software nowadays is client-server: whether web-based or mobile, more and more end-user software talk to servers. This means there's a huge asymmetry in usage: even for small-scale professional software the end users generally vastly outnumber the servers. And this implies the impacts of the individual clients need to be much lower than those of the servers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Vk0f66fE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/874g310cgab3mi9d1l9w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Vk0f66fE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/874g310cgab3mi9d1l9w.png" alt="Data table showing greenhouse gas emissions share broken down by tier and lifecycle stage; all values in user equipment line are red, other values in use phase column are orange; in the total column, user equipment is red, networks orange, and data centers green" width="606" height="237"&gt;&lt;/a&gt;&lt;/p&gt;


  Data Table
  &lt;div class="table-wrapper-paragraph"&gt;&lt;table id="ghg-balance"&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;th&gt;Manufacturing&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;User equipment&lt;/th&gt;
&lt;td&gt;40%&lt;/td&gt;
&lt;td&gt;26%&lt;/td&gt;
&lt;td&gt;66%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;Networks&lt;/th&gt;
&lt;td&gt;3%&lt;/td&gt;
&lt;td&gt;17%&lt;/td&gt;
&lt;td&gt;19%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;Data centers&lt;/th&gt;
&lt;td&gt;1%&lt;/td&gt;
&lt;td&gt;14%&lt;/td&gt;
&lt;td&gt;15%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;td&gt;44%&lt;/td&gt;
&lt;td&gt;56%&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

Greenhouse gas emissions balance (&lt;a href="https://www.greenit.fr/wp-content/uploads/2019/11/GREENIT_EENM_etude_EN_accessible.pdf" title="The environmental footprint of the digital world"&gt;source&lt;/a&gt;, PDF, 533 KB)



&lt;p&gt;What &lt;a href="https://en.wikipedia.org/wiki/Life-cycle_assessment"&gt;life-cycle assessments (LCA)&lt;/a&gt; for end-users' devices tell us is that manufacturing, transport and disposal summed up immensely outweighs use, ranging from 65% up to nearly 98% of the global warming potential (GWP). Of course, this depends where the device was manufactured and where it's being used, with the use location's biggest impact being related to the carbon footprint of the electric system, as the use phase is all about charging or powering our smartphones, laptops and desktops.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RxuveL9N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0rfxpou9hknkwzxdik5m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RxuveL9N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0rfxpou9hknkwzxdik5m.png" alt="Bar chart of the estimated greenhouse gas (GHG) emissions for the Google Pixel 7; production is 7 times bigger than customer use, itself much bigger than transportation or recycling" width="713" height="327"&gt;&lt;/a&gt;&lt;/p&gt;
Estimated Greenhouse Gas (GHG) emissions for a Google Pixel 7 (&lt;a href="https://www.gstatic.com/gumdrop/sustainability/pixel-7-product-environmental-report.pdf" title="Pixel 7 Product Environmental Report"&gt;source&lt;/a&gt;, PDF, 224 KB)



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vbDGNEzb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xnu3ek3db0q834bvortd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vbDGNEzb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xnu3ek3db0q834bvortd.png" alt="Piechart of the estimated carbon footprint for a Dell Precision 3520 broken down by lifecycle phase, with a secondary piechart breaking down the footprint of the manufacturing phase by component; manufacturing is more than 4.5 times bigger than use, itself much bigger than transportation or end of life; components with the biggest impacts are the display, twice as big as the solid state drive, followed by the power supply and mainboard" width="572" height="297"&gt;&lt;/a&gt;&lt;/p&gt;
Estimated carbon footprint allocation for my Dell Precision 3520, assuming 4 years of use (I've had mine for more than 5.5 years already): 304 kg CO₂e ± 68 kg CO₂e (&lt;a href="https://i.dell.com/sites/csdocuments/CorpComm_Docs/en/carbon-footprint-precision-3520.pdf" title="Dell Precision 3520 Product Carbon Footprint"&gt;source&lt;/a&gt;, PDF, 557 KB)



&lt;p&gt;I am French, working mainly for French companies with most of their users in France, so I'm ready to admit I'm biased towards a very low use phase weight compared to other regions: go explore data for your users on &lt;a href="https://app.electricitymaps.com/map"&gt;Electricity Map&lt;/a&gt; and &lt;a href="https://ourworldindata.org/grapher/carbon-intensity-electricity"&gt;Our World in Data&lt;/a&gt;. And yet, that doesn't change the fact that the use phase has a much lower carbon footprint than all three of manufacturing, transport, and disposal as a whole.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ourworldindata.org/grapher/carbon-intensity-electricity" title="Our World in Data: Carbon intensity of electricity, 2022; interactive visualization"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5iovSx7Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://ourworldindata.org/grapher/exports/carbon-intensity-electricity.svg" alt="Map of carbon intensity of electricity in 2022, per country; countries whose electricity consumption emits less than 100 g CO₂e per kWh are mainly in Central, Eastern, and Southern Africa, and in Europe" width="800" height="565"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What we can infer from this, is that keeping our devices longer will  increase the share of use in the whole life-cycle impacts. Fairphone measured that extending the lifespan of their phones from 3 to 5 years &lt;q&gt;helps reduce the yearly emissions on global warming by 31%, while a further extension to 7 years of use helps reduce the yearly impact by 44%.&lt;/q&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rLg-Fh2i--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a1e8lgkox9iwpv5z4af3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rLg-Fh2i--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a1e8lgkox9iwpv5z4af3.png" alt="Barchart of yearly emissions for the Fairphone 4, per baseline scenario" width="485" height="293"&gt;&lt;/a&gt;&lt;/p&gt;
Fairphone 4: comparative of yearly emissions per baseline scenario (&lt;a href="https://www.fairphone.com/wp-content/uploads/2022/07/Fairphone-4-Life-Cycle-Assessment-22.pdf" title="Life Cycle Assessment of the Fairphone 4"&gt;source&lt;/a&gt;, PDF, 1.1 MB)



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Extending the lifespan of a smartphone from 3 to 5 years can reduce its yearly global warming impacts by almost a third.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Things are different for servers though, where the use phase's share varies much more depending on use location: from 4% up to 85%! As noted in the ReadME Project Q&amp;amp;A linked above, big companies' datacenters are for the most part net-neutral in carbon emissions, so not only the geographic regions of your servers matter, but also the actual datacenters in those regions. This implies that whatever you do on the server side, its impact will likely be limited (remember what I was saying in the introduction?) Of course there are exceptions, and there will always be, so please look at this through the prism of your own workloads.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8U2QQk57--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p1c7ydpdlq4qal4pkp4n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8U2QQk57--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p1c7ydpdlq4qal4pkp4n.png" alt="Piechart of estimated carbon footprint allocation for a Dell PowerEdge R640, assuming 4 years of use: use is more than 4.5 times bigger than manufacturing, itself an order of magnitude bigger than transportation or end of life." width="486" height="359"&gt;&lt;/a&gt;&lt;/p&gt;
Estimated carbon footprint for a Dell PowerEdge R640 server, assuming 4 years of use: 7730 kg CO₂e (&lt;a href="https://i.dell.com/sites/csdocuments/CorpComm_Docs/en/carbon-footprint-poweredge-r640.pdf" title="Dell PowerEdge R640 Product Carbon Footprint"&gt;source&lt;/a&gt;, PDF, 514 KB)



&lt;p&gt;Keep in mind the orders of magnitude though: 70 kg CO₂e for a single Pixel 7 (on 3 years) vs. 7730 kg CO₂e for a Dell PowerEdge R640 server (on 4 years), that's 110 smartphones for a server (or a 83:1 ratio when considering yearly emissions): chances are that you'll have much more users than that. The ratio for laptops (304 kg CO₂e on 4 years for a Dell Precision 3520) would be 25 laptops for a server. But as seen previously the actual carbon footprint will vary a lot depending on the location; you can explore some data in the &lt;a href="https://dataviz.boavizta.org/manufacturerdata"&gt;Boavizta data visualization tool&lt;/a&gt; that compiles dozens of LCAs of various manufacturers. The Dell PowerEdge R640 in France would actually emit 1701 kg CO₂e rather than 7730 kg CO₂e: that's a 4.5:1 ratio! Comparatively, my Dell Precision 3520 would fall from 304 kg CO₂e to 261 kg CO₂e, only a 1.16:1 ratio. The laptop to server ratio would thus fall from 25 down to 7.9:1, which makes the laptops' impacts comparatively much bigger than the server compared to other regions.&lt;/p&gt;

&lt;p&gt;Note that there are three tiers: end-users, datacenters, and networks. Network energy consumption however &lt;a href="https://doi.org/10.1016/j.joule.2021.05.007"&gt;doesn't vary proportionally to the amount of data transferred&lt;/a&gt;, which means we as users of those networks don't have much levers on their footprint. That being said, data transmission is &lt;a href="https://developer.android.com/training/connectivity/minimize-effect-regular-updates#:~:text=Requests%20that%20your%20app%20makes%20to%20the%20network%20are%20a%20major%20cause%20of%20battery%20drain%20because%20they%20turn%20on%20power%2Dconsuming%20cellular%20or%20Wi%2DFi%20radios."&gt;among the things that will drain the batteries of mobile devices&lt;/a&gt;, so reducing the amount of data you exchange on the network could have a more direct impact on the battery life of end-users' smartphones (even though what will drain the battery the most will more likely be the screen).&lt;/p&gt;

&lt;h2&gt;
  
  
  Taking action
&lt;/h2&gt;

&lt;p&gt;So, what have we learned so far?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It's important that end users keep their devices longer,&lt;/li&gt;
&lt;li&gt;we can't do much about networks,&lt;/li&gt;
&lt;li&gt;the location (geographic region and datacenter) of servers matter a lot, more so than how and how much we use them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, what can we do about it?&lt;/p&gt;

&lt;p&gt;For servers, it's relatively simple: if you can, rent servers in energy efficient datacenters, and/or countries with low-carbon electricity; in addition, or otherwise, then of course optimize your server-side architecture and code. If you manage your own servers, avoid buying machines to let them sit idle: maximize their utilization.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pick servers in carbon-neutral or low-carbon datacenters first, then optimize your architecture and code.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For the networks, our actions are probably limited to reducing data usage, &lt;q&gt;not because it reduces immediate emissions (it doesn't), but to avoid the need for rapid expansion of the network infrastructure&lt;/q&gt; (I'm quoting &lt;a href="https://limited.systems/"&gt;Wim Vanderbauwhede&lt;/a&gt; here, from a private conversation).&lt;/p&gt;

&lt;p&gt;For the end-users' devices, it's more complicated, but not out of reach: we want users to keep their devices as long as possible so, put differently, we must not be responsible for them to change their devices. There will always be people changing devices "for the hype" or on some scheduled basis (or just because the vendor stopped pushing security updates, &lt;a href="https://en.wikipedia.org/wiki/Planned_obsolescence#Software_degradation_and_lock-out"&gt;some form of planned obsolescence&lt;/a&gt;, or can't be repaired; two things laws could alleviate), but there are also many people who keep them as long as possible (because they're eco-conscious or can't afford purchasing a new device, or simply because they don't feel the need for changing something that's still fully functioning.) For those people, don't be the one to make them change their mind and cross the line.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Don't be the one that will make your users change their device.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is something we won't ever be able to measure, as it depends on how people perceive the overall experience on their device, but it boils down to perceived performance. So by all means, optimize your mobile apps and web frontends, test on old devices and slow networks (even if only emulated), and monitor their real-user performance (e.g. through &lt;a href="https://web.dev/vitals/"&gt;Web Vitals&lt;/a&gt;). As part of performance testing, have a look at electricity use, as it will both be directly associated with emissions to produce that electricity, and be perceptible by the user (battery drain). And don't forget to account for the app downloads as part of the overall perceived performance: light mobile apps that don't need to be updated every other day, frontend JS and CSS that can be cached and won't update several times a day either (defeating the cache).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Optimize for the perceived performance and battery life.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Don't forget about the space taken by your app on the user's device too: users shouldn't have to make a choice between apps due to no space left on device, so when possible prefer a website or progressive web app (PWA) to a native application (you can still publish them to application stores if required, through tiny wrapper native apps).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;When possible, prefer a website or PWA to a native application.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  A note to product managers
&lt;/h2&gt;

&lt;p&gt;The above advices were mostly technical, answering the question &lt;q&gt;What can I do as an architect or developer?&lt;/q&gt;&lt;br&gt;
but product managers have their share, and they're actually the ones in power here: they can choose which features to build or not build, they can shape the features, they can reduce software complexity by limiting the number of features and of levers and knobs. This will undoubtedly avoid bloat and help you make things leaner and faster.&lt;/p&gt;

&lt;p&gt;Avoid &lt;a href="https://en.wikipedia.org/wiki/Feature_creep"&gt;feature creep&lt;/a&gt; and beware of &lt;a href="https://en.wikipedia.org/wiki/Wirth%27s_law"&gt;Wirth's law&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Refrain from adding features, reduce software complexity.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Last, but not least, make sure you really need software! Sometimes you should embrace &lt;a href="https://en.wikipedia.org/wiki/Low_technology"&gt;low-tech&lt;/a&gt;. For example, instead of developing a mobile app with accounts to identify the user so you can notify them, then maybe you could simply use SMS (assuming you have some out-of-band means of knowing their phone number, and the latency of distribution is acceptable). And sometimes what you're trying to address with software just isn't worth it, particularly if it involves IoT (remember that we should strive for fewer devices that we keep longer, not more).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Sometimes, ideas aren't even worth their impacts.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Conversely, as we'll need to electrify parts of our economy to reduce their carbon footprint, &lt;q&gt;software is one of the few sectors to start with a head-starts: we get greener at the same rate as the grid without other work needed&lt;/q&gt; (I'm quoting &lt;a href="https://infrequently.org/"&gt;Alex Russell&lt;/a&gt; here, from a private conversation), so please do use software to digitalize and replace more carbon-intensive activities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other pitfalls
&lt;/h2&gt;

&lt;p&gt;Besides only evaluating electricity consumption on your servers, another pitfall is trying to attribute emissions to each user or request: when you have dozens, hundreds or even thousands of concurrent requests, how do you distribute electricity consumption among them? There's an &lt;a href="https://datatracker.ietf.org/doc/draft-martin-http-carbon-emissions-scope-2/"&gt;IETF proposal for a HTTP response header&lt;/a&gt; exposing such information, and while it's a commendable idea I doubt it's realistic. My personal belief is that display of such information is often a sign of &lt;a href="https://en.wikipedia.org/wiki/Greenwashing"&gt;greenwashing&lt;/a&gt;. To my knowledge, data can only be accurate in aggregates.&lt;/p&gt;

&lt;p&gt;If you really do want to show how &lt;em&gt;green&lt;/em&gt; you are, conduct a life-cycle assessment (LCA): take all three scopes into account, all three tiers, evaluating impacts over more criterias than the global warming potential (GWP) alone.&lt;/p&gt;

&lt;p&gt;Here are a couple resources if you want to go farther:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.greenit.fr/2023/04/18/quels-pieges-a-eviter-pour-evaluer-lempreinte-environnementale-du-numerique/"&gt;Pitfalls to avoid when assessing the environmental footprint of digital technology&lt;/a&gt; (in French)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.greensoftware.foundation/"&gt;Learn Green Software&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Thanks to &lt;a href="https://infrequently.org/"&gt;Alex Russell&lt;/a&gt; and &lt;a href="https://limited.systems/"&gt;Wim Vanderbauwhede&lt;/a&gt; for their feedback.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>environment</category>
      <category>sustainability</category>
      <category>co2</category>
      <category>carbonfootprint</category>
    </item>
    <item>
      <title>Naming things is hard, SPA edition</title>
      <dc:creator>Thomas Broyer</dc:creator>
      <pubDate>Tue, 28 Mar 2023 16:09:10 +0000</pubDate>
      <link>https://dev.to/tbroyer/naming-things-is-hard-spa-edition-3g41</link>
      <guid>https://dev.to/tbroyer/naming-things-is-hard-spa-edition-3g41</guid>
      <description>&lt;p&gt;During the past few months, social networks have been shaken by a &lt;em&gt;single-page&lt;/em&gt; vs &lt;em&gt;multi-page applications&lt;/em&gt; (SPA vs MPA) battle, more specifically related to Next.js and React, following, among other things, &lt;a href="https://mobile.twitter.com/rauchg/status/1619492334961569792"&gt;a tweet by Guillermo Rauch&lt;/a&gt; and &lt;a href="https://github.com/reactjs/reactjs.org/pull/5487#issuecomment-1409720741"&gt;a GitHub comment by Dan Abramov&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I've read a few articles and been involved in a few discussions about those and it appeared that we apparently don't all have the same definitions, so I'll give mine here and hope people rally behind them.&lt;/p&gt;

&lt;h2&gt;
  
  
  SPA vs MPA: it's about navigation &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;It's not that hard: a &lt;em&gt;single-page&lt;/em&gt; application means that you load a page (HTML) once, and then do everything in there by manipulating its DOM and browser history, fetching data as needed.&lt;br&gt;
This is the exact same thing as &lt;em&gt;client-side navigation&lt;/em&gt; and requires some form of &lt;em&gt;client-side routing&lt;/em&gt; to handle navigation (particularly from history, i.e. using the back and forward browser buttons).&lt;/p&gt;

&lt;p&gt;Conversely, a &lt;em&gt;multi-page&lt;/em&gt; application means that each navigation involves loading a new page.&lt;/p&gt;

&lt;p&gt;This by itself is a controversial topic: despite SPAs having lots of problems (user experience –aborting navigation, focus management, timing of when to update the URL bar–, &lt;a href="https://nolanlawson.com/2019/11/05/what-ive-learned-about-accessibility-in-spas/"&gt;accessibility&lt;/a&gt;, performance even by not being able to leverage streaming) due to taking responsibility and having to reimplement &lt;a href="https://dev.to/tigt/routing-im-not-smart-enough-for-a-spa-5hki"&gt;many things&lt;/a&gt; from the browser (loading feedback, error handling, focus management, scrolling), some people strongly believe this is &lt;a href="https://twitter.com/dan_abramov/status/1621949445540659201"&gt;“one of the first interesting optimizations”&lt;/a&gt; and they &lt;a href="https://twitter.com/dan_abramov/status/1617963492908335104"&gt;“can’t really seriously consider websites that reload page on every click good UX”&lt;/a&gt;&lt;br&gt;
(I've only quoted Dan Abramov from the React team here, but I don't want to single him out: he's far from being alone with this view; others are &lt;a href="https://andy-bell.co.uk/the-extremely-loud-minority/"&gt;in denial&lt;/a&gt; thinking that &lt;a href="https://www.epicweb.dev/the-webs-next-transition#:~:text=This%20is%20the%20strategy%20used%20by%20most%20of%20the%20industry%20today."&gt;“this is the strategy used by most of the industry today”&lt;/a&gt;).&lt;br&gt;
Some of those issues are supposedly (and hopefully) fixed by the new &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigation_API"&gt;navigation API&lt;/a&gt; that's currently only implemented in Chromium browsers.&lt;br&gt;
And despite &lt;a href="https://www.zachleat.com/web/single-page-applications/"&gt;their many advantages&lt;/a&gt;, MPAs aren't free from limitations too, otherwise we probably wouldn't have had SPAs to being with.&lt;/p&gt;

&lt;p&gt;My opinion? There's no one-size-fits-all: most sites and apps could (&lt;a href="https://www.thoughtworks.com/radar/techniques/spa-by-default"&gt;and probably should&lt;/a&gt;) be MPAs, and an SPA is a good (and better) fit for others.&lt;br&gt;
It's also OK to use both MPA and SPA in a single application depending on the needs.&lt;br&gt;
Jason Miller published &lt;a href="https://jasonformat.com/application-holotypes/"&gt;a rather good article&lt;/a&gt; 4 years ago (I don't agree with everything in there though).&lt;br&gt;
Nolan Lawson also has written &lt;a href="https://nolanlawson.com/2022/06/27/spas-theory-versus-practice/"&gt;a good and balanced series&lt;/a&gt; on MPAs vs SPAs.&lt;/p&gt;

&lt;p&gt;And we haven't even talked about where the &lt;em&gt;rendering&lt;/em&gt; is done yet!&lt;/p&gt;

&lt;h2&gt;
  
  
  Rendering: SSR, ESR, SWSR, and CSR &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Before diving into &lt;em&gt;where&lt;/em&gt; it's done, we first need to define &lt;em&gt;what&lt;/em&gt; rendering is.&lt;/p&gt;

&lt;p&gt;My definition of &lt;em&gt;rendering&lt;/em&gt; is applying some form of &lt;em&gt;templating&lt;/em&gt; to some &lt;em&gt;data&lt;/em&gt;.&lt;br&gt;
This means that getting some HTML fragment from the network and putting it into the page with some form of &lt;code&gt;innerHTML&lt;/code&gt; is &lt;strong&gt;not&lt;/strong&gt; rendering.&lt;br&gt;
Conversely, getting some &lt;em&gt;virtual DOM&lt;/em&gt; as JSON for example and reconstructing the equivalent DOM from it &lt;strong&gt;would&lt;/strong&gt; qualify as rendering.&lt;/p&gt;

&lt;p&gt;Now that we've defined &lt;em&gt;what&lt;/em&gt; rendering is, let's see &lt;em&gt;where&lt;/em&gt; it can be done: basically at each and any stage of delivery: the origin server (SSR), edge (ESR), service-worker (SWSR), or client (CSR).&lt;/p&gt;

&lt;p&gt;There's also a whole bunch of &lt;em&gt;prerendering&lt;/em&gt; techniques: static site generation (SSG), on-demand generation, distributed persistent rendering (DPR), etc.&lt;/p&gt;

&lt;p&gt;All these rendering stages, except client-side rendering (CSR), generate HTML to be delivered to the browser engine.&lt;br&gt;
CSR will however directly manipulate the DOM most of the time, but sometimes will also generate HTML to be used with some form of &lt;code&gt;innerHTML&lt;/code&gt;; the details here don't really matter.&lt;/p&gt;

&lt;p&gt;Rendering at the origin server or at the edge (Cloudflare Workers, Netlify Functions, etc.) can be encompassed under the name server-side rendering (SSR), but depending on the context SSR can refer to the origin server only.&lt;br&gt;
Similarly, rendering in a service worker could be included in client-side rendering (CSR), but most of the time CSR is only about rendering in a browsing context.&lt;br&gt;
I suppose we could use &lt;em&gt;browser-side rendering&lt;/em&gt; (BSR) to encompass CSR and SWSR.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CNrFEg2s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/veq2zyvn3ibx5mki348f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CNrFEg2s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/veq2zyvn3ibx5mki348f.png" alt="Schema of SSR, ESR, SWSR and CSR, with grouping representing SSR-in-the-broader-sense (SSR and ESR) vs. BSR (SWSR and CSR), and which generate HTML (SSR, ESR and SWSR) or manipulate the DOM (CSR)" width="608" height="228"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As noted by Jason Miller and Addy Osmani in their &lt;a href="https://web.dev/rendering-on-the-web/"&gt;Rendering on the Web&lt;/a&gt; blog post, applications can leverage several stages of rendering (SSR used in the broader sense here), but like many they conflate SPA and CSR.&lt;br&gt;
Eleventy (and possibly others) also allows &lt;a href="https://www.11ty.dev/docs/plugins/edge/"&gt;rendering a given page at different stages&lt;/a&gt;, with parts of the page prerendered at build-time or rendered on the origin server, while other parts will be rendered at the edge.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does that imply? &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;My main point is that rendering is almost orthogonal to single-page vs multi-page: an SPA doesn't imply CSR.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://chromestatus.com/metrics/feature/timeline/popularity/2617"&gt;Most web sites are MPAs&lt;/a&gt; with SSR, sometimes ESR.&lt;/li&gt;
&lt;li&gt;Most React/Vue/Angular applications are SPAs with CSR: the HTML page is mostly empty, generally the same for every URL, and the page loads data on &lt;em&gt;boot&lt;/em&gt; and renders it (at the time of writing, the &lt;a href="https://angular.io"&gt;Angular website&lt;/a&gt; is such an SPA+CSR).&lt;/li&gt;
&lt;li&gt;Next.js/Gatsy/Remix/Nuxt/Angular Universal/Svelte Kit/Solid Start/îles applications are SPAs with SSR and CSR: data is present as HTML in the  page, but navigations then use CSR staying on the same page (and actually, despite the content being present in the HTML page, those frameworks will discard and re-render it client-side on &lt;em&gt;boot&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;Qwik City/Astro/Deno Fresh/Enhance/Marko Run applications are MPAs with SSR (and CSR as needed through &lt;a href="https://jasonformat.com/islands-architecture/"&gt;&lt;em&gt;islands of interactivity&lt;/em&gt;&lt;/a&gt;); Qwik City provides &lt;a href="https://qwik.builder.io/docs/faq/#can-qwik-do-spa"&gt;an easy way&lt;/a&gt; to switch to an SPA with SSR and CSR (though contrary to the above-mentioned frameworks, Qwik City won't re-render on page load).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://turbo.hotwired.dev/handbook/drive"&gt;Hotwire Turbo Drive&lt;/a&gt; (literally &lt;em&gt;HTML over the wire&lt;/em&gt;; formerly Turbolinks) and &lt;a href="https://htmx.org"&gt;htmx&lt;/a&gt; applications are SPAs with SSR.&lt;/li&gt;
&lt;li&gt;GitHub is known for its use of Turbolinks and is actually both MPA and SPA, depending on pages and sometimes navigation (going from a user profile to a repository loads a new page, but the reverse is a client-side navigation).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some combinations aren't really useful: an MPA with CSR (and without SSR) would mean loading an almost empty HTML page at each navigation to then fetch data (or possibly getting it right from HTML page) and do the rendering. Imagine the Angular website (which already makes a dubious choice of not including the content in the HTML page, for a documentation site) but where all navigations would load a new (almost empty) page.&lt;/p&gt;

&lt;p&gt;Similarly, if you're doing a SPA, there's no real point in doing rendering in a service worker as it could just as well be done in the browsing context; unless maybe you're doing SPA navigation only on some pages/situations (video playing?) and want to leverage SWSR for all pages including MPAs?&lt;/p&gt;

&lt;h2&gt;
  
  
  Other considerations
&lt;/h2&gt;

&lt;p&gt;In an application architecture, navigation and rendering locality aren't the only considerations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inline updates
&lt;/h3&gt;

&lt;p&gt;Not every interaction has to be a navigation:&lt;br&gt;
there are many cases where a form submission would &lt;em&gt;return&lt;/em&gt; to the same page (reacting to an article on &lt;a href="https://dev.to"&gt;Dev.to&lt;/a&gt;, posting a comment, updating your shopping cart), in which case progressive enhancement could be used to do an inline update without a full page refresh.&lt;/p&gt;

&lt;p&gt;Those are independent from SPAs: you can very well have an MPA and use such inline updates.&lt;br&gt;
Believe it or not, this is exactly what &lt;a href="https://dev.to"&gt;Dev.to&lt;/a&gt; does for their comment form (most other features like following the author, reacting to the post or a comment, or replying to a comment however won't work at all if JavaScript is somehow broken).&lt;/p&gt;

&lt;h3&gt;
  
  
  Concatenation and Includes &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Long before we had capable enough JavaScript in the browser to build full-blown applications (in the old times of DHTML, before AJAX), there already were optimization techniques on the servers to help build an HTML page from different pieces, some of which could have been &lt;em&gt;prerendered&lt;/em&gt; and/or cached.&lt;br&gt;
Those were &lt;a href="https://en.wikipedia.org/wiki/Server_Side_Includes"&gt;&lt;em&gt;server-side includes&lt;/em&gt;&lt;/a&gt; and &lt;a href="https://www.w3.org/TR/esi-lang/"&gt;&lt;em&gt;edge-side includes&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While they are associated with specific syntaxes, the concepts can be used today &lt;a href="https://blog.cloudflare.com/edge-side-includes-with-cloudflare-workers/"&gt;in edge functions&lt;/a&gt; or &lt;a href="https://philipwalton.com/articles/smaller-html-payloads-with-service-workers/"&gt;even in service workers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The different parts being concatenated/included this way can be themselves static or prerendered, or rendered on-demand.&lt;br&gt;
Actually the above-mentioned feature of Eleventy where parts of a page are server-rendered or prerendered and other parts are rendered at the edge is very similar to those &lt;em&gt;includes&lt;/em&gt; as well.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>spa</category>
      <category>mpa</category>
      <category>rendering</category>
    </item>
    <item>
      <title>Migrating from Jekyll to Eleventy</title>
      <dc:creator>Thomas Broyer</dc:creator>
      <pubDate>Mon, 13 Mar 2023 01:12:34 +0000</pubDate>
      <link>https://dev.to/tbroyer/migrating-from-jekyll-to-eleventy-1g50</link>
      <guid>https://dev.to/tbroyer/migrating-from-jekyll-to-eleventy-1g50</guid>
      <description>&lt;p&gt;Yes, this is going to be yet another one of those articles explaining how I migrated &lt;a href="https://blog.ltgt.net"&gt;my blog&lt;/a&gt; from &lt;a href="https://jekyllrb.com/"&gt;Jekyll&lt;/a&gt; to &lt;a href="https://11ty.dev/"&gt;Eleventy&lt;/a&gt;. You've been warned.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why?
&lt;/h2&gt;

&lt;p&gt;I don't really have issues with Jekyll and I've been using it for 10 years now here, but I haven't really &lt;em&gt;chosen&lt;/em&gt; Jekyll: it's been more-or-less imposed on me by GitHub Pages.&lt;br&gt;
But GitHub now has added the possibility to &lt;a href="https://docs.github.com/en/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site#publishing-with-a-custom-github-actions-workflow"&gt;deploy using a custom GitHub Actions workflow&lt;/a&gt;, and this is game-changer!&lt;/p&gt;

&lt;p&gt;I could have kept using Jekyll with unlocked possibilities, but I'm not a Rubyist, that's just not a language I'm comfortable with, and I know almost nothing about Gems, so definitely not something I'd be comfortable maintaining going forward.&lt;/p&gt;

&lt;p&gt;I also could have just kept using the built-in Jekyll Pages integration, and this is what I would have done if I hadn't found any satisfying alternative. I'm not forced to change, so at least I have a fallback in the form of the &lt;em&gt;status quo&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So what would replace it? Let's evaluate my requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;I have articles written in HTML (exports from Posterous) and Markdown, using a bit of Liquid to link to other articles (with the &lt;code&gt;post_url&lt;/code&gt; Jekyll tag). The Markdown articles use &lt;a href="https://github.github.com/gfm/"&gt;GitHub Flavored Markdown&lt;/a&gt;, including syntax-highlighted fenced code blocks, with embedded HTML. Ideally I shouldn't have to update the articles at all.&lt;/li&gt;
&lt;li&gt;I only have 4 templates only (&lt;code&gt;index.html&lt;/code&gt;, &lt;code&gt;rss.xml&lt;/code&gt;, and &lt;code&gt;default&lt;/code&gt; and &lt;code&gt;post&lt;/code&gt; layouts) so migrating to another templating engine wouldn't really be a problem. The &lt;code&gt;index.html&lt;/code&gt; uses pagination (even though I still only have a single page). The &lt;code&gt;default&lt;/code&gt; layout builds a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP"&gt;Content Security Policy&lt;/a&gt; using flags from the articles' front matter.&lt;/li&gt;
&lt;li&gt;I also have a few static files: CSS, JS, and images (and a file to &lt;a href="https://support.google.com/webmasters/answer/9008080?hl=en#html_verification"&gt;verify ownership&lt;/a&gt; for the Google Search Console).&lt;/li&gt;
&lt;li&gt;Of course, because &lt;a href="https://www.w3.org/Provider/Style/URI"&gt;cool URIs don't change&lt;/a&gt;, the permalinks have to be ported to the new solution.&lt;/li&gt;
&lt;li&gt;I hadn't identified it at first, but I actually have an old article that's not published, through Jekyll's &lt;code&gt;published: false&lt;/code&gt; in the front matter. In the worst case, I'd just delete it (it'd still be there in the Git history).&lt;/li&gt;
&lt;li&gt;Nice to have: I kinda like Jekyll's &lt;code&gt;_drafts&lt;/code&gt; folder using the file's last modified date, and &lt;code&gt;_posts&lt;/code&gt; folder with the publication date as part of the file name. (I don't commit my drafts, and yes that means I don't have backups; I don't have many drafts, and I'll probably never finish and publish them so 🤷)&lt;/li&gt;
&lt;li&gt;Of course I want something I'm comfortable using for the next 10 years, in terms of technology and ecosystem. This means essentially that I'd like a Node-based solution.&lt;/li&gt;
&lt;li&gt;Last, but not least, I want the output to be (almost) identical (for now at least) to the Jekyll site: must be static HTML, with &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;s added by the layouts and possibly right from the articles, no &lt;em&gt;client-side hydration&lt;/em&gt; and upgrading to a Single Page Application.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The choice
&lt;/h3&gt;

&lt;p&gt;The &lt;em&gt;HTML-first&lt;/em&gt; approach rules out (&lt;em&gt;a priori&lt;/em&gt;, correct me if I'm wrong) every React or Vue based approach, or similar.&lt;/p&gt;

&lt;p&gt;I've quickly evaluated a couple alternatives, namely &lt;a href="https://astro.build/"&gt;Astro&lt;/a&gt; and Eleventy.&lt;/p&gt;

&lt;p&gt;Astro is fun, but I must say it doesn't really look &lt;em&gt;content oriented&lt;/em&gt;, relegating the content into its &lt;code&gt;src/pages&lt;/code&gt;, or worse, a subfolder inside &lt;code&gt;src/content/&lt;/code&gt;.&lt;br&gt;
I really like the typesafe nature of content collections, but moving everything down to &lt;code&gt;src/content/blog&lt;/code&gt; really &lt;em&gt;hides&lt;/em&gt; the content away IMO.&lt;br&gt;
Extracting the publication date from the file name &lt;a href="https://github.com/humanwhocodes/astro-jekyll"&gt;is possible&lt;/a&gt;, but it looks more and more like a &lt;em&gt;development&lt;/em&gt; project rather than a &lt;em&gt;content&lt;/em&gt; project.&lt;br&gt;
It's great, but not what I'm looking for here.&lt;/p&gt;

&lt;p&gt;I then looked at Eleventy. I have to admit my first contacts with the Eleventy documentation months ago left me with a bitter taste as I couldn't really figure out how collections worked and how you were supposed (or not) to organize your files. Looking at &lt;a href="https://github.com/tweetback/tweetback"&gt;tweetback&lt;/a&gt; more recently didn't really help: absolutely everything is JS, loading content from a SQLite database.&lt;/p&gt;

&lt;p&gt;I decided to give it a chance: maybe I misunderstood the documentation the last time(s) I read it.&lt;br&gt;
And indeed it was the case: moving from Jekyll to Eleventy probably couldn't be easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  How?
&lt;/h2&gt;

&lt;p&gt;I felt my way a bit, so I'll summarize here &lt;a href="https://github.com/tbroyer/blog.ltgt.net/commit/1baabc320ebefbbbaae2e37c6beeceed2c2167cf"&gt;what I ended up doing&lt;/a&gt;, also describing some things I tried along the way.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting Started
&lt;/h3&gt;

&lt;p&gt;Removing Jekyll consists in deleting the &lt;code&gt;_config.yml&lt;/code&gt; and possibly &lt;code&gt;Gemfile&lt;/code&gt; (I didn't have one).&lt;br&gt;
Adding Eleventy means initializing a new NPM packaging and adding the &lt;code&gt;@11ty/eleventy&lt;/code&gt; dependency (and of course adding &lt;code&gt;node_modules&lt;/code&gt; to the &lt;code&gt;.gitignore&lt;/code&gt;), and creating a &lt;a href="https://www.11ty.dev/docs/config/#default-filenames"&gt;configuration file&lt;/a&gt; (I chose &lt;code&gt;eleventy.config.cjs&lt;/code&gt; rather than the &lt;code&gt;.eleventy.js&lt;/code&gt; hidden file).&lt;/p&gt;

&lt;p&gt;Because the deployment workflow is different, the &lt;code&gt;CNAME&lt;/code&gt; file becomes useless and can be deleted.&lt;br&gt;
A new GitHub Actions workflow also has to be created, using the &lt;code&gt;actions/configure-pages&lt;/code&gt;, &lt;code&gt;actions/upload-pages-artifact&lt;/code&gt;, and &lt;code&gt;actions/deploy-pages&lt;/code&gt; actions. I took inspiration from &lt;a href="https://github.com/actions/starter-workflows/blob/main/pages/astro.yml"&gt;the Astro starter workflow&lt;/a&gt; and updated it for Eleventy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Markdown
&lt;/h3&gt;

&lt;p&gt;Eleventy supports Markdown out of the box, with all the options I needed, except syntax highlighting and heading anchors for deep linking.&lt;br&gt;
It also automatically &lt;a href="https://www.11ty.dev/docs/dates/"&gt;extracts the date from the file name&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Syntax highlighting is as easy as using &lt;a href="https://www.11ty.dev/docs/plugins/syntaxhighlight/"&gt;the official plugin&lt;/a&gt;, but then the generated HTML markup is different than with the Rouge highlighter in Jekyll, so I had to change the CSS accordingly.&lt;br&gt;
I ended up importing an existing theme: display would be slightly different than before, but actually probably better looking.&lt;/p&gt;

&lt;p&gt;Deep linking requires using &lt;a href="https://github.com/valeriangalliat/markdown-it-anchor"&gt;the &lt;code&gt;markdown-it-anchor&lt;/code&gt; plugin&lt;/a&gt;, and to make sure existing deep links wouldn't break I provided my own &lt;code&gt;slugify&lt;/code&gt; function mimicking the way CommonMarkGhPages computes the slug from the heading text (I happen to have a few headings with &lt;code&gt;&amp;lt;code&amp;gt;&lt;/code&gt; in them, and CommonMarkGhPages would compute the slug from the rendered HTML leading to things like &lt;code&gt;codejavaccode&lt;/code&gt;; I chose to break those few links in favor of better-looking anchor slugs).&lt;br&gt;
I also disabled &lt;code&gt;tabIndex&lt;/code&gt; to keep the same rendering as previously (I'll read more on the accessibility implications and possibly revert that choice later.)&lt;/p&gt;

&lt;p&gt;I reimplemented the &lt;code&gt;post_url&lt;/code&gt; first as a &lt;a href="https://www.11ty.dev/docs/shortcodes/"&gt;custom short code&lt;/a&gt; but that meant updating all articles to quote the argument (due to how Eleventy wires things up), so I ended up using a &lt;a href="https://www.11ty.dev/docs/custom-tags/"&gt;custom tag&lt;/a&gt;; that's specific to the Liquid template engine (in case I would want to change later on) but at least I don't have to update the articles.&lt;/p&gt;

&lt;p&gt;In terms of rendering, besides syntax highlighting, the only difference is the &lt;code&gt;&amp;lt;br&amp;gt;&lt;/code&gt; which are now rendered that way rather than &lt;code&gt;&amp;lt;br /&amp;gt;&lt;/code&gt; (there's an option in &lt;code&gt;markdown-it&lt;/code&gt; but I'll keep the less XHTML-y, more HTML-y syntax).&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;rss.xml&lt;/code&gt; file wouldn't be treated as a template by default, so I &lt;a href="https://www.11ty.dev/docs/languages/custom/#aliasing-an-existing-template-language"&gt;aliased the &lt;code&gt;xml&lt;/code&gt; extension&lt;/a&gt; to the Liquid engine, and added an explicit &lt;code&gt;permalink:&lt;/code&gt; to avoid Eleventy creating an &lt;code&gt;rss.xml/index.html&lt;/code&gt; file.&lt;br&gt;
I did the same with the &lt;code&gt;css&lt;/code&gt; extension so I could &lt;a href="https://www.11ty.dev/docs/languages/liquid/#supported-features"&gt;use an &lt;code&gt;include&lt;/code&gt;&lt;/a&gt; to bring in the syntax-highlighting theme in my &lt;code&gt;style.css&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Liquid Templating
&lt;/h3&gt;

&lt;p&gt;I had to rename my layout files to use a &lt;code&gt;.liquid&lt;/code&gt; extension rather than &lt;code&gt;.html&lt;/code&gt;.&lt;br&gt;
I didn't want to move them though, so I &lt;a href="https://www.11ty.dev/docs/config/#directory-for-layouts-(optional)"&gt;configured a layouts directory&lt;/a&gt; instead.&lt;/p&gt;

&lt;p&gt;I also had to handle all the Jekyll-specific things I was using: &lt;code&gt;xml_escape&lt;/code&gt;, &lt;code&gt;date_to_xmlschema&lt;/code&gt;, &lt;code&gt;date_to_string&lt;/code&gt;, and &lt;code&gt;date_to_long_string&lt;/code&gt; filters, and the &lt;code&gt;site.time&lt;/code&gt; and &lt;code&gt;site.github.url&lt;/code&gt; variables (we already handled the &lt;code&gt;post_url&lt;/code&gt; tag above).&lt;/p&gt;

&lt;p&gt;At first, I tried to recreate them in Eleventy (which is easy with &lt;a href="https://www.11ty.dev/docs/shortcodes/"&gt;custom shortcodes&lt;/a&gt; and &lt;a href="https://www.11ty.dev/docs/data-global/"&gt;global data files&lt;/a&gt;), but finally decided that I could replace most with more standard Liquid that would be compatible right-away with LiquidJS: &lt;code&gt;xml_escape&lt;/code&gt; becomes &lt;code&gt;escape&lt;/code&gt;, &lt;code&gt;date_*&lt;/code&gt; become &lt;code&gt;date:&lt;/code&gt; with the appropriate format (this made it possible to fix my &lt;code&gt;&amp;lt;time&amp;gt;&lt;/code&gt; elements erroneously including the time), and &lt;code&gt;site.time&lt;/code&gt; becomes &lt;code&gt;"now"&lt;/code&gt; or &lt;code&gt;"today"&lt;/code&gt; with the &lt;code&gt;date&lt;/code&gt; filter.&lt;br&gt;
I put that in a separate commit as that's compatible with Jekyll Liquid as well.&lt;br&gt;
And all that's left is therefore &lt;code&gt;site.github.url&lt;/code&gt; that can be put in a global data file (a JS file getting the value out of an environment variable, fed by the &lt;code&gt;actions/configure-pages&lt;/code&gt; output in the GitHub Actions workflow).&lt;/p&gt;

&lt;p&gt;Finally, I actually had to update all templates to use Eleventy's way of &lt;a href="https://www.11ty.dev/docs/pagination/"&gt;handling pagination&lt;/a&gt;, and looping over collections.&lt;/p&gt;

&lt;p&gt;Speaking of collections, I initially used &lt;a href="https://www.11ty.dev/docs/data-template-dir/"&gt;directory data files&lt;/a&gt; to assign a &lt;code&gt;post&lt;/code&gt; tag to all posts in &lt;code&gt;_posts&lt;/code&gt; and &lt;code&gt;_drafts&lt;/code&gt;.&lt;br&gt;
This didn't handle the &lt;code&gt;published: false&lt;/code&gt;, so I used a &lt;a href="https://www.11ty.dev/docs/collections/#advanced-custom-filtering-and-sorting"&gt;custom collection&lt;/a&gt; in the configuration file instead.&lt;br&gt;
I probably could have also used a &lt;a href="https://www.11ty.dev/docs/data-computed/"&gt;computed&lt;/a&gt; &lt;a href="https://www.11ty.dev/docs/collections/#how-to-exclude-content-from-collections"&gt;&lt;code&gt;eleventyExcludeFromCollections&lt;/code&gt;&lt;/a&gt; to exclude it, but this also helped fix an issue with the sort order and apparently a bug in LiquidJS's &lt;code&gt;for&lt;/code&gt; loop with both &lt;code&gt;reversed&lt;/code&gt; and &lt;code&gt;limit:&lt;/code&gt; where it would limit before reversing whichever way I wrote things, contrary to &lt;a href="https://liquidjs.com/tags/for.html#reversed"&gt;what the doc says&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One last change I made: update the Content Security Policy to account for the Eleventy dev mode autoreload; I used &lt;code&gt;eleventy.env.runMode != "build"&lt;/code&gt; to &lt;a href="https://www.11ty.dev/docs/data-eleventy-supplied/#eleventy-variable"&gt;detect when run with autoreload&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Static Files
&lt;/h3&gt;

&lt;p&gt;Contrary to Jekyll where any file without front matter is simply copied, static files have to be &lt;a href="https://www.11ty.dev/docs/copy/"&gt;explicitly declared&lt;/a&gt; with Eleventy.&lt;br&gt;
I also had to &lt;a href="https://www.11ty.dev/docs/ignores/"&gt;ignore&lt;/a&gt; those HTML files I needed to just copy without processing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Permalinks
&lt;/h3&gt;

&lt;p&gt;Permalinks for the &lt;code&gt;rss.xml&lt;/code&gt; and &lt;code&gt;style.css&lt;/code&gt; are defined right in those files' front matter.&lt;br&gt;
The &lt;code&gt;index.html&lt;/code&gt; uses pagination so I &lt;a href="https://www.11ty.dev/docs/pagination/#remapping-with-permalinks"&gt;declared a mapping&lt;/a&gt; there as well.&lt;/p&gt;

&lt;p&gt;Finally I decided to compute the permalink for posts right in the front matter of the &lt;code&gt;post&lt;/code&gt; layout, using the &lt;code&gt;page.fileSlug&lt;/code&gt; gives me exactly what I want (the date part has already been removed by Eleventy).&lt;br&gt;
Using a JS front matter allowed me to filter out the &lt;code&gt;published: false&lt;/code&gt; article so it &lt;a href="https://www.11ty.dev/docs/permalinks/#skip-writing-to-the-file-system"&gt;wouldn't ever be rendered to disk&lt;/a&gt; (I already excluded it from the &lt;code&gt;posts&lt;/code&gt; collection, but Eleventy would still process and render it).&lt;/p&gt;

&lt;h3&gt;
  
  
  Drafts
&lt;/h3&gt;

&lt;p&gt;To handle drafts, I'm using &lt;a href="https://www.11ty.dev/docs/collections/#getfilteredbyglob(-glob-)"&gt;the &lt;code&gt;getFilteredByGlob&lt;/code&gt; function&lt;/a&gt; when declaring the &lt;code&gt;posts&lt;/code&gt; collection, so I can decide whether to include the &lt;code&gt;_drafts&lt;/code&gt; folder depending on an environment variable.&lt;br&gt;
This would include  the drafts in the &lt;code&gt;posts&lt;/code&gt; collection so they would appear in the &lt;code&gt;index.html&lt;/code&gt; and &lt;code&gt;rss.xml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;More importantly though, when not including drafts, I have to ignore the &lt;code&gt;_drafts&lt;/code&gt; folder, otherwise the drafts are still processed and generated (despite not being linked to as they don't appear in the &lt;code&gt;posts&lt;/code&gt; collection).&lt;br&gt;
This is actually not really a problem given that I don't commit drafts to my Git repository, so I would observe this behavior only locally.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comparing the results
&lt;/h3&gt;

&lt;p&gt;To make sure the output was identical to the Jekyll-based version, I built the site once with Jekyll before any modification and backed up the &lt;code&gt;_site&lt;/code&gt; folder; then &lt;a href="https://meldmerge.org/"&gt;compared it&lt;/a&gt; with the output of Eleventy to make sure everything was OK.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;As I felt my way and learned about Eleventy, this took me nearly two weekends to complete (not full time, don't worry!)&lt;br&gt;
What took me the most time actually was probably finding (and deciding on) the new syntax-highlighting theme!&lt;br&gt;
Otherwise, things went really smoothly.&lt;/p&gt;

&lt;p&gt;I'm very happy with the outcome, so I switched over.&lt;br&gt;
And now that I control the build workflow, I know I could setup an asset pipeline, minify the generated HTML, bring in &lt;a href="https://github.com/11ty/eleventy-plugin-bundle"&gt;more Eleventy plugins&lt;/a&gt; to split the syntax-highlighting theme out and &lt;a href="https://piaille.fr/@tbroyer/109988938746853236"&gt;only send it when there's a code block&lt;/a&gt; on the page, etc.&lt;/p&gt;

&lt;p&gt;A big &lt;strong&gt;would recommend!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>eleventy</category>
      <category>jekyll</category>
    </item>
    <item>
      <title>The benefits of Web Component Libraries</title>
      <dc:creator>Thomas Broyer</dc:creator>
      <pubDate>Mon, 27 Feb 2023 17:27:07 +0000</pubDate>
      <link>https://dev.to/tbroyer/the-benefits-of-web-component-libraries-527h</link>
      <guid>https://dev.to/tbroyer/the-benefits-of-web-component-libraries-527h</guid>
      <description>&lt;p&gt;Web component browser APIs aren't that many, and not that hard to grasp (if you don't know about them, have a look at Google's &lt;a href="https://web.dev/learn/html/template/" rel="noopener noreferrer"&gt;Learn HTML section&lt;/a&gt; and MDN's &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components" rel="noopener noreferrer"&gt;Web Components guide&lt;/a&gt;);&lt;br&gt;
but creating a web component actually requires &lt;a href="https://web.dev/custom-elements-best-practices/" rel="noopener noreferrer"&gt;taking care of many small things&lt;/a&gt;.&lt;br&gt;
This is where web component libraries come in very handy, freeing us of having to think about some of those things by taking care of them for us.&lt;br&gt;
Most of the things I'll mention here are handled one way of another by other libraries (GitHub's &lt;a href="https://catalyst.rocks/" rel="noopener noreferrer"&gt;Catalyst&lt;/a&gt;, &lt;a href="https://github.com/matthewp/haunted" rel="noopener noreferrer"&gt;Haunted&lt;/a&gt;, &lt;a href="https://hybrids.js.org/" rel="noopener noreferrer"&gt;Hybrids&lt;/a&gt;, Salesforce's &lt;a href="https://lwc.dev" rel="noopener noreferrer"&gt;LWC&lt;/a&gt;, &lt;a href="https://slimjs.com/" rel="noopener noreferrer"&gt;Slim.JS&lt;/a&gt;, Ionic's &lt;a href="https://stenciljs.com/" rel="noopener noreferrer"&gt;Stencil&lt;/a&gt;) but I'll focus on Google's &lt;a href="https://lit.dev" rel="noopener noreferrer"&gt;Lit&lt;/a&gt; and Microsoft's &lt;a href="https://fast.design" rel="noopener noreferrer"&gt;FAST&lt;/a&gt; here as they probably are the most used web component libraries out there (ok, &lt;a href="https://npmtrends.com/@github/catalyst-vs-@lwc/compiler-vs-@microsoft/fast-element-vs-@stencil/core-vs-haunted-vs-hybrids-vs-lit-vs-slim-js" rel="noopener noreferrer"&gt;I lied&lt;/a&gt;, Lit definitely is, FAST not that much, far behind Lit and Stencil; but Lit and FAST have many things in common, starting with the fact that they are &lt;em&gt;just&lt;/em&gt; native web components, contrary to Stencil that &lt;em&gt;compiles&lt;/em&gt; to a web component).&lt;br&gt;
Both Lit and FAST leverage TypeScript decorators to simplify the code even further so I'll use that in examples,&lt;br&gt;
even though they can also be used in pure JS (decorators are coming to JS soon BTW). I'll also leave the most apparent yet most complex aspect for the end.&lt;/p&gt;

&lt;p&gt;Let's dive in!&lt;/p&gt;
&lt;h2&gt;
  
  
  Registration
&lt;/h2&gt;

&lt;p&gt;It's a small detail, and I wouldn't even consider it a &lt;em&gt;benefit&lt;/em&gt;;&lt;br&gt;
it's mostly syntactic sugar, but with FAST at least it also does a few other things that we'll see later: registering the custom element.&lt;/p&gt;

&lt;p&gt;In vanilla JS, it goes like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-element&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MyElement&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With Lit:&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="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;customElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-element&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And with FAST:&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="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;customElement&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-element&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// other properties will come here later&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;FASTElement&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Attributes and Properties
&lt;/h2&gt;

&lt;p&gt;With native HTML elements, we're used to accessing attribute (also known as &lt;em&gt;content attributes&lt;/em&gt; in the HTML spec) values as properties (aka &lt;em&gt;IDL attributes&lt;/em&gt;) on the DOM element (think &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;checked&lt;/code&gt;, &lt;code&gt;disabled&lt;/code&gt;, &lt;code&gt;tabIndex&lt;/code&gt;, etc. even &lt;code&gt;className&lt;/code&gt; and &lt;code&gt;htmlFor&lt;/code&gt; although they use sligthly different names to avoid conflicting with JS keywords) with sometimes some specificities: the &lt;code&gt;value&lt;/code&gt; attribute of &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; elements is accessible through the &lt;code&gt;defaultValue&lt;/code&gt; property, the &lt;code&gt;value&lt;/code&gt; property giving access to the actual value of the element (along with &lt;code&gt;valueAsDate&lt;/code&gt; and &lt;code&gt;valueAsNumber&lt;/code&gt; that additionally convert the value).&lt;/p&gt;

&lt;p&gt;Custom elements have to implement this themselves if they want it, and web component libraries make it a breeze.&lt;br&gt;
They help us &lt;em&gt;reflect&lt;/em&gt; properties to attributes when they are modified (if that's what we want; and all while avoiding infinite loops), convert attribute values (always strings) to/from property values, or handle &lt;em&gt;boolean attributes&lt;/em&gt; (where we're only interested in their absence or presence, not their actual value: think &lt;code&gt;checked&lt;/code&gt; and &lt;code&gt;disabled&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Let's compare some of these cases, first without library:&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;MyElement&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;// Attributes have to be explicitly observed&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reflected-converted&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="s1"&gt;reflected&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="s1"&gt;non-reflected&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="s1"&gt;bool&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="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;reflectedConverted&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// In a real element, you'd probably use a getter and setter&lt;/span&gt;
    &lt;span class="c1"&gt;// to somehow update the element when the property is set.&lt;/span&gt;
    &lt;span class="nx"&gt;nonReflected&lt;/span&gt;&lt;span class="p"&gt;;&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;switch &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="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reflected-converted&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;// Convert the attribute value&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;reflectedConverted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nc"&gt;Number&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="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;non-reflected&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="nx"&gt;nonReflected&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="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;// other attributes handled in accessors below&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;reflectedConverted&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="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;reflectedConverted&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;reflectedConverted&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="c1"&gt;// avoid infinite loop with attributeChangedCallback&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;newValue&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;reflectedConverted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;reflectedConverted&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newValue&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reflected-converted&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;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// Here we let the browser automatically convert to string&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;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reflected-converted&lt;/span&gt;&lt;span class="dl"&gt;'&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;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;reflected&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="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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reflected&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;set&lt;/span&gt; &lt;span class="nf"&gt;reflected&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;newValue&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reflected&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;else&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;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reflected&lt;/span&gt;&lt;span class="dl"&gt;'&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;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;bool&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bool&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;set&lt;/span&gt; &lt;span class="nf"&gt;bool&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toggleAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bool&lt;/span&gt;&lt;span class="dl"&gt;'&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;Now with Lit:&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;property&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reflected-converted&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;reflect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;reflectedConverted&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;property&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;reflect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;reflected&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;property&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;non-reflected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;nonReflected&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;property&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Boolean&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And with FAST:&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;FASTElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reflected-converted&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;nullableNumberConverter&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;reflectedConverted&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;attr&lt;/span&gt; &lt;span class="nx"&gt;reflected&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;attribute&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;non-reflected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fromView&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;nonReflected&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;boolean&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Early-initialized properties
&lt;/h2&gt;

&lt;p&gt;Another thing with properties is that they could be set on a DOM element even before it's &lt;em&gt;upgraded&lt;/em&gt;:&lt;br&gt;
the script that defines the custom element does not need to be loaded by the time the browser parses the custom tag in the HTML,&lt;br&gt;
and some script might access that element in the DOM before the script that &lt;em&gt;defines&lt;/em&gt; it has loaded;&lt;br&gt;
only then will the element be &lt;em&gt;upgraded&lt;/em&gt;: the class instantiated to take control of the custom element.&lt;/p&gt;

&lt;p&gt;When that happens, you wouldn't want properties that would have been set on the element earlier to be overwritten by the upgrade process.&lt;/p&gt;

&lt;p&gt;Without a library, you would have to take care of it yourself with code similar to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&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="c1"&gt;// "upgrade" properties&lt;/span&gt;
        &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;propName&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reflectedConverted&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="s1"&gt;reflected&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="s1"&gt;nonReflected&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="s1"&gt;bool&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasOwnProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;propName&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;value&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;propName&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
                &lt;span class="k"&gt;delete&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;propName&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;propName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&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;Again, libraries do that for you, automatically, based on the previously seen declarations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Responding to property changes
&lt;/h2&gt;

&lt;p&gt;The common way to respond to property changes is to implement a setter. This requires also implementing a getter though, as well as storing the value in a private field. When changing the value from &lt;code&gt;attributeChangedCallback&lt;/code&gt;, make sure to also use the setter and not assign directly to the backing field.&lt;/p&gt;

&lt;p&gt;To respond to changes to the &lt;code&gt;nonReflected&lt;/code&gt; property in the above example, one would have to write it 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="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;nonReflected&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;nonReflected&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="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;nonReflected&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;nonReflected&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;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;nonReflected&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="c1"&gt;// respond to change here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both Lit and FAST provide their own way of doing this, though most of the time this is not really needed given that most reaction to change is to update the shadow tree, and Lit and FAST have their own ways of doing this (see below for more about rendering).&lt;/p&gt;

&lt;p&gt;With Lit, you listen to changes to any property and have to tell them apart by name, a bit similar to &lt;code&gt;attributeChangedCallback&lt;/code&gt; but &lt;em&gt;batched&lt;/em&gt; for several properties at a time:&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="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;property&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;non-reflected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nx"&gt;nonReflected&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// You could also use updated(changedProperties), depending on your needs&lt;/span&gt;
&lt;span class="nf"&gt;willUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changedProperties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PropertyValues&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changedProperties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nonReflected&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;// respond to change here&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;With FAST, you can implement a method with a &lt;code&gt;Changed&lt;/code&gt; suffix appended to the property name:&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="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;attribute&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;non-reflected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fromView&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nx"&gt;nonReflected&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;nonReflectedChanged&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="kr"&gt;string&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// respond to change here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Shadow DOM and CSS stylesheets
&lt;/h2&gt;

&lt;p&gt;The most efficient way to manage CSS stylesheets in Shadow DOM is to use so-called &lt;em&gt;constructable stylesheets&lt;/em&gt;: construct a &lt;code&gt;CSSStyleSheet&lt;/code&gt; instance once (or soon &lt;code&gt;import&lt;/code&gt; a CSS file), then reuse it in each element's shadow tree through &lt;code&gt;adoptedStyleSheets&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;const&lt;/span&gt; &lt;span class="nx"&gt;sheet&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;CSSStyleSheet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
    :host { display: block; }
    :host([hidden]) { display: none; }
`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attachShadow&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;open&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="nx"&gt;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;adoptedStyleSheets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;sheet&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;With Lit, you'd rather use this more &lt;em&gt;declarative&lt;/em&gt; syntax:&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="s2"&gt;`
        :host { display: block; }
        :host([hidden]) { display: none; }
    `&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;and similarly with FAST:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="s2"&gt;`
    :host { display: block; }
    :host([hidden]) { display: none; }
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;customElement&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-element&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;FASTElement&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Constructable stylesheets currently still require a polyfill in Safari though (this is being added in Safari 16.4), but both Lit and FAST take care of this for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rendering and templating
&lt;/h2&gt;

&lt;p&gt;The most efficient way to populate the shadow tree of a custom element is by cloning a &lt;em&gt;template&lt;/em&gt; that has been initialized once.&lt;br&gt;
That template could be any document fragment but the &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; element was made specifically for these use-cases.&lt;br&gt;
You would then retrieve nodes inside the shadow tree to add event listeners and/or manipulate it in response to those inside events or to attribute and property changes (see above) from the outside.&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;template&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="s1"&gt;template&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;button&amp;gt;Add&amp;lt;/button&amp;gt;
    &amp;lt;output&amp;gt;&amp;lt;/output&amp;gt;
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;output&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="c1"&gt;// … (upgrade properties as seen above) …&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;attachShadow&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;open&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="nx"&gt;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cloneNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="c1"&gt;// Using an &amp;lt;output&amp;gt; element makes it easier&lt;/span&gt;
        &lt;span class="c1"&gt;// We could also create a text node and append it ourselves&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;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;output&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="nx"&gt;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;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;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&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;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// … (count property, with attribute changed callback and converter) …&lt;/span&gt;
    &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;count&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="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// … (reflect to attribute or whatever) …&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;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&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="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-element&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MyElement&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Things get much more complex when you want to conditionally render some subtrees (the easiest probably being to toggle their &lt;code&gt;hidden&lt;/code&gt; attribute), or render and update a list of elements.&lt;/p&gt;

&lt;p&gt;This is where Lit and FAST (and a bunch of other libraries) work much differently from the above, introducing the concept of &lt;em&gt;reactive&lt;/em&gt; or &lt;em&gt;observable&lt;/em&gt; properties and a &lt;em&gt;render&lt;/em&gt; lifecycle based on a specific syntax for templates allowing placeholders for dynamic values, a syntax to register event listeners right from the template, and composability.&lt;/p&gt;

&lt;p&gt;With Lit, that could look like:&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="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;customElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-element&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;property&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&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="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// No need for an &amp;lt;output&amp;gt; element here, though we could&lt;/span&gt;
        &lt;span class="c1"&gt;// (and it would possibly even be better for accessibility)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
            &amp;lt;button @click=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;Add&amp;lt;/button&amp;gt;
            &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&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="p"&gt;}&lt;/span&gt;

    &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and with FAST:&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="c1"&gt;// No need for an &amp;lt;output&amp;gt; element here, though we could&lt;/span&gt;
&lt;span class="c1"&gt;// (and it would possibly even be better for accessibility)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MyElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;`
    &amp;lt;button @click=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&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="s2"&gt;&amp;gt;Add&amp;lt;/button&amp;gt;
    &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;customElement&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-element&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;FASTElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;numberConverter&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The way Lit and FAST work is by observing changes to the properties and scheduling an update everytime it happens.&lt;/p&gt;

&lt;p&gt;With Lit, the update (also called &lt;em&gt;rerender&lt;/em&gt;) will call the &lt;code&gt;render()&lt;/code&gt; method of the component and then process the template. The rerender is scheduled using a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide" rel="noopener noreferrer"&gt;microtask&lt;/a&gt; such that it can &lt;em&gt;batch&lt;/em&gt; changes to multiple properties into a single rerender.&lt;/p&gt;

&lt;p&gt;With FAST, the update is instead scheduled using &lt;code&gt;requestAnimationFrame&lt;/code&gt; (achieving the same &lt;em&gt;batching&lt;/em&gt; as Lit) and will call every lambda of the template that needs to be: FAST tracks which dynamic part uses which properties to only reevaluate those parts when a given property changes.&lt;/p&gt;

&lt;p&gt;In the examples above, any change to the &lt;code&gt;count&lt;/code&gt; property, either from the outside or in response to the click of the button, schedules a update.&lt;br&gt;
And in FAST's case, only the &lt;code&gt;x =&amp;gt; x.count&lt;/code&gt; lambda is called and the corresponding DOM node updated.&lt;br&gt;
In Lit's case, the button's click listener would also be evaluated, but determined to be the same as before so no change would be performed.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;html&lt;/code&gt; tagged template literal (both in Lit and FAST, also in other libraries such as &lt;a href="https://github.com/github/jtml" rel="noopener noreferrer"&gt;&lt;code&gt;@github/jtml&lt;/code&gt;&lt;/a&gt;) will use a &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; under the hood to parse the HTML once and reuse it later, just like the &lt;code&gt;css&lt;/code&gt; seen earlier uses a constructable stylesheet. It puts markers in the HTML (special comments, elements or attributes) in place of the dynamic parts so it can find them back to attach event listeners and inject values, making it possible to &lt;em&gt;surgically&lt;/em&gt; update only the nodes that need it, without touching anything else (FAST actually being even more &lt;em&gt;surgical&lt;/em&gt; by tracking the properties used in each dynamic part).&lt;/p&gt;

&lt;p&gt;With Lit's &lt;code&gt;render()&lt;/code&gt; method returning such a &lt;em&gt;template&lt;/em&gt; and called each time a property or internal state changed, its programming model looks a bit like React, rerendering and returning a new JSX each time a prop or state changed;&lt;br&gt;
while FAST's approach looks a bit more like Angular (or Vue or Svelte) where each component is associated with a single template at definition time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other niceties
&lt;/h2&gt;

&lt;p&gt;Lit and FAST also provide some helpers to peek into the shadow tree: to get a direct reference to some node, or the &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt;s' assigned nodes.&lt;/p&gt;

&lt;p&gt;Lit also pioneers &lt;a href="https://lit.dev/docs/composition/controllers/" rel="noopener noreferrer"&gt;&lt;em&gt;reactive controllers&lt;/em&gt;&lt;/a&gt; that allow code reuse between components through composition, where the controller can &lt;em&gt;hook&lt;/em&gt; into the render lifecycle (i.e. trigger a rerender of the component that uses the controller).&lt;br&gt;
The goal is ultimately to make them &lt;a href="https://github.com/webcomponents-cg/community-protocols/issues/27" rel="noopener noreferrer"&gt;reusable across frameworks&lt;/a&gt; too.&lt;br&gt;
Some have already embraced it, like Haunted with &lt;a href="https://hauntedhooks.netlify.app/docs/hooks/usecontroller/" rel="noopener noreferrer"&gt;its &lt;code&gt;useController()&lt;/code&gt;&lt;/a&gt; that allows using reactive controllers in Haunted components, or &lt;a href="https://apolloelements.dev/" rel="noopener noreferrer"&gt;Apollo Elements&lt;/a&gt; that's built around reactive controllers. Lit also provides a &lt;code&gt;useController()&lt;/code&gt; React hook as part of its &lt;code&gt;@lit-labs/react&lt;/code&gt; package (that also makes it easier to use a Lit component in a React application by wrapping it as a React component), and &lt;a href="https://github.com/lit/lit/issues/1682" rel="noopener noreferrer"&gt;there are prototypes&lt;/a&gt; for several frameworks such as Angular or Svelte, or even native web components &lt;a href="https://next.apolloelements.dev/api/libraries/mixins/controller-host-mixin/" rel="noopener noreferrer"&gt;through a mixin&lt;/a&gt;.&lt;br&gt;
FAST developers are interested in supporting reactive controllers too, but those currently don't quite match with the way FAST updates the rendering.&lt;/p&gt;

&lt;p&gt;The Lit team provides reactive controllers to &lt;a href="https://github.com/lit/lit/tree/main/packages/labs/context" rel="noopener noreferrer"&gt;manage contextual values&lt;/a&gt;, easily &lt;a href="https://lit.dev/docs/composition/controllers/#asynchronous-tasks" rel="noopener noreferrer"&gt;wire asynchronous tasks&lt;/a&gt; to rendering, &lt;a href="https://github.com/lit/lit/tree/main/packages/labs/observers" rel="noopener noreferrer"&gt;wrap a bunch of native observers&lt;/a&gt; (mutation, resize, intersection, performance), or &lt;a href="https://github.com/lit/lit/tree/main/packages/labs/router" rel="noopener noreferrer"&gt;handle routing&lt;/a&gt;. Others are embracing them too: Apollo Elements for GraphQL already cited above; James Garbutt has &lt;a href="https://github.com/43081j/relit" rel="noopener noreferrer"&gt;a collection of utilities&lt;/a&gt; for Lit, many of them being reactive controllers usable outside Lit; &lt;a href="https://github.com/nanostores/lit" rel="noopener noreferrer"&gt;Nano Stores&lt;/a&gt; provide reactive controllers; Guillem Cordoba has &lt;a href="https://github.com/guillemcordoba/lit-svelte-stores" rel="noopener noreferrer"&gt;controllers for Svelte Stores&lt;/a&gt;; etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Web component libraries are really helpful to streamline the development of your components.&lt;br&gt;
While you could develop custom elements without library, chances are you'd be eventually creating your own set of helpers, reinventing the wheel (there are &lt;a href="https://webcomponents.dev/blog/all-the-ways-to-make-a-web-component/board/" rel="noopener noreferrer"&gt;more than enough&lt;/a&gt; ways to build web components already).&lt;br&gt;
Understand what each library brings and pick one.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>webcomponents</category>
      <category>lit</category>
      <category>fastdesign</category>
    </item>
    <item>
      <title>The Javax to Jakarta mess, it's even worse than I thought</title>
      <dc:creator>Thomas Broyer</dc:creator>
      <pubDate>Fri, 22 Apr 2022 15:52:36 +0000</pubDate>
      <link>https://dev.to/tbroyer/the-javax-to-jakarta-mess-its-even-worse-than-i-thought-54ag</link>
      <guid>https://dev.to/tbroyer/the-javax-to-jakarta-mess-its-even-worse-than-i-thought-54ag</guid>
      <description>&lt;p&gt;In the &lt;a href="https://dev.to/tbroyer/the-javax-jakarta-mess-and-a-gradle-solution-3c44"&gt;previous post&lt;/a&gt;, I described how the Javax to Jakarta migration was a mess, but doing more research on the subject I discovered that it's actually way worse than that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EDIT(2022-11-18):&lt;/strong&gt; There's a new/updated Gradle plugin to help us, and Spring 6 uses Jakarta EE 9 as a baseline. Continue reading for details.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EDIT(2023-05-15):&lt;/strong&gt; Guice has made the switch to &lt;code&gt;jakarta&lt;/code&gt; in version 7, with a version 6 that stays on &lt;code&gt;javax&lt;/code&gt; but also supports &lt;code&gt;jakarta.inject&lt;/code&gt; to help in the transition.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wait, how could it be worse‽
&lt;/h2&gt;

&lt;p&gt;Until now, I focused on APIs, but what I forgot about were implementations. I didn't really forgot about them, as they were partly what led me to do the research to begin with, but I forgot about how you may want to have implementations for both Java EE and Jakarta EE 9+ at the same time.&lt;/p&gt;

&lt;p&gt;The case that got me looking at the subject was a combination of Resteasy, jOOQ, and Sentry-Java. Resteasy and Sentry-Java both require a Servlet implementation (I'm using an embedded Jetty server, but I could have picked Tomcat or Undertow). Resteasy and jOOQ both depend on XML Binding. Resteasy and jOOQ (and Pac4j and Jackson, that I also use) actually maintain two parallel versions, comptible with either Java EE 8 or Jakarta EE 9 (and in the case of jOOQ at least, they're not really &lt;em&gt;parallel&lt;/em&gt;, it's more that the previous version is kept &lt;em&gt;perfused&lt;/em&gt; with all the bugfixes being backported, but the featureset of the Java EE 8 compatible version is not the same as of the Jakarta EE 9 compatible one). Sentry-Java on the other side is only compatible with the &lt;code&gt;javax.&lt;/code&gt; flavor of Servlets. What this means is that I had to choose between using the &lt;em&gt;older&lt;/em&gt; versions of Resteasy and jOOQ, or forking Sentry-Java to bring it to Jakarta EE 9 (this is what I ended up doing, as it's only 3 classes). And of course I discovered the problem totally &lt;em&gt;by accident&lt;/em&gt;, looking the results of a &lt;code&gt;./gradlew dependencies&lt;/code&gt; where I had the &lt;em&gt;older&lt;/em&gt; version of Resteasy but the latest version of jOOQ, and noting that the &lt;code&gt;jakarta.xml.bind:jakarta.xml.bind-api&lt;/code&gt; transitive dependency of Resteasy was being upgraded to 3.0.0 because of jOOQ.&lt;/p&gt;

&lt;p&gt;If it was only a matter of waiting for everyone to provide a Jakarta EE compatible version, it could possibly be workable, although it would take years and some companies don't have incentives in such upgrades (anyone knows when &lt;del&gt;Guice, or&lt;/del&gt; Dagger, will move to &lt;code&gt;jakarta.inject&lt;/code&gt;? fortunately I don't use anything that &lt;em&gt;depends&lt;/em&gt; on Dependency Injection, so I can live very well with &lt;code&gt;javax.inject&lt;/code&gt; alongside everything &lt;code&gt;jakarta.*&lt;/code&gt;, and that's indeed what I'm currently doing. &lt;strong&gt;UPDATE(2023-05-15):&lt;/strong&gt; Guice made the switch in version 7, so I can finally move on).&lt;/p&gt;

&lt;p&gt;No, what I had totally forgotten actually were reference implementations, and specifically (in my case) for Mail. Many EE APIs are frameworks (Servlets, REST) where you &lt;strong&gt;have&lt;/strong&gt; to pick one flavor and one implementation anyway: you wouldn't use Java EE Servlets within a Jakarta EE Servlet container, just like you wouldn't use Vert.x handlers in such container either, or a Spring Web resource within Resteasy or Jersey, and you wouldn't use Resteasy and Jersey at the same time either. But Mail is different, it's a library that you use to send mails, and because it knows how to parse multipart content, it can be used transitively by other libraries that you'll depend on (honestly, I don't think that will be the case for me, fortunately).&lt;/p&gt;

&lt;p&gt;So you may want or need to use both JavaMail and Jakarta Mail at the same time. And &lt;a href="https://github.com/eclipse-ee4j/mail/issues/527" rel="noopener noreferrer"&gt;it happens&lt;/a&gt; that this is not possible, because Jakarta EE decided to keep using the &lt;code&gt;com.sun.mail.*&lt;/code&gt; package names while migrating to Jakarta EE 9 APIs. Note that those exact same class names are actually published at separate Maven coordinates, similar to the Java EE 8 vs. Jakarta EE 8, but this time it extends to the whole Jakarta EE irrespective of the version (and to make the matter worse, they changed the Maven coordinates again for Jakarta EE 10: from &lt;code&gt;com.sun.mail:javax.mail&lt;/code&gt; to &lt;code&gt;com.sun.mail:jakarta.mail&lt;/code&gt; to &lt;code&gt;org.eclipse.angus:jakarta.mail&lt;/code&gt; ; oh, and they reset the versioning scheme, so you also have to somehow guess that Angus Mail 1.0.0 is Jakarta Mail 2.1)&lt;/p&gt;

&lt;p&gt;It's as if everything was deliberately made to make the migration as painful as possible, and specifically make it impossible (or at the very least make zero effort to make it possible) to have both Java EE and Jakarta EE in the same project, for no technical reason (besides we'll only have to search/replace &lt;code&gt;javax.&lt;/code&gt; with &lt;code&gt;jakarta.&lt;/code&gt;, and nothing else, which you'll convene is rather weak an argument).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fkabofxhvj47wqjs9ey99.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fkabofxhvj47wqjs9ey99.png" alt="Lieutenant Columbo saying “Just one more thing”"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another thing I had totally forgotten, was Java EE's &lt;em&gt;tradition&lt;/em&gt; to publish API jars that are only good to compile against, and then reference implementations that &lt;strong&gt;also&lt;/strong&gt; contain the API classes, but this time with actual code in the methods (this is also why you have EE vendor flavors of them by the way, because they have to change one constant somewhere to point to their actual implementation class).&lt;/p&gt;

&lt;p&gt;This means that &lt;code&gt;com.sun.mail:javax.mail&lt;/code&gt; contains the same classes as &lt;code&gt;javax.mail:javax.mail-api&lt;/code&gt;. And because they apparently always have to complicate things, this JAR is also a &lt;em&gt;bundle&lt;/em&gt; of &lt;code&gt;mailapi&lt;/code&gt; (that still also contains the API classes), &lt;code&gt;smtp&lt;/code&gt;, &lt;code&gt;imap&lt;/code&gt; and &lt;code&gt;pop3&lt;/code&gt;. Jakarta EE 9 followed the exact same layout, except for the &lt;code&gt;javax&lt;/code&gt; to &lt;code&gt;jakarta&lt;/code&gt; renaming (but keeping the &lt;code&gt;com.sun.mail&lt;/code&gt; package names though, remember?) Angus Mail (the Jakarta EE 10 reference implementation) doesn't depart from that tradition and also provides &lt;code&gt;org.eclipse.angus:jakarta.mail&lt;/code&gt; as a &lt;em&gt;bundle&lt;/em&gt; of both &lt;code&gt;org.eclipse.angus:angus-mail&lt;/code&gt; and &lt;code&gt;jakarta.mail:jakarta.mail-api&lt;/code&gt; (but this time it seems like &lt;code&gt;jakarta.mail:jakarta.mail-api&lt;/code&gt; contains &lt;em&gt;normal&lt;/em&gt; classes, not a version stripped out of the methods' code to keep only the ABI), and &lt;code&gt;org.eclipse.angus:angus-mail&lt;/code&gt; is a &lt;em&gt;bundle&lt;/em&gt; of &lt;code&gt;angus-core&lt;/code&gt;, &lt;code&gt;image&lt;/code&gt;, &lt;code&gt;smtp&lt;/code&gt;, &lt;code&gt;pop3&lt;/code&gt;, and &lt;code&gt;logging-mailhandler&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It's also been a &lt;em&gt;tradition&lt;/em&gt; to have JARs bundling &lt;strong&gt;all&lt;/strong&gt; the EE APIs together: &lt;code&gt;javax.javaee-web-api&lt;/code&gt; and &lt;code&gt;javax:javaee-api&lt;/code&gt;, respectively &lt;code&gt;jakarta.platform:jakartaee-web-api&lt;/code&gt; and &lt;code&gt;jakarta.platform:jakartaee-api&lt;/code&gt; (fortunately, they now also publish a &lt;code&gt;jakarta.platform:jakartaee-bom&lt;/code&gt; that simply &lt;em&gt;references&lt;/em&gt; the other Maven artifacts rather than &lt;em&gt;bundling&lt;/em&gt; them in a fat jar).&lt;/p&gt;

&lt;p&gt;Don't get me wrong, it's OK to build such JARs for people who don't use Maven or Maven-compatible dependency resolvers ; what is &lt;strong&gt;not&lt;/strong&gt; OK is to publish them to the Central Repository with their &lt;strong&gt;own&lt;/strong&gt; Maven coordinates (did you keep count of how many JARs contained &lt;code&gt;javax.mail.*&lt;/code&gt; or &lt;code&gt;jakarta.mail.*&lt;/code&gt; classes and how many contained &lt;code&gt;com.sun.mail.*&lt;/code&gt; classes?)&lt;/p&gt;

&lt;h2&gt;
  
  
  OK, so it's indeed way worse, but we have Gradle our savior, right?
&lt;/h2&gt;

&lt;p&gt;Gradle can help indeed: we can teach it to fail the build if we ever have conflicting JARs in our classpaths, but there are cases that don't have a clean solution besides picking one side and &lt;em&gt;rewriting&lt;/em&gt; everything to either &lt;code&gt;javax&lt;/code&gt; or &lt;code&gt;jakarta&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Detecting more conflicts
&lt;/h3&gt;

&lt;p&gt;Using the same kind of component metadata rules as in our previous installment, we'd be able to teach Gradle that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;javax.mail:javax.mail-api&lt;/code&gt;, &lt;code&gt;com.sun.mail:javax.mail&lt;/code&gt;, and &lt;code&gt;com.sun.mail:mailapi&lt;/code&gt; (in version 1) are incompatible as they all contain &lt;code&gt;javax.mail.*&lt;/code&gt; classes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;com.sun.mail:javax.mail&lt;/code&gt; is incompatible with &lt;code&gt;com.sun.mail:mailapi&lt;/code&gt;, &lt;code&gt;com.sun.mail:smtp&lt;/code&gt;, &lt;code&gt;com.sun.mail:imap&lt;/code&gt;, and &lt;code&gt;com.sun.mail:pop3&lt;/code&gt; as it's a bundle of those 4 JARs (it provides all the same capabilities than each one of them taken individually), same for &lt;code&gt;com.sun.mail:jakarta.mail&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;jakarta.mail:jakarta.mail-api&lt;/code&gt;, &lt;code&gt;com.sun.mail:jakarta.mail&lt;/code&gt;, &lt;code&gt;com.sun.mail:mailapi&lt;/code&gt; (in version 2), and &lt;code&gt;org.eclipse.angus:jakarta.mail&lt;/code&gt; are incompatible as they all contain &lt;code&gt;jakarta.mail.*&lt;/code&gt; classes (and note that Angus Mail doesn't use the same versioning scheme, because it wasn't complicated enough)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;org.eclipse.angus:jakarta.mail&lt;/code&gt; and &lt;code&gt;org.eclipse.angus:angus-mail&lt;/code&gt; are incompatible with their subsets&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;org.eclipse.angus&lt;/code&gt; artifacts are incompatible with their &lt;code&gt;com.sun.mail&lt;/code&gt; counterparts (and again beware of the new versioning scheme)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;com.sun.mail&lt;/code&gt; artifacts in version 1 shouldn't be upgraded to version 2&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(and I hope I haven't missed more cases‼)&lt;/p&gt;

&lt;h3&gt;
  
  
  Rewriting things?
&lt;/h3&gt;

&lt;p&gt;There are tools to rewrite JARs and classes, the most complete probably being &lt;a href="https://github.com/eclipse/transformer" rel="noopener noreferrer"&gt;Eclipse Transformer&lt;/a&gt;, that is apparently used by the Payara application server to rewrite EARs dynamically at deployment time.&lt;/p&gt;

&lt;p&gt;There's a &lt;a href="https://github.com/hibernate/jakarta-transformer-plugin/" rel="noopener noreferrer"&gt;Gradle plugin&lt;/a&gt; by Hibernate, but it's not much documented and it looks like Hibernate didn't even use it in their migration. The plugin seems to be tailored to creating JARs to be deployed (historically, the &lt;code&gt;*-jakarta&lt;/code&gt; Hibernate JARS, even though there were made without the plugin as far as I can tell), not for rewriting those dependencies that you might be using.&lt;/p&gt;

&lt;p&gt;To rewrite your dependencies, you could theoretically use an &lt;a href="https://docs.gradle.org/current/userguide/artifact_transforms.html" rel="noopener noreferrer"&gt;artifact transform&lt;/a&gt;. Registering it would probably require either identifying the dependencies that need rewriting (by way of &lt;em&gt;attributes&lt;/em&gt;), or apply it to each and every dependency (by adding said attribute to all variants of all components). That mess was decided years ago, we should be passed the point where someone already packaged it so you only have to apply one Gradle plugin and it Just Works™, but the community seems to have decided that we should all suffer this mess and wait for every library you depend on to have migrated, and in the mean time be locked with older versions, possibly unmaintained, of your other dependencies.&lt;/p&gt;

&lt;p&gt;Note that this rewriting wouldn't magically solve all your problems: you'd still have to have capabilities rule to prevent having several components with different Maven coordinates provide the same package names (including after the rewriting), but this time maybe resolve the conflicts automatically to pick the highest Jakarta EE version.&lt;/p&gt;

&lt;h2&gt;
  
  
  So what now?
&lt;/h2&gt;

&lt;p&gt;Honestly, I'm fed up.&lt;/p&gt;

&lt;del&gt;Maybe I'll try to make a plugin with all these rules (or contribute them to Jendrik Johannes' [Java Ecosystem Capabilities Gradle Plugin](https://github.com/jjohannes/java-ecosystem-capabilities)) so at least I can verify that I don't have issues. If anyone would like to help create a list of all the conflicts (including the vendor libraries), get in touch (but I don't promise anything).&lt;/del&gt;

&lt;p&gt;&lt;strong&gt;UPDATE(2022-11-18):&lt;/strong&gt; the &lt;a href="https://github.com/gradlex-org/java-ecosystem-capabilities" rel="noopener noreferrer"&gt;GradleX plugin&lt;/a&gt; now has most of those rules, at least for the official Java EE/Jakarta EE artifacts, i.e. not the Jetty, Tomcat or Glassfish flavors. See also &lt;a href="https://github.com/gradlex-org/java-ecosystem-capabilities/issues/6#issuecomment-1311312175" rel="noopener noreferrer"&gt;this comment&lt;/a&gt; for current limitations; specifically it won't downgrade Jakarta EE 8 to Java EE 8, so Jakarta EE 9 dependencies might upgrade them and break things at runtime.&lt;/p&gt;

&lt;p&gt;But for the rest, the best thing to do is probably to poke at project maintainers so they do the upgrade and/or provide parallel flavors (possibly helped by the Eclipse Transformer, and by yourself: please don't be assholes with open source maintainers, lend them a hand or sponsor them).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPDATE(2022-11-18):&lt;/strong&gt; Spring Framework 6 has been released that uses Jakarta EE 9 as a baseline. This will undoubtedly drive adoption of the &lt;code&gt;jakarta.*&lt;/code&gt; namespace, but not all projects have an interest in such combination (e.g. &lt;del&gt;Guice [&lt;a href="https://github.com/google/guice/issues/1383" rel="noopener noreferrer"&gt;tracking issue&lt;/a&gt;] and&lt;/del&gt; Dagger [&lt;a href="https://github.com/google/dagger/issues/2058" rel="noopener noreferrer"&gt;tracking issue&lt;/a&gt;] will likely stay with &lt;code&gt;javax.inject&lt;/code&gt; for a good while&lt;del&gt;, and Guice Servlets with &lt;code&gt;javax.servlet&lt;/code&gt; [&lt;a href="https://github.com/google/guice/issues/1490" rel="noopener noreferrer"&gt;tracking issue&lt;/a&gt;]&lt;/del&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPDATE(2023-05-15):&lt;/strong&gt; Guice made the switch to &lt;code&gt;jakarta.inject&lt;/code&gt;, &lt;code&gt;jakarta.servlet&lt;/code&gt;, and &lt;code&gt;jakarta.persistence&lt;/code&gt; in &lt;a href="https://github.com/google/guice/wiki/Guice700" rel="noopener noreferrer"&gt;version 7&lt;/a&gt;, and simultaneously published &lt;a href="https://github.com/google/guice/wiki/Guice600" rel="noopener noreferrer"&gt;version 6&lt;/a&gt; that's still on &lt;code&gt;javax&lt;/code&gt; but also supports &lt;code&gt;jakarta.inject&lt;/code&gt;, to help in the transition. Note that to support both namespaces, Guice 6 must depend on the &lt;code&gt;javax.inject:javax.inject:1&lt;/code&gt; artifact, which makes it incompatible with JPMS.&lt;/p&gt;

&lt;p&gt;And maybe in the future I won't use "standard APIs" as often as I used to: I'd rather have libraries that don't know how to talk to each other, and write some glue code, than libraries you cannot even put in the same classpath. So maybe I'll try alternatives to Servlets and/or Jakarta RS, trying to minimize my dependency on them through clear segregation (already what I'm doing mostly, where the Jakarta RS endpoints only &lt;em&gt;translate&lt;/em&gt; the HTTP request to a business-oriented service, and translate the result back to an HTTP response), such that rewriting the Web layer/adapter would indeed be costly, but entirely doable. I'm glad I never actually tried to use &lt;code&gt;javax.json&lt;/code&gt; for instance, similar to how I already ditched &lt;code&gt;javax.ws.rs.client&lt;/code&gt; for OkHttp a few years ago.&lt;/p&gt;

&lt;p&gt;Theoretically, I could also embrace the Java Module System (JPMS), as it's good to detect duplicate packages and missing dependencies (e.g. when Jakarta EE 8 is upgraded to Jakarta EE 9, assuming a change in module name), but it's a whole other mess as it adds yet another naming scheme. Just as an example, the JBoss version of Jakarta RS 3.0 is still using the &lt;code&gt;java.ws.rs&lt;/code&gt; module name rather than &lt;code&gt;jakarta.ws.rs&lt;/code&gt;, and Jetty's version of Jakarta Servlet 5.0 is using &lt;code&gt;jetty.servlet.api&lt;/code&gt; rather than &lt;code&gt;jakarta.servlet&lt;/code&gt;, Jakarta EE 8 dependencies themselves have sometimes switched module name during patch releases; this means that you cannot just swap one JAR for another as the JVM will then complain that some &lt;code&gt;requires&lt;/code&gt; is not fulfilled. Not to mention that many JARs still aren't compatible with JPMS, with not even an &lt;code&gt;Automatic-Module-Name&lt;/code&gt;: Resteasy 6 and Sentry-Java for instance, and some Java EE dependencies too (e.g. &lt;code&gt;javax.inject:javax.inject&lt;/code&gt;) so downgrading Jakarta EE 8 to Java EE 8 is not without problems either. That would probably deserve a third post, but for my sanity I'd rather wait a couple years 😁&lt;/p&gt;

</description>
      <category>java</category>
      <category>javax</category>
      <category>jakarta</category>
      <category>gradle</category>
    </item>
    <item>
      <title>The Javax to Jakarta mess, and a Gradle solution</title>
      <dc:creator>Thomas Broyer</dc:creator>
      <pubDate>Mon, 18 Apr 2022 19:09:22 +0000</pubDate>
      <link>https://dev.to/tbroyer/the-javax-jakarta-mess-and-a-gradle-solution-3c44</link>
      <guid>https://dev.to/tbroyer/the-javax-jakarta-mess-and-a-gradle-solution-3c44</guid>
      <description>&lt;p&gt;&lt;strong&gt;EDIT(2022-11-18):&lt;/strong&gt; There's a new Gradle plugin to help us. Continue reading for details.&lt;/p&gt;

&lt;p&gt;Nearly five years ago, Oracle was preparing the release of Java EE 8 and &lt;a href="https://blogs.oracle.com/theaquarium/post/opening-up-java-ee"&gt;announced&lt;/a&gt; that it would move it to an open source foundation. Less a month later, they &lt;a href="https://blogs.oracle.com/theaquarium/post/opening-up-java-ee-an-update"&gt;announced&lt;/a&gt; they selected the Eclipse Foundation for that work. Two years later, Jakarta EE 8 was &lt;a href="https://jakarta.ee/news/jakarta-ee-8-released/"&gt;released&lt;/a&gt; as fully compatible version of Java EE 8. The only thing that changed in that period was about migrating the process to the Eclipse Foundation and Jakarta EE Working Group.&lt;/p&gt;

&lt;p&gt;According to &lt;a href="https://blogs.oracle.com/javamagazine/post/transition-from-java-ee-to-jakarta-ee"&gt;this article&lt;/a&gt;, the source code was exactly the same, except for some additional commits maybe because Oracle transfered the head of the &lt;em&gt;master&lt;/em&gt; branches of Git repositories, and the artifacts got released twice as milestones of the transfer process: first proving that the code could be built, then a less technical but more procedural release where the Java EE name was replaced by Jakarta EE in javadocs (of course this also means different terms and conditions).&lt;/p&gt;

&lt;p&gt;Then fifty months after that, Jakarta EE 9 was &lt;a href="https://jakarta.ee/news/jakarta-ee-9-released/"&gt;released&lt;/a&gt; with only two major changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the package names were all migrated from &lt;code&gt;javax.*&lt;/code&gt; to &lt;code&gt;jakarta.*&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;some older specifications were pruned&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  But you talked about a mess‽
&lt;/h2&gt;

&lt;p&gt;Yes, Oracle being Oracle, they transfered the technology and documentation, but not the name and trademark. Indeed, Java EE was renamed to Jakarta EE. But that's not all, they also prohibited any modification to the &lt;code&gt;javax.*&lt;/code&gt; packages, so everything would eventually be moved new packages. The Eclipse Foundation &lt;a href="https://eclipse-foundation.blog/2019/05/03/jakarta-ee-java-trademarks/"&gt;presented it&lt;/a&gt; as what Eclipse and Oracle had agreed on but let's not be fooled by that PR wording: what would you expect from the company that almost ruined our whole industry with the trial against Google over Android?&lt;/p&gt;

&lt;p&gt;That's only part of the problem though. We could have had &lt;code&gt;javax.*&lt;/code&gt; artifacts (I'm talking about Maven coordinates here) using the &lt;code&gt;javax.*&lt;/code&gt; package names, and &lt;code&gt;jakarta.*&lt;/code&gt; artifacts using the &lt;code&gt;jakarta.*&lt;/code&gt; package names, and while this big breaking change would have had a years-long impact, it would have been somewhat manageable (we're right into it now actually, with many projects maintaining two branches, one for each package namespace).&lt;/p&gt;

&lt;p&gt;But the Jakarta Working Group decided to publish Jakarta EE 8 under the same Maven coordinates as what they expected to publish later Jakarta EE versions. I have no idea if they were somehow &lt;em&gt;forced&lt;/em&gt; to publish those at all (or could have possibly kept them into their own repository), but they could have at least used specific Maven coordinates, as they already knew at that point that this would happen: Oracle &lt;em&gt;froze&lt;/em&gt; the &lt;code&gt;javax.*&lt;/code&gt; package name in May 2019, while Jakarta EE 8 came out four months later in September 2019. I have no idea how much Jakarta Working Group members were aware of &lt;a href="https://jakewharton.com/java-interoperability-policy-for-major-version-updates/"&gt;this decision&lt;/a&gt; by Square, Inc. to version their Maven coordinates and package names when releasing versions with major breaking changes, back in December 2015 (but the problem itself was nothing new); but the decision to publish &lt;code&gt;javax.*&lt;/code&gt; and later &lt;code&gt;jakarta.*&lt;/code&gt; packages under the same Maven coordinates was hugely misguided, and possibly the worst mistake in all this story.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does this mean in practice?
&lt;/h2&gt;

&lt;p&gt;First, when there was only Jakarta EE 8, you could have had (transitive) dependencies on both Java EE 8 (or earlier) and Jakarta EE 8. This would cause duplicates of the &lt;code&gt;javax.*&lt;/code&gt; classes in the classpath, because dependency managers aren't told that those artifacts are actually the same but renamed. If you had an older Java EE artifact on the classpath, it could also &lt;em&gt;shadow&lt;/em&gt; the newer Jakarta EE, causing breakages at compile-time or, worse, at runtime.&lt;/p&gt;

&lt;p&gt;But now that there's also Jakarta EE 9 (and 9.1, and very soon Jakarta EE 10), you could have Jakarta EE 8 artifacts being &lt;em&gt;upgraded&lt;/em&gt; to Jakarta EE 9 despite being a completely incompatible API.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://www.eclipse.org/community/eclipse_newsletter/2020/november/1.php"&gt;their newsletter&lt;/a&gt; just before the Jakarta EE 9 release, Eclipse acknowledged the practical issues it caused, inviting people to actually depend on Java EE 8 artifacts rather than Jakarta EE 8 ones, but this came &lt;strong&gt;way&lt;/strong&gt; too late, the harm had been done already.&lt;/p&gt;

&lt;p&gt;In retrospect, we could say that nobody should have dependended upon Jakarta EE 8 artifacts, or they should have used version ranges to exclude the next major version (but version ranges are bad for build reproducibility, unless you use a dependency manager that somehow supports version locking).&lt;/p&gt;

&lt;h2&gt;
  
  
  Oh wow! Ok, but you hinted at a Gradle solution?
&lt;/h2&gt;

&lt;p&gt;Yes, this is where Gradle really shines compared to many other dependency managers: it lets you hook into the dependency resolution process and &lt;em&gt;fix&lt;/em&gt; many things.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPDATE(2022-11-18):&lt;/strong&gt; there's &lt;a href="https://github.com/gradlex-org/java-ecosystem-capabilities"&gt;a new plugin&lt;/a&gt; in town that applies &lt;strong&gt;some&lt;/strong&gt; of the solutions mentionned below (see &lt;a href="https://github.com/gradlex-org/java-ecosystem-capabilities/issues/6#issuecomment-1311312175"&gt;this comment&lt;/a&gt; for the limitations; specifically it won't downgrade Jakarta EE 8 to Java EE 8, so Jakarta EE 9 dependencies might upgrade them and break things at runtime)&lt;/p&gt;

&lt;p&gt;With Maven for instance, you could use the &lt;a href="https://maven.apache.org/enforcer/maven-enforcer-plugin/"&gt;Maven Enforcer Plugin&lt;/a&gt; with &lt;a href="https://www.mojohaus.org/extra-enforcer-rules/banDuplicateClasses.html,"&gt;Mojohaus "ban duplicate classes" rule&lt;/a&gt; to detect the Java EE vs Jakarta EE 8 issue, or the &lt;a href="https://maven.apache.org/enforcer/enforcer-rules/dependencyConvergence.html"&gt;built-in "dependency convergence" rule&lt;/a&gt; and you would have to resolve it yourself through &lt;a href="https://maven.apache.org/pom.html#Exclusions"&gt;dependency exclusions&lt;/a&gt; everywhere needed, (and because the "dependency convergence" rule isn't configurable, you cannot enable it only for the Jakarta EE dependencies, so it has a huge impact). By the way, if you look at how the "dependency convergence" rule is implemented, you'll see that it will actually resolve the dependency once again so it can get to the dependency details, I don't think you can peek into, and influence the dependency resolution process itself.&lt;/p&gt;

&lt;p&gt;With Gradle, we can do two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;declare that Java EE and Jakarta EE 8 are actually the same thing under a different name&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;fix&lt;/em&gt; third-parties' dependencies on Jakarta EE 8 to reject any upgrade to Jakarta EE 9+ (Gradle also has the equivalent to Maven's "dependency convergence" rule through &lt;a href="https://docs.gradle.org/current/userguide/resolution_strategy_tuning.html#fail-version-conflict"&gt;&lt;code&gt;failOnVersionConflict()&lt;/code&gt;&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This would fail the build the same way as with Maven, but Gradle also allows us to automatically fix those: because Jakarta EE 8 artifacts are fully compatible with Java EE 8 ones, we could rewrite third-parties' dependencies to actually use Java EE 8 rather than Jakarta EE 8.&lt;/p&gt;

&lt;h3&gt;
  
  
  Declaring that Java EE and Jakarta EE 8 are equivalent
&lt;/h3&gt;

&lt;p&gt;To declare that Jakarta EE 8 replaced Java EE, we could use a &lt;a href="https://docs.gradle.org/current/userguide/resolution_rules.html#sec:module_replacement"&gt;module replacement rule&lt;/a&gt;, but this would also say that Jakarta EE 9 replaced Java EE, which is not actually true: there's no real problem having both Java EE 8 (&lt;code&gt;javax.*&lt;/code&gt; package) and Jakarta EE 9 (&lt;code&gt;jakarta.*&lt;/code&gt; package) in the same classpath.&lt;/p&gt;

&lt;p&gt;So we'd rather &lt;a href="https://docs.gradle.org/current/userguide/component_metadata_rules.html#adding_missing_capabilities_to_detect_conflicts"&gt;declare&lt;/a&gt; that Jakarta EE 8 artifacts (and only those) provide the same &lt;a href="https://docs.gradle.org/current/userguide/dependency_capability_conflict.html"&gt;&lt;em&gt;capabilities&lt;/em&gt;&lt;/a&gt; as their Java EE counterparts.&lt;/p&gt;

&lt;p&gt;For XML Binding for instance (which also changed name from JAXB), this would look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;components&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;withModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"jakarta.xml.bind:jakarta.xml.bind-api"&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="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2."&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;allVariants&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;withCapabilities&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nf"&gt;addCapability&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"javax.xml.bind"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"jaxb-api"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that rule,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you can have &lt;code&gt;javax.xml.bind:jaxb-api&lt;/code&gt; in any version (Java EE) and &lt;code&gt;jakarta.xml.bind:jakarta-xml.bind-api:3.0.0&lt;/code&gt; or a later version (Jakarta EE 9+)) without error, but&lt;/li&gt;
&lt;li&gt;you cannot have &lt;code&gt;javax.xml.bind:jaxb-api&lt;/code&gt; and &lt;code&gt;jakarta.xml.bind:jakarta-xml.bind-api:2.3.3&lt;/code&gt; (Jakarta EE 8).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we have such a conflict, Gradle will allow us to resolve it using a rule too, rather than having to play with exclusions which are a PITA to maintain in the long run. Let's first continue to detect the problems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reject upgrades of Jakarta EE 8 to Jakarta EE 9+
&lt;/h3&gt;

&lt;p&gt;Rejecting such upgrades, within the same coordinates, is not as easy.&lt;/p&gt;

&lt;p&gt;As we've seen above, we could retrospectively say that the dependencies should have originally been declared to reject them, but that's never the case in practice. Fortunately, Gradle allows us to &lt;a href="https://docs.gradle.org/current/userguide/component_metadata_rules.html#fixing_wrong_dependency_details"&gt;&lt;em&gt;fix&lt;/em&gt; those declarations&lt;/a&gt; at resolution time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;components&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;all&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;allVariants&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;withDependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;dep&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;find&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"jakarta.xml.bind"&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
                    &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"jakarta.xml.bind-api"&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
                    &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;versionConstraint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includesMajor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2"&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="n"&gt;dep&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;dep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;version&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"[3,)"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;includesMajor&lt;/code&gt; here would be a Kotlin extension function doing the check on the version, which has to deal with Gradle's &lt;a href="https://docs.gradle.org/current/userguide/rich_versions.html"&gt;&lt;em&gt;rich versions&lt;/em&gt;&lt;/a&gt; including version ranges for instance. The simplest implementation would only look at the &lt;em&gt;required version&lt;/em&gt;, which is what a simple version in a Maven POM would map to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nc"&gt;VersionConstraint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includesMajor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;major&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;requiredVersion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"${major}."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Resolving the Java EE / Jakarta EE 8 conflicts
&lt;/h3&gt;

&lt;p&gt;We've used a &lt;em&gt;capability&lt;/em&gt; to make them &lt;em&gt;incompatible&lt;/em&gt; with one another, but this will &lt;em&gt;only&lt;/em&gt; fail the build if such a thing arise. We then have to use a &lt;a href="https://docs.gradle.org/current/userguide/dependency_capability_conflict.html#sub:selecting-between-candidates"&gt;capabilities resolution rule&lt;/a&gt; to select between them.&lt;/p&gt;

&lt;p&gt;Because we declared the capability only on Jakarta EE 8, we can safely use &lt;code&gt;selectHighestVersion()&lt;/code&gt; to pick the Jakarta EE 8.&lt;/p&gt;

&lt;p&gt;Why safely? Because this is evaluated after versions are mediated. What this means is that if you have all three of &lt;code&gt;javax.xml.bind:jaxb-api:2.3.1&lt;/code&gt; (Java EE 8), &lt;code&gt;jakarta.xml.bind:jakarta.xml.bind-api:2.3.3&lt;/code&gt; (Jakarta EE 8), and &lt;code&gt;jakarta.xml.bind:jakarta.xml.bind-api:3.0.0&lt;/code&gt; (Jakarta EE 9), then Gradle will first upgrade the Jakarta EE dependency to Jakarta EE 9, which will &lt;em&gt;remove&lt;/em&gt; Jakarta EE 8 from the equation, and at the same time the capability conflict, leaving the Java EE 8 and Jakarta EE 9. In this case, because Java EE 8 and Jakarta EE 8 are fully compatible, this is not a problem at all; it would be though if we had a dependency on an older version of Java EE, as this could break compatibility for the library that depends on Jakarta EE 8. Anyway, all of this won't happen because we &lt;em&gt;also&lt;/em&gt; made it so that the Jakarta EE 8 dependency won't be upgraded to Jakarta EE 9 or later.&lt;/p&gt;

&lt;p&gt;To look at the whole picture, if you only had the Java EE (any version) and Jakarta EE 8 dependencies, then the conflict would be on the &lt;code&gt;javax.xml.bind:jaxb-api:2.3.1&lt;/code&gt; and &lt;code&gt;javax.xml.bind:jaxb-api:2.3.3&lt;/code&gt; capabilities (Gradle capabilities follow the same naming rules as Maven coordinates, with a group, a name and a version) so with &lt;code&gt;selectHighestVersion()&lt;/code&gt; the &lt;code&gt;2.3.3&lt;/code&gt; capability would be selected, hence the Jakarta EE artifact. In other words, the Java EE 8 would be upgraded to Jakarta EE 8. This works because they kept Jakarta EE versions increasing after Java EE ones, and we declared the capability using that version.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resolving the Jakarta EE 8 / Jakarta EE 9+ conflicts
&lt;/h3&gt;

&lt;p&gt;Because we know that Java EE 8 and Jakarta EE 8 are fully compatible with one another, the solution here will be to actually &lt;em&gt;downgrade&lt;/em&gt; Jakarta EE 8 to Java EE 8.&lt;/p&gt;

&lt;p&gt;We'll thus replace the rule we added above that would reject the upgrade to Jakarta EE 9+, with a similar one that actually downgrades to Java EE 8.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;components&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;all&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;allVariants&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;withDependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;found&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;removeIf&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"jakarta.xml.bind"&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
                    &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"jakarta.xml.bind-api"&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
                    &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;versionConstraint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includesMajor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2"&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="n"&gt;found&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"javax.xml.bind:jaxb-api:2.3.1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;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;Note that we might want to actually conserve the attributes when replacing the dependency that way (or maybe not, I haven't thought about it much yet).&lt;/p&gt;

&lt;p&gt;Such a rule actually trumps all our previous attempts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;if we replace Jakarta EE 8 with Java EE 8, we no longer have to detect when both are present, even less so resolve any such conflict; and&lt;/li&gt;
&lt;li&gt;because Jakarta EE 8 has been replaced, we no longer have to reject upgrading it to Jakarta EE 9 either.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can still keep those rules &lt;em&gt;just in case&lt;/em&gt;, e.g. if Jakarta EE 8 is added as a direct dependency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem solved then?
&lt;/h2&gt;

&lt;p&gt;Well, for some definition of solved, yes.&lt;/p&gt;

&lt;p&gt;Ideally, one would have to go through all the Java EE and Jakarta EE dependencies (fortunately, they can all be found in &lt;code&gt;javax:javaee-api&lt;/code&gt; and &lt;code&gt;jakarta.platform:jakartaee-bom&lt;/code&gt; POMs) and package all those rules into a plugin.&lt;/p&gt;

&lt;p&gt;To that, one would have to add all the artifacts from EE vendors (e.g. for JAX-RS alone: &lt;code&gt;javax:javaee-api&lt;/code&gt;, &lt;code&gt;javax:javaee-web-api&lt;/code&gt;, &lt;code&gt;org.jboss.spec.javax.ws.rs:jboss-jaxrs-api_2.1_spec&lt;/code&gt;, &lt;code&gt;org.jboss.resteasy:jaxrs-api&lt;/code&gt;, &lt;code&gt;org.apache.servicemix.specs:org.apache.servicemix.specs.jaxrs-api-2.1&lt;/code&gt;, &lt;code&gt;org.apache.aries.spec:org.apache.aries.javax.jax.rs-api&lt;/code&gt;, &lt;code&gt;org.apache.geronimo.specs:geronimo-jaxrs_2.1_spec&lt;/code&gt;, &lt;code&gt;org.apache.tomee:javaee-api&lt;/code&gt;, and additionally for Jakarta RS: &lt;code&gt;jakarta.platform:jakartaee-api&lt;/code&gt;, &lt;code&gt;jakarta.platform:jakartaee-web-api&lt;/code&gt;, &lt;code&gt;org.jboss.spec.javax.ws.rs:jboss-jaxrs-api_3.0_spec&lt;/code&gt;, &lt;code&gt;org.apache.tomee:jakartaee-api&lt;/code&gt;, and I'm probably missing some) to declare them as providing the same Java EE or Jakarta EE capability (and note that versions do not match).&lt;/p&gt;

&lt;p&gt;Then there needs to be extensive testing with various combinations of Java EE 7, Java EE 8, Jakarta EE 8 and Jakarta EE 9, including the &lt;code&gt;includesMajor&lt;/code&gt; version check hinted above.&lt;/p&gt;

&lt;p&gt;And of course, because we tap into each and every component, looking at all their dependencies, this needs to be optimized so as to not slow down all your builds.&lt;/p&gt;

&lt;p&gt;Easier said than done.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Many thanks to Björn Kautler (Vampire) and Jendrik Johannes for the discussion and ideas.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>javax</category>
      <category>jakarta</category>
      <category>gradle</category>
    </item>
  </channel>
</rss>
