<?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: Luken</title>
    <description>The latest articles on DEV Community by Luken (@luken7).</description>
    <link>https://dev.to/luken7</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3471429%2F2f6e23ec-7b5d-4b36-b23f-2882b36aec0f.png</url>
      <title>DEV Community: Luken</title>
      <link>https://dev.to/luken7</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/luken7"/>
    <language>en</language>
    <item>
      <title>Wraplet vs Web Components</title>
      <dc:creator>Luken</dc:creator>
      <pubDate>Thu, 04 Jun 2026 11:30:03 +0000</pubDate>
      <link>https://dev.to/luken7/wraplet-vs-web-components-3045</link>
      <guid>https://dev.to/luken7/wraplet-vs-web-components-3045</guid>
      <description>&lt;p&gt;This post was originally published at &lt;a href="https://wraplet.dev/blog/wraplet-vs-web-components/" rel="noopener noreferrer"&gt;https://wraplet.dev/blog/wraplet-vs-web-components/&lt;/a&gt; where the code examples are interactive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Wraplet and Web Components aren't strangers. They share a lot of DNA, and the best way to see it is to look at the same widget written in both.&lt;/p&gt;

&lt;p&gt;Here is a simple "color toggle" as a Web Component:&lt;/p&gt;

&lt;h3&gt;
  
  
  The Web Components clicker
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;color-toggle&amp;gt;&amp;lt;strong&amp;gt;&lt;/span&gt;Click me!&lt;span class="nt"&gt;&amp;lt;/strong&amp;gt;&amp;lt;/color-toggle&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// A Web Component is registered as a brand-new HTML tag.&lt;/span&gt;
&lt;span class="c1"&gt;// The class IS the element - it extends HTMLElement directly.&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ColorToggle&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;// `connectedCallback` runs when the browser inserts the element&lt;/span&gt;
    &lt;span class="c1"&gt;// into the DOM. There is no explicit "initialize" - the browser&lt;/span&gt;
    &lt;span class="c1"&gt;// upgrades the tag for you.&lt;/span&gt;
    &lt;span class="nf"&gt;connectedCallback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleClick&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// `disconnectedCallback` runs when the browser removes the&lt;/span&gt;
    &lt;span class="c1"&gt;// element. You are responsible for unwiring everything you set&lt;/span&gt;
    &lt;span class="c1"&gt;// up in `connectedCallback`.&lt;/span&gt;
    &lt;span class="nf"&gt;disconnectedCallback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeEventListener&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleClick&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;handleClick&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="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&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;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;red&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="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&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="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// The element only becomes "alive" once we register the tag name.&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="s2"&gt;color-toggle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ColorToggle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Wraplet clicker
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;strong&amp;gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;data-js-clicker&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Click me!&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&amp;lt;/strong&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AbstractWraplet&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wraplet&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// A single class wrapping a real DOM node.&lt;/span&gt;
&lt;span class="c1"&gt;// No virtual DOM, no compiler, no hidden reactivity.&lt;/span&gt;
&lt;span class="c1"&gt;// What you read here is exactly what runs in the browser.&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Clicker&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;AbstractWraplet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nx"&gt;override&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;onInitialize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// The `nodeManager` is a small helper that ties listeners&lt;/span&gt;
        &lt;span class="c1"&gt;// to the wraplet lifecycle. They get cleaned up automatically&lt;/span&gt;
        &lt;span class="c1"&gt;// when the wraplet is destroyed - no leaks, no surprises.&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;nodeManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&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;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;red&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="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&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="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[data-js-clicker]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clicker&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;Clicker&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;clicker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wraplet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at them side by side and you'll notice they aren't that different:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A class per element.&lt;/strong&gt; Behavior lives in a class, not in a hook tree, a template, or a reactivity graph. What you see in the file is what runs in the browser.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No virtual DOM.&lt;/strong&gt; Neither one diffs anything or rewrites your source (well, except the TypeScript transpiler). The class touches the real DOM, with the real DOM APIs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lifecycle hooks.&lt;/strong&gt; Both expose a "this is where I start working" hook and a "this is where I tear down" hook. Custom elements call them &lt;code&gt;connectedCallback&lt;/code&gt; and &lt;code&gt;disconnectedCallback&lt;/code&gt;; wraplets call them &lt;code&gt;onInitialize&lt;/code&gt; and &lt;code&gt;onDestroy&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Framework-agnostic.&lt;/strong&gt; A Web Component lives in any page that can render its tag. A wraplet lives in any page that can run its script. Both play nicely with React, Vue, Angular, plain HTML, or any server-rendered template.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;At home in server-rendered HTML.&lt;/strong&gt; Neither asks you to rebuild your pages as a single-page app. They just add behavior to markup that already exists.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your mental model of frontend code is "DOM nodes with classes on them, not a render tree," both technologies will feel familiar.&lt;/p&gt;

&lt;h2&gt;
  
  
  So where's the actual difference?
&lt;/h2&gt;

&lt;p&gt;The similarities run out the moment you ask: what &lt;em&gt;is&lt;/em&gt; a component?&lt;/p&gt;

&lt;p&gt;A custom element &lt;strong&gt;is&lt;/strong&gt; an &lt;code&gt;HTMLElement&lt;/code&gt; subclass. The class and the element are the same object - &lt;code&gt;document.querySelector("color-toggle")&lt;/code&gt; returns the instance itself. A wraplet, by contrast, &lt;strong&gt;wraps&lt;/strong&gt; a node. The class lives in &lt;code&gt;this&lt;/code&gt;, the node lives in &lt;code&gt;this.node&lt;/code&gt;, and the two are bound together but remain separate. The HTML stays whatever it already was - a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;, a &lt;code&gt;&amp;lt;strong&amp;gt;&lt;/code&gt;, an &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; - and behavior is attached to it by selector.&lt;/p&gt;

&lt;p&gt;This single decision shapes everything that follows: composition, lifecycle, and dependency management all flow from it. We'll come back to those in the next section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lifecycle comparison
&lt;/h3&gt;

&lt;p&gt;Web Component's lifecycle is the same as the element's lifecycle.&lt;/p&gt;

&lt;p&gt;Wraplet's lifecycle is decoupled from the element's one. Wraplet can be initialized before the element is added to the DOM or destroyed before it is removed.&lt;/p&gt;

&lt;p&gt;Additionally, wraplet's lifecycle events can be chained together by the &lt;a href="https://wraplet.dev/docs/concepts/dependency-manager" rel="noopener noreferrer"&gt;DependencyManager&lt;/a&gt; making it possible to treat multiple interdependent wraplets, managing their own dependencies, as a single unit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dependency management, side by side
&lt;/h3&gt;

&lt;p&gt;Wraplet's dependency management is declarative. To see what that means in practice, compare the same "greeter" widget written both ways. First, the Web Component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;greeter-element&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;data-name&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Your name"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;data-submit&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Greet&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;data-output&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/greeter-element&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// A Web Component reaches into its children with `querySelector`&lt;/span&gt;
&lt;span class="c1"&gt;// and a manual cast. The compiler has no way to verify that the&lt;/span&gt;
&lt;span class="c1"&gt;// selector and the cast actually agree with the markup.&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GreeterElement&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;private&lt;/span&gt; &lt;span class="nx"&gt;nameInput&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLInputElement&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;submit&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLButtonElement&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;output&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;connectedCallback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nameInput&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;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[data-name]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;HTMLInputElement&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;submit&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;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[data-submit]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;HTMLButtonElement&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;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="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[data-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;as&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&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;submit&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleClick&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;disconnectedCallback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeEventListener&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleClick&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;handleClick&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="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nameInput&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stranger&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;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`Hello, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;!`&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="s2"&gt;greeter-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;GreeterElement&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The compiler has no way to verify that &lt;code&gt;[data-name]&lt;/code&gt; actually matches an &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;, or that the markup was rendered at all. Rename a selector in the HTML and nothing in the TypeScript complains - until a user clicks the button.&lt;/p&gt;

&lt;p&gt;Now the same widget as a wraplet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;data-js-greeter&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;data-js-greeter__name&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Your name"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;data-js-greeter__submit&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Greet&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;data-js-greeter__output&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;AbstractWraplet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;AbstractDependentWraplet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;DDM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;WrapletDependencyMap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wraplet&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Small leaf wraplets - each one wraps a single DOM node and&lt;/span&gt;
&lt;span class="c1"&gt;// exposes a tiny, typed API around it.&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NameInput&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;AbstractWraplet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLInputElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;getValue&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="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;node&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;SubmitButton&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;AbstractWraplet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLButtonElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;callback&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;void&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&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;nodeManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addListener&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="nf"&gt;callback&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;Output&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;AbstractWraplet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;value&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="k"&gt;void&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;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="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="c1"&gt;// A typed, declarative dependency map.&lt;/span&gt;
&lt;span class="c1"&gt;// `this.d.nameInput` will be a `NameInput` instance, not an&lt;/span&gt;
&lt;span class="c1"&gt;// `HTMLInputElement` you have to cast yourself.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;nameInput&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[data-js-greeter__name]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NameInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;multiple&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[data-js-greeter__submit]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SubmitButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;multiple&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[data-js-greeter__output]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;multiple&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;satisfies&lt;/span&gt; &lt;span class="nx"&gt;WrapletDependencyMap&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;Greeter&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;AbstractDependentWraplet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nx"&gt;override&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;onInitialize&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 querySelector, no casts. Renaming a key in the map&lt;/span&gt;
        &lt;span class="c1"&gt;// immediately surfaces every call site as a compile error.&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;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&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;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nameInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stranger&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;d&lt;/span&gt;&lt;span class="p"&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;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Hello, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;!`&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;const&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;HTMLElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[data-js-greeter]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;greeter&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;Greeter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DDM&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;map&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;greeter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wraplet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initialize&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;a href="https://wraplet.dev/docs/concepts/dependency-manager" rel="noopener noreferrer"&gt;dependency map&lt;/a&gt; declares each child once: a selector, a class, and a few flags. From that single source, TypeScript infers everything else. &lt;code&gt;this.d.nameInput&lt;/code&gt; is a &lt;code&gt;NameInput&lt;/code&gt; instance with its own methods, not an &lt;code&gt;HTMLInputElement&lt;/code&gt; you had to cast. Rename a key and every usage breaks loudly, in the compiler, before the page ever loads. Mis-type a child's API and you fail to compile.&lt;/p&gt;

&lt;p&gt;Web Components have no equivalent. Slots help with placement, not with typing. You can build a typed wrapper layer on top, but then you're recreating, by hand, what Wraplet gives you out of the box.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Side note&lt;/strong&gt;: You may have noticed that the Wraplet example is more verbose. The additional code is not a dead weight, though, so don't be discouraged by it. The dependency map and the contracts exposed by dependencies make code much more readable. Wraplet is all about what's great in OOP: encapsulation and contracts. If you follow the recommended practices, it will always be trivial to understand the component's structure.&lt;/p&gt;

&lt;p&gt;It's not strictly forced, though. You are free to implement &lt;strong&gt;impure&lt;/strong&gt; wraplets. These are the wraplets that reach out and interact with multiple nodes directly. You'll lose the clarity of dependency structure and contracts but reduce the code verbosity to the Web Component's level, while keeping some of the other wraplet's advantages. Like the ability of an impure wraplet to participate in a dependency tree of another wraplet or automatic listener cleanup.&lt;/p&gt;

&lt;p&gt;Ignoring the feature of dependency map is not recommended, though, because readability is difficult to overestimate, and wraplet is all about readability and long-term maintainability.&lt;/p&gt;

&lt;p&gt;You can think about Wraplet's verbosity this way: It's not a flashy Ferrari supposed to get out of fashion next week. It's a military truck of JavaScript frameworks. Made to be easy to maintain and reliable, first and foremost.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Listener cleanup
&lt;/h3&gt;

&lt;p&gt;In a Web Component, every listener you attached in &lt;code&gt;connectedCallback&lt;/code&gt; is your responsibility to remove in &lt;code&gt;disconnectedCallback&lt;/code&gt;. The compiler won't remind you, and forgetting is a classic source of leaks in long-lived pages.&lt;/p&gt;

&lt;p&gt;A wraplet extending &lt;code&gt;AbstractWraplet&lt;/code&gt; or &lt;code&gt;AbstractDependentWraplet&lt;/code&gt; attaches its listeners through &lt;code&gt;this.nodeManager.addListener(...)&lt;/code&gt;, which ties them to the wraplet's lifecycle. When the wraplet is destroyed, the &lt;a href="https://wraplet.dev/docs/concepts/node-manager" rel="noopener noreferrer"&gt;node manager&lt;/a&gt; removes them automatically. You only write cleanup code for things outside the framework's reach - third-party widgets, raw timers, external subscriptions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reactivity and attributes
&lt;/h3&gt;

&lt;p&gt;Web Components have a built-in path for attribute reactivity: &lt;code&gt;observedAttributes&lt;/code&gt; plus &lt;code&gt;attributeChangedCallback&lt;/code&gt;. It's small, native, and it's the canonical way for a custom element to react to its own attributes.&lt;/p&gt;

&lt;p&gt;Wraplet doesn't provide an attribute-based reactivity system. By default, state changes are expressed as method calls between wraplets, not as attribute mutations the framework observes.&lt;/p&gt;

&lt;p&gt;When it comes to interaction between a wraplet and a node, if you really want a reactivity system, it's possible to use a custom/external solution. Such a reactivity engine could be installed on wraplets that need it, through composition. Wraplet is not tied to any specific reactivity engine and doesn't plan to be. This is because it aims at providing solution only to the most fundamental issues and leaves the rest to the ecosystem.&lt;/p&gt;

&lt;p&gt;So if you specifically want "set an attribute, get a callback", Web Components do that out of the box, and Wraplet doesn't.&lt;/p&gt;

&lt;h3&gt;
  
  
  Shadow DOM vs the real DOM
&lt;/h3&gt;

&lt;p&gt;Web Components ship with Shadow DOM: an encapsulated subtree with its own scoped styles and its own slot-based composition. That is a real strength when you are publishing a widget into pages whose CSS you do not control - a chat widget on a customer's site, for instance.&lt;/p&gt;

&lt;p&gt;It is also a real cost when you &lt;em&gt;do&lt;/em&gt; control the page. Styling across the boundary requires explicit CSS custom properties or &lt;code&gt;::part&lt;/code&gt; selectors, debugging across the boundary is harder, and the slot system is its own small composition language you have to learn.&lt;/p&gt;

&lt;p&gt;Wraplets have no Shadow DOM. They work directly on the live tree, with the same CSS rules and the same &lt;code&gt;querySelector&lt;/code&gt; semantics as everything else on the page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problems and solutions
&lt;/h2&gt;

&lt;p&gt;The technical comparison above sketched the shape of each technology. Now let's go deeper into &lt;em&gt;why&lt;/em&gt; Wraplet exists in the first place - where these differences really came from. Most of them came from the simple fact that Web Component is an element, not a behavior.&lt;/p&gt;

&lt;p&gt;At its core Wraplet decouples the behavior from the DOM, which solves quite a few problems, as we'll show here.&lt;/p&gt;

&lt;h3&gt;
  
  
  WC: Challenging composition
&lt;/h3&gt;

&lt;p&gt;Because a web component is an element, you cannot "merge" the behaviors of two web components in a single element. For example, let's say you have this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;my-webcomponent&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"row"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        ...
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/my-webcomponent&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you want to add a second behavior to your container. How would you do that?&lt;/p&gt;

&lt;p&gt;Like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;second-behavior&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;my-webcomponent&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"row"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            ...
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/my-webcomponent&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/second-behavior&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;my-webcomponent&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;second-behavior&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"row"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            ...
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/second-behavior&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/my-webcomponent&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do you see the problem? The HTML elements are inherently hierarchical.&lt;/p&gt;

&lt;p&gt;Wouldn't it be nice if you could &lt;em&gt;compose&lt;/em&gt; your element's behavior from the library of behaviors, without meddling in the HTML structure? As long as Web Components are elements, it won't be easily possible. You would have to build in a composition layer on top of &lt;code&gt;my-webcomponent&lt;/code&gt; from the example above. It's a boilerplate that should be easily available from the get-go.&lt;/p&gt;

&lt;p&gt;There are propositions to solve this problem, e.g.: &lt;a href="https://github.com/webplatformco/project-custom-attributes/" rel="noopener noreferrer"&gt;project-custom-attributes&lt;/a&gt; But that's where we stand right now.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wraplet: Natural composition
&lt;/h3&gt;

&lt;p&gt;Wraplet uses an element, but an element is oblivious to a wraplet. It means that multiple wraplets can be attached to the same element, providing different behaviors. Extending an element's behavior becomes as simple as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;data-js-behavior1&lt;/span&gt; &lt;span class="na"&gt;data-js-behavior2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"row"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows thinking about wraplets as behaviors.&lt;/p&gt;

&lt;h3&gt;
  
  
  WC: The lifecycle of a component is the same as the lifecycle of an element
&lt;/h3&gt;

&lt;p&gt;When a web component is added to the DOM, the &lt;code&gt;connectedCallback()&lt;/code&gt; hook gets called. When it is removed, the &lt;code&gt;disconnectedCallback()&lt;/code&gt; hook gets called. There is a problem with this. These are synchronous hooks, just like adding or removing an element from DOM are synchronous operations. The problem becomes clear when you want to do something that is decoupled from the lifecycle of an element itself. For example, you want to run destruction logic before the element is removed from the DOM. This is often useful because during destruction you can still show something to the user. Or maybe you want to initialize an element before adding it to the DOM, so it will already have the correct state. With web components, you would need to implement a custom lifecycle logic. There is no standard for such a thing, which means that you may encounter methods like &lt;code&gt;init()&lt;/code&gt;, &lt;code&gt;initialize()&lt;/code&gt;, &lt;code&gt;start()&lt;/code&gt; or &lt;code&gt;ready()&lt;/code&gt; in different implementations. It makes things less readable, but you could accept that and roll out your own lifecycle methods. They could solve your problem as long as it's in the scope of a single component. The moment you need to tie together async lifecycles of multiple web components, you've got a problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wraplet: Lifecycle of a component is decoupled from the lifecycle of an element
&lt;/h3&gt;

&lt;p&gt;Wraplet can wrap elements that are not attached to the DOM yet. They can be prepared before they'll even be shown to the user. Wraplets can also be destroyed before an element is removed from the DOM. The destruction logic is happening, and at the same time an element may display a spinner to the user. Decoupling of the wraplet's lifecycle from the element's one gives you flexibility.&lt;/p&gt;

&lt;p&gt;As an additional bonus, this lifecycle can be shared across multiple wraplets, allowing for dependent asynchronous lifecycles - initializing one component can trigger initialization of others, and the whole process completes when the whole dependency tree is ready. It makes wraplets more self-contained, as they can easily manage their own asynchronous dependencies across the whole dependency tree.&lt;/p&gt;

&lt;h3&gt;
  
  
  WC: Inability to extend built-in elements
&lt;/h3&gt;

&lt;p&gt;I mean, you can, but not in Safari, so in fact, you cannot. Practically, you have to reimplement built-in elements or use libraries that do it for you. That means additional bandwidth used, and, maybe even more important nowadays, additional dependencies (more supply chain attack vectors, more potential vulnerabilities).&lt;/p&gt;

&lt;p&gt;If you want to polyfill a built-in element support, at the moment of me writing this article, &lt;code&gt;@ungap/custom-elements&lt;/code&gt; weights &lt;strong&gt;2.76 kB&lt;/strong&gt;. For comparison, the whole &lt;code&gt;wraplet&lt;/code&gt; weights &lt;strong&gt;4.48 kB&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wraplet: All elements are treated the same, consistently
&lt;/h3&gt;

&lt;p&gt;From the wraplet's perspective, a built-in element is no different from a custom one. Yes, you can wrap a Web Component in a wraplet. This provides you with a unified DX. You only need to know what an element does to interact with it. That's it.&lt;/p&gt;

&lt;h3&gt;
  
  
  WC: Imperative dependency management
&lt;/h3&gt;

&lt;p&gt;Web Components find their dependencies by querying the DOM directly.&lt;/p&gt;

&lt;p&gt;What happens when one of the dependencies is missing? How to treat optional dependencies and the required ones? That's the logic you have to implement yourself. To understand what a component uses, you have to scan the imperative code, which makes for a bad developer experience. This is because a large part of the answer of what code does comes from what it needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wraplet: Declarative dependency management
&lt;/h3&gt;

&lt;p&gt;When creating a wraplet dependent on other wraplets, you declare a dependency map first. Then, based on this map, the &lt;code&gt;DependencyManager&lt;/code&gt; finds proper elements and instantiates the dependencies giving you back a map of instances that are ready to use. You don't need to validate the structure of your dependencies. You don't need to assert their types. Everything is done for you and validated automatically when your wraplet comes to life, so if there are any errors, you will get immediate feedback. It makes code more readable and much easier to maintain.&lt;/p&gt;

&lt;h2&gt;
  
  
  So when should you pick which?
&lt;/h2&gt;

&lt;p&gt;Both technologies are useful. The honest answer is rarely "one is always better" - it's "which trade-off fits this project?"&lt;/p&gt;

&lt;p&gt;Pick Web Components when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need to publish a self-contained widget into pages whose CSS and scripts you don't control - embeddable chat, analytics dashboards, third-party form widgets. Shadow DOM was designed for exactly that.&lt;/li&gt;
&lt;li&gt;You want a no-dependency, browser-native solution and you're prepared to accept weaker typing and looser composition in exchange.&lt;/li&gt;
&lt;li&gt;"Set an attribute, get a callback" is a natural fit for your component's public API.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pick Wraplet when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're progressively enhancing existing markup - server-rendered apps,
CMS templates, admin panels, large legacy frontends.&lt;/li&gt;
&lt;li&gt;You want TypeScript to actually catch your DOM wiring mistakes, including parent-child relationships.&lt;/li&gt;
&lt;li&gt;You want a readable structure and lifecycles spanning across complex components.&lt;/li&gt;
&lt;li&gt;You want listener cleanup to be the framework's problem, not yours.&lt;/li&gt;
&lt;li&gt;You want the same tag and CSS rules as the rest of your page, with no Shadow DOM boundary to design around.&lt;/li&gt;
&lt;li&gt;You like Web Components, but you are concerned about the scalability and maintenance of a large codebase. Wraplet was designed to address that specifically.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Wraplet and Web Components look at the same situation - frontend fatigue, framework magic, drift away from the DOM - and answer it differently. Web Components answer with a browser-native API and strong runtime encapsulation. Wraplet answers with a TypeScript-first library, a typed dependency model, and a DOM-decoupled lifecycle that works together with this model.&lt;/p&gt;

&lt;p&gt;If your project lives close to existing server-rendered HTML, and you appreciate what TypeScript brings in to the codebases, the &lt;a href="https://wraplet.dev/docs/getting-started/quick-start" rel="noopener noreferrer"&gt;Quick start&lt;/a&gt; is the fastest way to get started.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webcomponents</category>
      <category>typescript</category>
      <category>oop</category>
    </item>
    <item>
      <title>Front-end, regrounded: Why Wraplet might be what you’re missing</title>
      <dc:creator>Luken</dc:creator>
      <pubDate>Wed, 27 May 2026 15:35:45 +0000</pubDate>
      <link>https://dev.to/luken7/why-wraplet-gg8</link>
      <guid>https://dev.to/luken7/why-wraplet-gg8</guid>
      <description>&lt;p&gt;This post was originally written at &lt;a href="https://wraplet.dev/blog/why-wraplet" rel="noopener noreferrer"&gt;https://wraplet.dev/blog/why-wraplet&lt;/a&gt; where the code examples are interactive.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Landscape
&lt;/h2&gt;

&lt;p&gt;Difficulty in recognizing what depends on what is the main reason why the code is unreadable. It applies to both: a micro and macro scale. It's this relationship that makes the fabric of a mental model.&lt;/p&gt;

&lt;p&gt;Frameworks solve this problem by forcing a specific structure on the code. This is what developers like because code becomes predictable. When the dependency model is easier to read, the logic is easier to follow. However, in my opinion, popular frameworks are doing way too much in this department. Everything is clean on the surface level but ugly underneath. And if things don't quite work as expected because of the hidden framework's logic that you didn't fully grasp, you are in for a rough ride of debugging a framework instead of your own code.&lt;/p&gt;

&lt;p&gt;This introduces a whole class of framework-specific knowledge that is needed to be proficient. This additional knowledge makes switching between frameworks exhausting. Imagine you inherited a codebase written in an unknown framework. You have new, clean code, but you cannot interpret it unless you will put a lot of effort to understand this framework's internals.&lt;/p&gt;

&lt;p&gt;This is what makes JavaScript's landscape so gruelling, unless you stick to a single framework and don't bother to learn others, even if they might be better for your use case. And what's the alternative? Bending your favorite framework to fit your needs against its strengths? Defaulting to jQuery or vanilla JS as the quick-and-dirty solution for everything else? Almost everyone at some point thought: "I would rather write this in vanilla JS",&lt;br&gt;
but it has its own problems: it's imperative, the code structure is completely up to the developer, and as each developer has different preferences, the resulting code will always be unreadable to someone. Inconsistent dependency structure is what makes&lt;br&gt;
vanilla JS hard to maintain. It's the same thing that made jQuery hated.&lt;/p&gt;

&lt;p&gt;What if we had a framework focused exclusively on giving you the readable and testable structure, with minimal internals and magic? It would make your "quick and dirty" solutions no longer dirty but actually maintainable without much of the framework-specific knowledge required.&lt;/p&gt;

&lt;p&gt;Wraplet enters the chat.&lt;/p&gt;
&lt;h2&gt;
  
  
  Wraplet
&lt;/h2&gt;

&lt;p&gt;Wraplet is a small TypeScript framework that wraps real DOM nodes with real classes, gives them a predictable lifecycle, and lets you wire them together with typed, declarative dependencies.&lt;/p&gt;

&lt;p&gt;That is essentially all it does, and most of the time, that is &lt;em&gt;all&lt;/em&gt; you actually need - unless you are building a full SPA, and even then, you can get surprisingly far with Wraplet alone.&lt;/p&gt;

&lt;p&gt;What follows is a short tour of &lt;em&gt;why&lt;/em&gt; that turn might be worth taking.&lt;/p&gt;
&lt;h3&gt;
  
  
  The growing fatigue with framework magic
&lt;/h3&gt;

&lt;p&gt;Wraplet came to life as an answer to the growing fatigue with the popular frameworks. React, Vue, Angular, Svelte - each of them is a great tool in its own right, and each of them asks you to accept a certain amount of magic in exchange for productivity.&lt;/p&gt;

&lt;p&gt;There is a:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;virtual DOM that diffs and patches on your behalf,&lt;/li&gt;
&lt;li&gt;reactivity system that quietly tracks reads in invisible scopes,&lt;/li&gt;
&lt;li&gt;compiler that rewrites your code into something that no longer
matches what you wrote,&lt;/li&gt;
&lt;li&gt;and an ever-growing collection of file-based routers, server components, hydration boundaries, and "use client" directives, all revolving around a constantly moving definition of what a "component" even is.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When everything works, it feels elegant. When something breaks, you are often debugging the framework instead of your code, and the gap between "what I wrote" and "what runs in the browser" keeps widening. A growing number of developers - especially those maintaining long-lived applications, server-rendered apps, or libraries - are starting to push back. They want code they can read top to bottom, without a mental model of a compiler sitting between them and the browser.&lt;/p&gt;

&lt;p&gt;Wraplet is built for exactly that audience. There is no virtual DOM, no compile-time transformation of your components, no hidden reactivity graph. A wraplet is a class. The class has a &lt;a href="https://dev.to/docs/concepts/lifecycle"&gt;lifecycle&lt;/a&gt; with a constructor, an &lt;code&gt;onInitialize&lt;/code&gt; hook, an &lt;code&gt;onDestroy&lt;/code&gt; hook, and a &lt;code&gt;node&lt;/code&gt; it owns. That is the whole mental model.&lt;/p&gt;

&lt;p&gt;Here is the smallest example - a clicker wrapping an existing&lt;br&gt;
piece of HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;strong&amp;gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;data-js-clicker&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Click me!&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&amp;lt;/strong&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AbstractWraplet&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wraplet&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// A single class wrapping a real DOM node.&lt;/span&gt;
&lt;span class="c1"&gt;// No virtual DOM, no compiler, no hidden reactivity.&lt;/span&gt;
&lt;span class="c1"&gt;// What you read here is exactly what runs in the browser.&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Clicker&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;AbstractWraplet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nx"&gt;override&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;onInitialize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// The `nodeManager` is a small helper that ties listeners&lt;/span&gt;
        &lt;span class="c1"&gt;// to the wraplet lifecycle. They get cleaned up automatically&lt;/span&gt;
        &lt;span class="c1"&gt;// when the wraplet is destroyed - no leaks, no surprises.&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;nodeManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&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;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;red&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="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&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="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[data-js-clicker]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clicker&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;Clicker&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;clicker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wraplet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The HTML is the HTML you would have written anyway. The class attaches to it, listens for clicks, and updates the DOM. There is no template language, no re-render cycle, no reconciliation. If you can read JavaScript and the DOM API, you can read - and debug - this code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Easier to understand - for humans &lt;em&gt;and&lt;/em&gt; LLMs
&lt;/h3&gt;

&lt;p&gt;A subtle but important consequence of avoiding magic is that Wraplet code is unusually friendly to &lt;em&gt;readers&lt;/em&gt;. And in 2026, "readers" no longer means just your teammates. Modern development is increasingly a collaboration between humans and large language models. Whether you use an LLM to review a pull request, generate a new component, or explain a legacy module, the model is doing the same thing a new hire does: it reads the code and tries to build&lt;br&gt;
a mental model from it.&lt;/p&gt;

&lt;p&gt;Heavily abstract frameworks make that harder, not easier. A React component's behavior depends on hooks, context providers, Suspense boundaries, and rendering rules that are nowhere to be seen in the component itself. A reactive Vue or Svelte component depends on compiler-generated update logic that you simply cannot read in the source. Decorator-heavy Angular code depends on dependency injection metadata produced at build time. In every case, the LLM - and the human alongside it - has to infer a lot of invisible&lt;br&gt;
context just to understand a snippet.&lt;/p&gt;

&lt;p&gt;Wraplet inverts that. A wraplet is a single class with explicitly declared dependencies in a plain object literal, using standard DOM APIs, with a lifecycle made of constructor and two methods you can actually see. That is a remarkably LLM-friendly surface. When you ask an assistant to add a feature, refactor a component, or write a test for a wraplet, it can reason about the code from the code itself, without needing to "know" how some framework's internals happen to behave this month. The same property helps onboarding: a new developer can open a wraplet file, read it linearly, and understand it - without first internalizing a runtime model.&lt;/p&gt;
&lt;h3&gt;
  
  
  The benefits of leaning hard on TypeScript
&lt;/h3&gt;

&lt;p&gt;Many frameworks &lt;em&gt;support&lt;/em&gt; TypeScript. Wraplet is &lt;em&gt;designed around&lt;/em&gt; it. The clearest example is its dependency map. Instead of looking up children with &lt;code&gt;querySelector&lt;/code&gt; and casting them, you describe them once - declaratively - and get a fully typed &lt;code&gt;this.d&lt;/code&gt; object back:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;data-js-greeter&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;data-js-greeter__name&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Your name"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;data-js-greeter__submit&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Greet&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;data-js-greeter__output&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;AbstractWraplet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;AbstractDependentWraplet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;DDM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;WrapletDependencyMap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wraplet&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Small leaf wraplets - each one owns a single, real DOM node&lt;/span&gt;
&lt;span class="c1"&gt;// and exposes a tiny, typed API around it.&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NameInput&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;AbstractWraplet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLInputElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;getValue&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="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;node&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;SubmitButton&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;AbstractWraplet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLButtonElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;callback&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;void&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Lifecycle-aware listener: removed automatically on destroy.&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;nodeManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addListener&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="nf"&gt;callback&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;Output&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;AbstractWraplet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;value&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="k"&gt;void&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;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="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="c1"&gt;// Declarative, typed dependency map.&lt;/span&gt;
&lt;span class="c1"&gt;// `satisfies WrapletDependencyMap` keeps full inference - hover any&lt;/span&gt;
&lt;span class="c1"&gt;// property in your IDE and you'll see the exact wraplet class behind it.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;nameInput&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[data-js-greeter__name]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NameInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;multiple&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[data-js-greeter__submit]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SubmitButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;multiple&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[data-js-greeter__output]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;multiple&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;satisfies&lt;/span&gt; &lt;span class="nx"&gt;WrapletDependencyMap&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;Greeter&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;AbstractDependentWraplet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nx"&gt;override&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;onInitialize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// `this.d` is fully typed from the map above.&lt;/span&gt;
        &lt;span class="c1"&gt;// Try renaming `submit` to `submitButton` - TypeScript will&lt;/span&gt;
        &lt;span class="c1"&gt;// immediately flag every place that needs updating.&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;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&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;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nameInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stranger&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;d&lt;/span&gt;&lt;span class="p"&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;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Hello, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;!`&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;const&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;HTMLElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[data-js-greeter]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// DDM manages dependencies. The instantiation/initialization&lt;/span&gt;
&lt;span class="c1"&gt;// can be simplified with helpers.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;greeter&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;Greeter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DDM&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;map&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;greeter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wraplet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things are happening here that pay back continuously. Renaming is safe: change &lt;code&gt;submit&lt;/code&gt; to &lt;code&gt;submitButton&lt;/code&gt; in the map and every usage of &lt;code&gt;this.d.submit&lt;/code&gt; immediately becomes a compile error - no grep, no surprises at runtime. Children are real types, not strings: &lt;code&gt;this.d.nameInput&lt;/code&gt; is a &lt;code&gt;NameInput&lt;/code&gt; instance with its methods autocompleted, and the selector lives in the map rather than scattered across your code. Wrong wiring fails to compile: calling a method that doesn't exist on a child, or treating an&lt;br&gt;
&lt;code&gt;HTMLInputElement&lt;/code&gt; as an &lt;code&gt;HTMLButtonElement&lt;/code&gt;, is caught before you ever reload the page. And all of this happens with no decorators, no metadata, no reflection - just plain TypeScript inference over plain object literals. What you see in the editor is what the compiler sees.&lt;/p&gt;

&lt;p&gt;This is the kind of type safety that scales well to large codebases. You don't have to &lt;em&gt;opt into&lt;/em&gt; it with extra annotations, and you don't lose it the moment you cross a framework boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, why Wraplet?
&lt;/h2&gt;

&lt;p&gt;Wraplet is not trying to replace React, Vue, or Angular. It is not the right tool for every project, see &lt;a href="https://wraplet.dev/docs/getting-started/what-is-wraplet/wraplet-vs-popular-frameworks" rel="noopener noreferrer"&gt;Wraplet vs popular frameworks&lt;/a&gt; for a fair comparison.&lt;/p&gt;

&lt;p&gt;But if you are tired of debugging framework internals, if you maintain a server-rendered app and want structure on the frontend without rewriting it as an SPA, if you build libraries or widgets that have to embed cleanly into other people's pages, if you want code that both humans &lt;em&gt;and&lt;/em&gt; LLMs can read without a runtime mental model, or if you simply want TypeScript to actually&lt;br&gt;
catch your DOM wiring mistakes - then Wraplet is worth a serious look.&lt;/p&gt;

&lt;p&gt;A good next step is the &lt;a href="https://wraplet.dev/docs/getting-started/quick-start" rel="noopener noreferrer"&gt;Quick start&lt;/a&gt; - it walks you from a one-line install to a working multi-component example in a few minutes. From there, the &lt;a href="https://wraplet.dev/docs/getting-started/what-is-wraplet/core-strengths" rel="noopener noreferrer"&gt;Core strengths&lt;/a&gt;&lt;br&gt;
page goes deeper into what the framework is really good at.&lt;/p&gt;

&lt;p&gt;Less magic. More TypeScript. Code you can actually read.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>frontend</category>
      <category>typescript</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
