<?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: Alexander Nenashev</title>
    <description>The latest articles on DEV Community by Alexander Nenashev (@alexander-nenashev).</description>
    <link>https://dev.to/alexander-nenashev</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%2F911056%2F3b0e9f74-4b5d-4adc-affd-cb01b512af15.jpg</url>
      <title>DEV Community: Alexander Nenashev</title>
      <link>https://dev.to/alexander-nenashev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alexander-nenashev"/>
    <language>en</language>
    <item>
      <title>A Declarative Naming Layer for the View Transition API</title>
      <dc:creator>Alexander Nenashev</dc:creator>
      <pubDate>Tue, 13 Oct 2026 13:32:33 +0000</pubDate>
      <link>https://dev.to/alexander-nenashev/a-declarative-naming-layer-for-the-view-transition-api-1m02</link>
      <guid>https://dev.to/alexander-nenashev/a-declarative-naming-layer-for-the-view-transition-api-1m02</guid>
      <description>&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/2FyH32I0Lyk" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;For a long time, I treated interface animation as mostly decorative. It could make a product feel more polished or visually impressive, but I rarely considered it functionally important. In fact, I usually preferred to disable animations wherever possible. A dropdown did not seem to need an animation. Smooth scrolling often felt slower than an immediate jump. Waiting for UI motion to finish could feel like unnecessary latency.&lt;/p&gt;

&lt;p&gt;Working on more complex interfaces changed my view.&lt;/p&gt;

&lt;p&gt;Transitions can be a form of user feedback. When an interaction causes a substantial change in the interface, the developer already knows exactly what happened: which component disappeared, which one replaced it, which panel expanded, and where the new state came from. A user who is still learning the interface does not have that internal model.&lt;/p&gt;

&lt;p&gt;An abrupt state change can therefore be difficult to interpret. A transition can preserve visual continuity between the old and new states. When an element moves, expands, collapses, or transforms into another part of the interface, the motion gives the user additional information about the relationship between those states. It helps explain what their action changed and how the new layout relates to the previous one.&lt;/p&gt;

&lt;p&gt;That makes transitions part of UX rather than merely visual styling. They can reduce the cognitive cost of navigating an unfamiliar interface and make its behavior easier to learn.&lt;/p&gt;

&lt;p&gt;This is one of the reasons I became interested in the View Transition API. It gives the browser a native mechanism for creating complex transitions between DOM states without manually implementing every animation and coordinating the old and new layouts yourself. For me, this changed View Transitions from a visual extra into a useful part of interface architecture.&lt;/p&gt;

&lt;p&gt;Once I started using them more extensively, however, a different problem appeared: managing &lt;code&gt;view-transition-name&lt;/code&gt; across a large component hierarchy quickly became verbose and difficult to maintain.&lt;/p&gt;

&lt;p&gt;That is the problem this article is about.&lt;/p&gt;




&lt;p&gt;The View Transition API is one of the browser APIs I enjoy using most. For simple cases, it is remarkably compact: update the DOM inside &lt;code&gt;document.startViewTransition()&lt;/code&gt;, assign a few &lt;code&gt;view-transition-name&lt;/code&gt; values, and let the browser handle the snapshots and interpolation.&lt;/p&gt;

&lt;p&gt;The complexity changes quickly once the interface contains many independently changing elements.&lt;/p&gt;

&lt;p&gt;In a real application, the same element may participate in different transitions depending on the operation. A transition may involve elements distributed across several Vue components. Some elements should inherit a transition namespace from their ancestors, while others should start a new namespace or stop propagation. Multiple independent transition hierarchies may coexist in the same DOM tree.&lt;/p&gt;

&lt;p&gt;I initially handled these cases individually, including with coding agents. The individual implementations worked, but the amount of imperative orchestration kept growing: find elements, assign names, coordinate old and new DOM states, wait for Vue updates, remove temporary state, handle dynamic instances, and repeat similar logic for the next transition.&lt;/p&gt;

&lt;p&gt;The code was becoming substantially more verbose than the UI behavior it described.&lt;/p&gt;

&lt;p&gt;I eventually moved the problem into a separate declarative naming layer.&lt;/p&gt;

&lt;p&gt;The result is a small internal system used in &lt;a href="https://aleklabs.dev/eye-trainer" rel="noopener noreferrer"&gt;AlekLabs Eye Trainer&lt;/a&gt;. View transition participation is described directly in component templates using attributes, while a runtime function activates the required transition hierarchy only for the duration of a transition. Vite compiler transforms, generated TypeScript types, and a VS Code grammar provide the tooling around it.&lt;/p&gt;

&lt;p&gt;The browser still performs the actual View Transition. The new layer deals with naming and orchestration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving Transition Identity Out of CSS
&lt;/h2&gt;

&lt;p&gt;A normal View Transition implementation often starts with CSS such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;view-transition-name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;card&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;That works well while an element has one permanent transition identity.&lt;/p&gt;

&lt;p&gt;My application needed something more dynamic. A DOM element can participate in one transition when a panel changes, another when a dialog opens, and a more specific child transition when only part of the same UI changes.&lt;/p&gt;

&lt;p&gt;Keeping all of that identity in CSS made the relationship between the component structure and transition structure increasingly difficult to see.&lt;/p&gt;

&lt;p&gt;I wanted the template itself to answer a simple question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Which View Transition hierarchies can this element participate in?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;CSS could then remain responsible for the visual behavior of the generated View Transition pseudo-elements.&lt;/p&gt;

&lt;p&gt;The distinction became important. The attributes in my templates do not directly set &lt;code&gt;view-transition-name&lt;/code&gt;. They are persistent metadata describing possible transition relationships. The actual &lt;code&gt;view-transition-name&lt;/code&gt; values exist only while a particular transition is running.&lt;/p&gt;

&lt;h2&gt;
  
  
  First Attempt: Vue Directives
&lt;/h2&gt;

&lt;p&gt;Vue directives initially looked like a natural solution.&lt;/p&gt;

&lt;p&gt;Something like this would have been expressive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;al-panel&lt;/span&gt; &lt;span class="na"&gt;v-transition=&lt;/span&gt;&lt;span class="s"&gt;"'settings'"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The limitation appeared as soon as the directive was placed on components.&lt;/p&gt;

&lt;p&gt;Vue directives used on components are applied to the component root. Unlike fallthrough attributes, they cannot be forwarded to another element with &lt;code&gt;v-bind="$attrs"&lt;/code&gt;. Multi-root components add further restrictions.&lt;/p&gt;

&lt;p&gt;My UI has a deep component hierarchy, including many internal UI components. I needed transition metadata to move naturally through those abstraction layers.&lt;/p&gt;

&lt;p&gt;Ordinary attributes already have exactly this property in Vue.&lt;/p&gt;

&lt;p&gt;That made attributes a much better transport mechanism.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Small Attribute Language
&lt;/h2&gt;

&lt;p&gt;The initial attribute names were too verbose. Repeating a long &lt;code&gt;view-transition-*&lt;/code&gt; or application-specific prefix throughout large templates made the transition structure visually noisy.&lt;/p&gt;

&lt;p&gt;I introduced &lt;code&gt;$&lt;/code&gt; as a source-level marker for View Transition metadata.&lt;/p&gt;

&lt;p&gt;A simplified example looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;al-panel-expandable&lt;/span&gt; &lt;span class="na"&gt;:&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="na"&gt;modulator-panel=&lt;/span&gt;&lt;span class="s"&gt;"num"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;modulation-curve-setting&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="na"&gt;-motion&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;al-adjust&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="na"&gt;-amount&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/al-panel-expandable&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first attribute establishes a transition family. If &lt;code&gt;num&lt;/code&gt; is &lt;code&gt;2&lt;/code&gt;, its resolved name can become:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;modulator-panel-2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The child attributes extend the inherited family:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;modulator-panel-2-motion
modulator-panel-2-amount
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interesting part is that &lt;code&gt;$-motion&lt;/code&gt; contains no knowledge of &lt;code&gt;modulator-panel&lt;/code&gt;, the instance number, or the component in which the hierarchy started.&lt;/p&gt;

&lt;p&gt;It simply declares its position inside the current transition family.&lt;/p&gt;

&lt;p&gt;This makes transition structure composable across components.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hierarchical Names
&lt;/h2&gt;

&lt;p&gt;The naming layer behaves as a hierarchy rather than a flat collection of unrelated IDs.&lt;/p&gt;

&lt;p&gt;A direct attribute starts a family:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;al-panel&lt;/span&gt; &lt;span class="na"&gt;:&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="na"&gt;al-setting=&lt;/span&gt;&lt;span class="s"&gt;"targetId"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A descendant can extend it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;al-slider&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="na"&gt;-value&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a target ID of &lt;code&gt;rotation&lt;/code&gt;, the runtime can resolve this as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;al-setting-rotation
al-setting-rotation-value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The hierarchy can cross Vue component boundaries because the compiler output consists of normal attributes.&lt;/p&gt;

&lt;p&gt;Several transition families can also coexist in the same area of the application. An element can permanently declare metadata for several possible scenarios while receiving only the actual &lt;code&gt;view-transition-name&lt;/code&gt; relevant to the transition currently being executed.&lt;/p&gt;

&lt;p&gt;This avoids a major practical problem of the native property: a rendered element taking part in a named View Transition has one &lt;code&gt;view-transition-name&lt;/code&gt;, and custom names must be unique among participating rendered elements.&lt;/p&gt;

&lt;p&gt;My layer therefore treats names as potential roles rather than permanent CSS state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Controlling Participation and Propagation
&lt;/h2&gt;

&lt;p&gt;The syntax gradually acquired a few compact controls as real cases appeared.&lt;/p&gt;

&lt;p&gt;The current model can be summarized like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Source syntax&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$panel&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start or participate in a transition family&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$-value&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Extend the inherited family with &lt;code&gt;value&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;:$panel="id"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add a dynamic instance value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$panel!&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Define the transition here without propagating it further&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$panel-&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Establish a family for descendants without making this element participate&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The last two are particularly useful in reusable component trees.&lt;/p&gt;

&lt;p&gt;Sometimes a component is only a structural boundary. Its children need a shared transition namespace, while the component root itself should not get its own snapshot. In other cases a transition applies to one level and should stop before unrelated nested UI starts inheriting it.&lt;/p&gt;

&lt;p&gt;These controls make that decision part of the template rather than additional JavaScript.&lt;/p&gt;

&lt;p&gt;The runtime also supports compact shortcut definitions and the browser's &lt;code&gt;match-element&lt;/code&gt; mode for cases where element identity itself should supply the unique transition matching. Internally, the parser resolves all of these forms into the same transition definition model.&lt;/p&gt;

&lt;h2&gt;
  
  
  The &lt;code&gt;$&lt;/code&gt; Syntax Exists Only During Authoring
&lt;/h2&gt;

&lt;p&gt;I did not want unconventional &lt;code&gt;$...&lt;/code&gt; attributes to become the permanent runtime DOM representation.&lt;/p&gt;

&lt;p&gt;A Vue compiler transform rewrites them during compilation.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$modulator-panel
$-amount
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;becomes runtime metadata in the form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data-transition-modulator-panel
data-transition--amount
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additional marker attributes are generated when propagation or parent-only behavior is requested. The transform operates on the Vue compiler AST, including both static attributes and static &lt;code&gt;v-bind&lt;/code&gt; argument names. &lt;/p&gt;

&lt;p&gt;For example, the source-level &lt;code&gt;!&lt;/code&gt; suffix becomes an internal &lt;code&gt;--no-propagate&lt;/code&gt; marker, while a trailing &lt;code&gt;-&lt;/code&gt; on a named family produces a &lt;code&gt;--parent-only&lt;/code&gt; marker. vite-plugin-vue-transition-attr…&lt;/p&gt;

&lt;p&gt;This gives me two representations with different purposes.&lt;/p&gt;

&lt;p&gt;The source representation is optimized for developer experience:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$modulator-panel
$-motion
$-amount
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The DOM representation is explicit and conventional enough for runtime inspection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data-transition-modulator-panel
data-transition--motion
data-transition--amount
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Runtime Orchestration
&lt;/h2&gt;

&lt;p&gt;All imperative orchestration is concentrated in one &lt;code&gt;startTransition()&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;Application code can start a transition by 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="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;startTransition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`al-setting-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;targetId&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;dialogTarget&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;target&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 can also start from a DOM element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;startTransition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;panel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amountCurveOn&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second form is useful when the caller does not need to know the hierarchy name. The runtime examines the element and its subtree and determines which declared transition names belong to it.&lt;/p&gt;

&lt;p&gt;Multiple prefixes can also be supplied to activate several declarative hierarchies during the same View Transition.&lt;/p&gt;

&lt;p&gt;Internally, &lt;code&gt;startTransition()&lt;/code&gt; performs the lifecycle that I previously had scattered across individual features.&lt;/p&gt;

&lt;p&gt;Before calling &lt;code&gt;document.startViewTransition()&lt;/code&gt;, it resolves the requested prefixes and temporarily assigns &lt;code&gt;viewTransitionName&lt;/code&gt; to matching elements. During the update callback it performs the application state change, waits for Vue's &lt;code&gt;nextTick()&lt;/code&gt;, removes the old assignments, scans the updated DOM, and assigns names again for the new state. After the browser reports the transition as finished, all temporary names are removed.&lt;/p&gt;

&lt;p&gt;The second scan is important. A transition frequently changes which elements exist. The old snapshot and new snapshot therefore cannot rely on a single static set of DOM nodes.&lt;/p&gt;

&lt;p&gt;The declarative attributes remain stable metadata. The runtime derives the correct active names independently on both sides of the DOM update.&lt;/p&gt;

&lt;p&gt;Prefix matching provides another useful property. Starting:&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="nf"&gt;startTransition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;modulator-panel-2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;can activate the family rooted at that prefix, including more specific descendants such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;modulator-panel-2-motion
modulator-panel-2-amount
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The runtime performs that matching centrally instead of requiring every call site to enumerate all participating elements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping CSS Generic
&lt;/h2&gt;

&lt;p&gt;The system also assigns a shared &lt;code&gt;view-transition-class&lt;/code&gt; to participating elements.&lt;/p&gt;

&lt;p&gt;This allows generic snapshot behavior to remain very small:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;::view-transition-old&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.view-transitioned&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
&lt;span class="nd"&gt;::view-transition-new&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.view-transitioned&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;object-fit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;view-transition-class&lt;/code&gt; is designed exactly for this kind of shared styling: unlike &lt;code&gt;view-transition-name&lt;/code&gt;, the class does not need to be unique and can style multiple named transition pseudo-elements. &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/view-transition-class?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;MDN Web Docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Specific transitions can still have their own CSS where required. The naming, hierarchy, selection, and lifecycle no longer have to be encoded there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Editor Tooling Became Part of the Design
&lt;/h2&gt;

&lt;p&gt;Once transition metadata moved into templates, another problem became obvious: attributes disappear visually inside large Vue files.&lt;/p&gt;

&lt;p&gt;I wanted transition structure to be immediately recognizable while reading a component.&lt;/p&gt;

&lt;p&gt;I added a tiny VS Code extension with a TextMate grammar that highlights &lt;code&gt;$...&lt;/code&gt; transition attributes separately from ordinary attributes. The grammar is injected directly into Vue template syntax. transition-attributes.tmLanguage package&lt;/p&gt;

&lt;p&gt;This turned out to be more valuable than I initially expected.&lt;/p&gt;

&lt;p&gt;When opening a large template, I can visually scan the transition structure without tracing JavaScript. Parent families, child segments, and dynamic transition bindings stand out immediately.&lt;/p&gt;

&lt;p&gt;The transition layer became visible as part of the component architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating Transition Names and Types
&lt;/h2&gt;

&lt;p&gt;The next step was eliminating manually maintained name lists.&lt;/p&gt;

&lt;p&gt;A second Vite plugin scans Vue files, parses each SFC with &lt;code&gt;@vue/compiler-sfc&lt;/code&gt;, parses the template with &lt;code&gt;@vue/compiler-dom&lt;/code&gt;, walks the AST, and collects &lt;code&gt;$...&lt;/code&gt; transition names. It runs initially and again when Vue files are added, changed, or removed. vite-plugin-transition-names&lt;/p&gt;

&lt;p&gt;It then generates a TypeScript source file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transitionNames&lt;/span&gt; &lt;span class="o"&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;al-setting&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;modulator-panel&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="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That generated constant feeds the runtime type:&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;type&lt;/span&gt; &lt;span class="nx"&gt;TransitionPrefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;transitionNames&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="o"&gt;|&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;transitionNames&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="s2"&gt;-&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a result, creating a new transition family in a template automatically makes the name available to TypeScript autocomplete at &lt;code&gt;startTransition()&lt;/code&gt; call sites. The same generated information also catches many naming mistakes during development.&lt;/p&gt;

&lt;p&gt;There is no separate registry to remember to update.&lt;/p&gt;

&lt;p&gt;The template is the source of truth.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Production Case
&lt;/h2&gt;

&lt;p&gt;This system grew out of &lt;a href="https://aleklabs.dev/eye-trainer" rel="noopener noreferrer"&gt;AlekLabs Eye Trainer&lt;/a&gt; rather than an isolated animation experiment.&lt;/p&gt;

&lt;p&gt;A single modulation component already contains several examples: dynamically identified modulator panels, motion and amount children, target settings, slider values, dialog transitions, and transitions started both from DOM elements and from explicit hierarchical prefixes.&lt;/p&gt;

&lt;p&gt;That is the kind of component where a collection of one-off View Transition implementations becomes difficult to maintain. Several independent UI operations coexist, and each operation may involve a different subset of the same component tree.&lt;/p&gt;

&lt;p&gt;With the naming layer, most of that information remains close to the markup.&lt;/p&gt;

&lt;p&gt;The behavioral code describes the state update.&lt;/p&gt;

&lt;p&gt;The template describes transition participation.&lt;/p&gt;

&lt;p&gt;The runtime connects the two.&lt;/p&gt;

&lt;h2&gt;
  
  
  This Is Not Fundamentally Vue-Specific
&lt;/h2&gt;

&lt;p&gt;My current implementation is integrated deeply with Vue because &lt;a href="https://aleklabs.dev/eye-trainer" rel="noopener noreferrer"&gt;AlekLabs Eye Trainer&lt;/a&gt; uses Vue. The compiler transform uses Vue's compiler packages, the generated-name plugin scans &lt;code&gt;.vue&lt;/code&gt; templates, and the transition runtime waits for &lt;code&gt;nextTick()&lt;/code&gt; before resolving the new DOM.&lt;/p&gt;

&lt;p&gt;The underlying model is framework-independent.&lt;/p&gt;

&lt;p&gt;The browser only sees DOM attributes and &lt;code&gt;document.startViewTransition()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A React, Svelte, Web Components, or plain HTML implementation could use the same architecture with a different authoring transform and a framework-appropriate mechanism for waiting until the DOM update completes.&lt;/p&gt;

&lt;p&gt;The important part is the separation between persistent declarative metadata and temporary native View Transition state.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned From Building It
&lt;/h2&gt;

&lt;p&gt;The View Transition API itself remained small throughout this process. Most of the engineering work appeared around naming, composition, lifecycle, discoverability, and developer tooling.&lt;/p&gt;

&lt;p&gt;For isolated transitions, direct &lt;code&gt;view-transition-name&lt;/code&gt; declarations are sufficient.&lt;/p&gt;

&lt;p&gt;For a larger application, I found it much more manageable to treat transition participation as its own declarative application layer.&lt;/p&gt;

&lt;p&gt;The final workflow is now straightforward: mark potential participants in the template, compose them into hierarchical namespaces, start a transition from a typed prefix or DOM element, and let one runtime function assign and clean up the browser state.&lt;/p&gt;

&lt;p&gt;Vite handles the source transformation and name discovery. TypeScript provides autocomplete and validation. The editor makes the structure visually recognizable. CSS focuses on the resulting animation.&lt;/p&gt;

&lt;p&gt;This changed View Transitions in &lt;a href="https://aleklabs.dev/eye-trainer" rel="noopener noreferrer"&gt;AlekLabs Eye Trainer&lt;/a&gt; from feature-specific imperative code into reusable infrastructure.&lt;/p&gt;

&lt;p&gt;And, more importantly for a growing UI, adding the next transition no longer requires inventing another orchestration mechanism.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Useful Side Effect: Agent-Friendly Transitions
&lt;/h2&gt;

&lt;p&gt;A pleasant side effect of this architecture is how well coding agents work with it.&lt;/p&gt;

&lt;p&gt;Once an agent is told that &lt;code&gt;$...&lt;/code&gt; attributes describe View Transition participation, it can usually extend fairly complex transitions without needing to understand the runtime implementation. For many tasks, the instruction can be as small as: use the &lt;code&gt;$&lt;/code&gt; transition attributes to connect these elements into the same transition hierarchy.&lt;/p&gt;

&lt;p&gt;Even transitions involving several components and multiple moving elements can often be implemented without adding feature-specific JavaScript or CSS. The shared runtime already handles name resolution, DOM updates, View Transition lifecycle, and cleanup.&lt;/p&gt;

&lt;p&gt;This also makes agent-generated changes much easier to review. Instead of inspecting a new block of transition-specific JavaScript together with additional CSS selectors and animation rules, I can often review only a few attribute changes in the template. The transition structure is visible directly in the markup, and the diff stays small.&lt;/p&gt;

&lt;p&gt;In practice, modern coding agents handle this declarative syntax very well. Once the pattern exists in the codebase, they can infer the hierarchy from nearby examples and produce complex multi-element transitions with surprisingly little guidance.&lt;/p&gt;

&lt;p&gt;For me, this has become another argument for moving repeated UI behavior into declarative infrastructure: it improves developer experience for humans, and it also gives coding agents a much smaller and more constrained surface to modify.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw9emjazkgjffr0tfkarq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw9emjazkgjffr0tfkarq.png" alt=" " width="800" height="1720"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>css</category>
      <category>frontend</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Hard Object References: Stable Object References for Mutable Application State</title>
      <dc:creator>Alexander Nenashev</dc:creator>
      <pubDate>Mon, 06 Jul 2026 18:21:29 +0000</pubDate>
      <link>https://dev.to/alexander-nenashev/hard-object-references-stable-object-references-for-mutable-application-state-6el</link>
      <guid>https://dev.to/alexander-nenashev/hard-object-references-stable-object-references-for-mutable-application-state-6el</guid>
      <description>&lt;p&gt;In JavaScript and TypeScript, object references are often treated as disposable. An object is created, assigned to a variable, passed around, replaced, copied, spread, cloned, and eventually discarded.&lt;/p&gt;

&lt;p&gt;That is normal language behavior, but in larger mutable systems it creates a specific class of bugs: stale aliases.&lt;/p&gt;

&lt;p&gt;A stale alias appears when one part of the program still holds a reference to an old object while another part has already replaced that object with a new one. The old reference is still valid JavaScript, but it no longer points to current data.&lt;/p&gt;

&lt;p&gt;Hard Object References is a discipline for avoiding that class of bugs.&lt;/p&gt;

&lt;p&gt;The idea is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Object and array references should be stable. Do not replace them as a normal update mechanism. Copy data into existing objects instead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This rule is useful for application state, but it is not limited to global stores. It applies to ordinary variables, local component state, nested fields, arrays, drafts, snapshots, runtime models, and temporary objects.&lt;/p&gt;

&lt;p&gt;The broader principle is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Replace primitive values. Do not replace object and array references.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The First Rule: &lt;code&gt;const&lt;/code&gt; for Objects and Arrays
&lt;/h2&gt;

&lt;p&gt;The first level is variable bindings.&lt;/p&gt;

&lt;p&gt;If a variable holds an object or array, it should normally be declared with &lt;code&gt;const&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&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;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;not:&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;let&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&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;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="cm"&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 point is not that the object becomes immutable. It does not.&lt;/p&gt;

&lt;p&gt;This is still possible:&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="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Alex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;items&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="nx"&gt;nextItem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The point is that the variable should not be rebound to a different object:&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="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nextUser&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nextItems&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That replacement changes which object the variable points to. Any other code that still holds the old reference now points to obsolete data.&lt;/p&gt;

&lt;p&gt;So the first rule is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Use &lt;code&gt;const&lt;/code&gt; for object and array references. Mutate or copy data into the object. Do not rebind the reference.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This rule also applies to temporary objects. A temporary object may be short-lived, but while it exists, its reference should still be stable.&lt;/p&gt;

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

&lt;p&gt;Consider this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;John&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Paris&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;street&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Main&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;const&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now another part of the code replaces the nested object:&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="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Berlin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;street&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Main&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this assignment:&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="nx"&gt;address&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The variable &lt;code&gt;address&lt;/code&gt; still points to a valid object, but it points to obsolete data.&lt;/p&gt;

&lt;p&gt;That is the dangerous part. The program does not necessarily crash. It continues to operate on the wrong object.&lt;/p&gt;

&lt;p&gt;A Hard Object References approach avoids the replacement:&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="nx"&gt;HardObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Berlin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;city&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 'Berlin'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reference stayed stable. The data changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Temporary Objects Are Not an Exception
&lt;/h2&gt;

&lt;p&gt;A temporary object is not an excuse to use unstable references.&lt;/p&gt;

&lt;p&gt;This is fine:&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;draft&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;HardObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;draft&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Alex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Berlin&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is suspicious:&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;let&lt;/span&gt; &lt;span class="nx"&gt;draft&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;draft&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Alex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Berlin&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The object may be temporary, but rebinding the reference still breaks the same mental model.&lt;/p&gt;

&lt;p&gt;If a temporary object needs new data, copy the data into it:&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="nx"&gt;HardObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;draft&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nextDraftData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;not:&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="nx"&gt;draft&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nextDraftData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This matters because temporary objects often become inputs to state updates:&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;draft&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;HardObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;draft&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;HardObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;draft&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The draft is not necessarily installed application state, but it still follows the same reference discipline.&lt;/p&gt;

&lt;p&gt;So the stronger rule is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The reference-stability discipline applies to objects in general. Application state is where violating it becomes most expensive.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Application State Makes the Rule Critical
&lt;/h2&gt;

&lt;p&gt;In small local code, replacing an object may be harmless.&lt;/p&gt;

&lt;p&gt;In application state, the same operation becomes dangerous because many parts of the program may retain references to the same object:&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;selectedAddress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&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;editorTarget&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&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;validationTarget&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&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;renderTarget&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this happens later:&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="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nextAddress&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;all previous references may become stale.&lt;/p&gt;

&lt;p&gt;This is why Hard Object References is especially important for state graphs:&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="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;
&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;
&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;settings&lt;/span&gt;
&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;
&lt;span class="nx"&gt;state&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="nx"&gt;pages&lt;/span&gt;
&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selection&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are not just values. They are live reference nodes.&lt;/p&gt;

&lt;p&gt;Once an object participates in such a graph, replacing its reference should not be the normal update mechanism.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Principle to Utility
&lt;/h2&gt;

&lt;p&gt;The principle can be followed manually:&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;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;HardObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nextUser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;HardObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nextSettings&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;HardObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nextItems&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But a consistent helper can make the rule explicit.&lt;/p&gt;

&lt;p&gt;For explanation, call that helper &lt;code&gt;HardObject&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;HardObject&lt;/code&gt; is not the principle itself. It is only one possible way to express the principle:&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;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;HardObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The purpose of &lt;code&gt;HardObject.create()&lt;/code&gt; is to install an object graph under a hard-reference contract.&lt;/p&gt;

&lt;p&gt;The matching update operation is:&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="nx"&gt;HardObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nextUser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Copy compatible data from &lt;code&gt;nextUser&lt;/code&gt; into &lt;code&gt;state.user&lt;/code&gt; without replacing object or array references.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So the conceptual pair is:&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;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;HardObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;HardObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nextUser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;create()&lt;/code&gt; establishes the hard-reference boundary.&lt;br&gt;
&lt;code&gt;set()&lt;/code&gt; updates data inside that boundary.&lt;/p&gt;
&lt;h2&gt;
  
  
  Type-Level Installation
&lt;/h2&gt;

&lt;p&gt;In the simplest implementation, &lt;code&gt;HardObject.create()&lt;/code&gt; can be only a TypeScript-level identity function.&lt;/p&gt;

&lt;p&gt;At runtime, it may simply return the same object:&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;HardObject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Hard&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Hard&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The value is returned as-is, but TypeScript can expose it as a type where object and array references are readonly.&lt;/p&gt;

&lt;p&gt;A simplified type could look like this:&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;type&lt;/span&gt; &lt;span class="nx"&gt;Primitive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;bigint&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;symbol&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&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;Hard&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Primitive&lt;/span&gt;
        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;
        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;TItem&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;ReadonlyArray&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Hard&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TItem&lt;/span&gt;&lt;span class="o"&gt;&amp;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;readonly&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;TKey&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;Hard&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;TKey&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This version is intentionally simplified. A real implementation may use a more refined type where primitive fields remain writable while object and array references become readonly.&lt;/p&gt;

&lt;p&gt;The architectural point is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;HardObject.create()&lt;/code&gt; gives the object graph a hard-reference contract.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That contract can start as TypeScript-only. Later, it can add runtime enforcement without changing the call site.&lt;/p&gt;

&lt;h2&gt;
  
  
  Runtime Enforcement
&lt;/h2&gt;

&lt;p&gt;TypeScript can catch many mistakes during development, but it does not enforce the rule at runtime.&lt;/p&gt;

&lt;p&gt;If runtime enforcement is needed, &lt;code&gt;HardObject.create()&lt;/code&gt; can recursively walk the object graph.&lt;/p&gt;

&lt;p&gt;Whenever it finds a property whose value is an object or array, it can make that property non-writable on its parent object:&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="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;defineProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&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="na"&gt;writable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;configurable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;enumerable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then it continues recursively into that nested object or array and applies the same rule to its children.&lt;/p&gt;

&lt;p&gt;The developer does not manually lock every nested property. &lt;code&gt;HardObject.create()&lt;/code&gt; is the centralized installation point.&lt;/p&gt;

&lt;p&gt;So &lt;code&gt;HardObject.create()&lt;/code&gt; can evolve in stages:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stage&lt;/th&gt;
&lt;th&gt;Runtime behavior&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Type-only&lt;/td&gt;
&lt;td&gt;Returns the same object&lt;/td&gt;
&lt;td&gt;Provides a TypeScript hard-reference contract&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runtime-enforced&lt;/td&gt;
&lt;td&gt;Recursively locks object/array fields&lt;/td&gt;
&lt;td&gt;Prevents object-reference replacement at runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Framework adapter&lt;/td&gt;
&lt;td&gt;Installs the graph into a reactive/store system&lt;/td&gt;
&lt;td&gt;Integrates with a specific environment&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The rule is not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Nothing can change.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The rule is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Primitive values may change. Object and array references should remain stable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So this is a normal update:&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="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Alex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;city&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Berlin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But this is not:&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="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Berlin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;street&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Main&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;address&lt;/code&gt; reference is hard.&lt;/p&gt;

&lt;p&gt;The correct update path is:&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="nx"&gt;HardObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Berlin&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Updating a Hard Object Graph
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;HardObject.set()&lt;/code&gt; is a copy-in operation:&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="nx"&gt;HardObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Alex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Berlin&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It means:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Copy compatible data from the source object into the target object without replacing object and array references.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Primitive fields can be assigned directly:&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="nx"&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;Alex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nested objects are updated recursively:&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="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;city&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Berlin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the &lt;code&gt;address&lt;/code&gt; object itself is not replaced.&lt;/p&gt;

&lt;p&gt;Arrays follow the same principle: the array reference remains stable, while its contents are updated through controlled logic.&lt;/p&gt;

&lt;p&gt;The exact implementation can vary. &lt;code&gt;HardObject&lt;/code&gt; may be a utility, a base class, a model method, a store helper, or a framework-specific adapter. The concept is lower-level than any one implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Change Tracking
&lt;/h2&gt;

&lt;p&gt;A common objection is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If references do not change, how do you detect changes?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The answer depends on the environment.&lt;/p&gt;

&lt;p&gt;In a reactive system, reference replacement does not have to be the primary change signal. If primitive fields and nested object properties are reactive, then normal mutation tracking can already observe changes inside the stable object graph.&lt;/p&gt;

&lt;p&gt;For example, in Vue or a similar reactive system, a deep watcher, computed dependency, proxy trap, or framework-level reactive effect may observe this operation:&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="nx"&gt;HardObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Alex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Berlin&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reference stays stable, but reactive property writes still occur.&lt;/p&gt;

&lt;p&gt;In that case, &lt;code&gt;HardObject.onChange()&lt;/code&gt; may not be necessary. The reactive system already provides change tracking.&lt;/p&gt;

&lt;p&gt;In a non-reactive system, &lt;code&gt;HardObject&lt;/code&gt; can provide a simple explicit notification mechanism:&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="nx"&gt;HardObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;change&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// mark dirty, update UI, notify observers, sync state, etc.&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;HardObject.set()&lt;/code&gt; can emit a change only if it actually changed at least one value. If the incoming data is equal to the current data, no change event is needed.&lt;/p&gt;

&lt;p&gt;So the rule is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Do not require object replacement to be the change signal.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Use the mechanism that fits the environment: framework reactivity, deep watchers, proxy-based tracking, explicit events, dirty flags, version counters, or observers.&lt;/p&gt;

&lt;p&gt;Hard Object References only says that object references should remain stable. It does not prescribe one universal change-tracking system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contrast with Immutable State
&lt;/h2&gt;

&lt;p&gt;Immutable update style treats state as a value. Updating state usually means producing a new object graph:&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;nextState&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="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;user&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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&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;Alex&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reference replacement is intentional. Changed references are used to detect changed branches.&lt;/p&gt;

&lt;p&gt;Hard Object References uses a different model:&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="nx"&gt;HardObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Alex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The object remains the same. Its data changes.&lt;/p&gt;

&lt;p&gt;The central contrast is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In immutable systems, references are change signals.&lt;br&gt;
In Hard Object References, references are stable addresses.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Topic&lt;/th&gt;
&lt;th&gt;Immutable Update Style&lt;/th&gt;
&lt;th&gt;Hard Object References&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Main model&lt;/td&gt;
&lt;td&gt;State as value&lt;/td&gt;
&lt;td&gt;State as object graph&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Update mechanism&lt;/td&gt;
&lt;td&gt;Produce new state&lt;/td&gt;
&lt;td&gt;Copy into existing graph&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Object binding&lt;/td&gt;
&lt;td&gt;May be replaced&lt;/td&gt;
&lt;td&gt;Prefer &lt;code&gt;const&lt;/code&gt; for object/array references&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Object field replacement&lt;/td&gt;
&lt;td&gt;Normal&lt;/td&gt;
&lt;td&gt;Forbidden or controlled&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Object identity&lt;/td&gt;
&lt;td&gt;Often disposable&lt;/td&gt;
&lt;td&gt;Stable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nested references&lt;/td&gt;
&lt;td&gt;Commonly replaced&lt;/td&gt;
&lt;td&gt;Preserved&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Change signal&lt;/td&gt;
&lt;td&gt;Reference change&lt;/td&gt;
&lt;td&gt;Reactivity, event, dirty flag, version counter, observer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best fit&lt;/td&gt;
&lt;td&gt;Serializable state, reducers, snapshots&lt;/td&gt;
&lt;td&gt;Complex mutable runtime state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Main risk&lt;/td&gt;
&lt;td&gt;Stale captured old state&lt;/td&gt;
&lt;td&gt;Uncontrolled mutation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Neither model is universally better.&lt;/p&gt;

&lt;p&gt;Immutable updates are excellent when state is mostly plain data and the architecture benefits from treating every update as a value transformation.&lt;/p&gt;

&lt;p&gt;Hard Object References is useful when state is a mutable object graph and references are used as live handles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where This Works Well
&lt;/h2&gt;

&lt;p&gt;Hard Object References fits systems where object references are retained and reused over time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;complex frontend state&lt;/li&gt;
&lt;li&gt;large component-local state&lt;/li&gt;
&lt;li&gt;document editors&lt;/li&gt;
&lt;li&gt;visual editors&lt;/li&gt;
&lt;li&gt;reader engines&lt;/li&gt;
&lt;li&gt;graph editors&lt;/li&gt;
&lt;li&gt;timeline editors&lt;/li&gt;
&lt;li&gt;canvas or layout tools&lt;/li&gt;
&lt;li&gt;class-based domain models&lt;/li&gt;
&lt;li&gt;runtime controllers&lt;/li&gt;
&lt;li&gt;selection models&lt;/li&gt;
&lt;li&gt;render pipelines&lt;/li&gt;
&lt;li&gt;async processes that keep object handles&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The boundary is not global state versus local state.&lt;/p&gt;

&lt;p&gt;A large component with complex internal state can suffer from the same stale-reference problems as an application-level store. If nested objects are passed around inside that component and later replaced, local aliases can become obsolete too.&lt;/p&gt;

&lt;p&gt;The real boundary is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can another piece of logic keep this object reference and later expect it to remain current?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If yes, the reference should usually be hard.&lt;/p&gt;

&lt;h2&gt;
  
  
  When This Rule Is Less Important
&lt;/h2&gt;

&lt;p&gt;This discipline is less critical for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;primitive values&lt;/li&gt;
&lt;li&gt;short-lived scalar calculations&lt;/li&gt;
&lt;li&gt;small isolated functions&lt;/li&gt;
&lt;li&gt;pure transformation pipelines&lt;/li&gt;
&lt;li&gt;immutable reducer-based architectures&lt;/li&gt;
&lt;li&gt;throwaway objects that never escape their local scope&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even in these cases, the rule can still be useful as a default bias, because reference stability depends on a question that is often hard to answer upfront:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can this reference escape, be retained, or become obsolete after replacement?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Benefits
&lt;/h2&gt;

&lt;p&gt;The main benefit is not less code. The main benefit is a stronger state invariant.&lt;/p&gt;

&lt;p&gt;Object references remain valid.&lt;/p&gt;

&lt;p&gt;Retained object handles do not silently become obsolete.&lt;/p&gt;

&lt;p&gt;Nested state ownership becomes clearer.&lt;/p&gt;

&lt;p&gt;Debugging gets a simple rule:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If an object reference changed, something violated the reference discipline.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is especially useful in systems where many subsystems interact with the same mutable object graph.&lt;/p&gt;

&lt;h2&gt;
  
  
  Costs
&lt;/h2&gt;

&lt;p&gt;The costs are real.&lt;/p&gt;

&lt;p&gt;Mutation must be controlled. If every part of the application mutates everything freely, the system becomes unstable.&lt;/p&gt;

&lt;p&gt;Change tracking must exist. In reactive systems, this may already be handled by the framework. In non-reactive systems, it may require explicit events, observers, dirty flags, or version counters.&lt;/p&gt;

&lt;p&gt;The approach is also less natural for tooling built around immutable snapshots, reducer replay, time travel, and structural sharing.&lt;/p&gt;

&lt;p&gt;Hard Object References is not a replacement for immutable architecture in every application. It is a different tradeoff for a different kind of state.&lt;/p&gt;

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

&lt;p&gt;Hard Object References treats object references as stable addresses, not disposable values.&lt;/p&gt;

&lt;p&gt;The rule starts with mandatory use of &lt;code&gt;const&lt;/code&gt; with objects and arrays and continues inside object graphs:&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="nx"&gt;HardObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nextAddress&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;not:&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="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nextAddress&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A utility such as &lt;code&gt;HardObject&lt;/code&gt; can make the rule explicit. &lt;code&gt;HardObject.create()&lt;/code&gt; establishes a hard-reference contract. It can start as a type-level identity function and later add runtime enforcement without changing the call site. &lt;code&gt;HardObject.set()&lt;/code&gt; updates the graph by copying data into existing references.&lt;/p&gt;

&lt;p&gt;This is not presented as a newly invented universal pattern. The underlying concerns are old: object identity, aliasing, ownership, mutation control, and stable object graphs. Hard Object References is my practical formulation of those concerns after repeatedly seeing the same class of bugs in mutable applications: references becoming obsolete because some part of the object graph was replaced instead of updated.&lt;/p&gt;

&lt;p&gt;In my own code, this discipline became a stability rule. It reduces Heisenbug-like behavior where the program still runs but some logic works with a detached object. It also changes the mental model: objects are no longer disposable containers that can be swapped at any level. They become stable addresses, and updates become explicit data transfer into those addresses.&lt;/p&gt;

&lt;p&gt;The core rule is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Replace primitive values. Do not replace object and array references.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or shorter:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;References are not change signals. They are stable addresses.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>webdev</category>
      <category>typescript</category>
      <category>javascript</category>
      <category>oop</category>
    </item>
    <item>
      <title>Why Every Frontend Project Should Have Its Own UI Layer</title>
      <dc:creator>Alexander Nenashev</dc:creator>
      <pubDate>Wed, 20 May 2026 11:22:55 +0000</pubDate>
      <link>https://dev.to/alexander-nenashev/why-every-frontend-project-should-have-its-own-ui-layer-3jj2</link>
      <guid>https://dev.to/alexander-nenashev/why-every-frontend-project-should-have-its-own-ui-layer-3jj2</guid>
      <description>&lt;p&gt;Modern frontend development is heavily centered around component frameworks: Vue, React, Svelte, Solid, Angular, and others. The ecosystem also provides endless UI libraries: Vuetify, PrimeVue, Naive UI, MUI, Ant Design, Chakra, Radix, Headless UI, shadcn/ui, and hundreds more.&lt;/p&gt;

&lt;p&gt;Most teams directly import these libraries into application code.&lt;/p&gt;

&lt;p&gt;I think that is a long-term architectural mistake.&lt;/p&gt;

&lt;p&gt;Every serious frontend project should have its own internal UI layer — its own component system — even if internally it relies almost entirely on third-party libraries.&lt;/p&gt;

&lt;p&gt;Not because reinventing buttons is fun.&lt;br&gt;
Because abstraction boundaries matter.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Problem With Directly Using UI Libraries
&lt;/h2&gt;

&lt;p&gt;Direct usage tightly couples the application to external implementation details.&lt;/p&gt;

&lt;p&gt;You start with something simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ElButton&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"primary"&lt;/span&gt; &lt;span class="na"&gt;size=&lt;/span&gt;&lt;span class="s"&gt;"small"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Save
&lt;span class="nt"&gt;&amp;lt;/ElButton&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A year later:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the library changes APIs,&lt;/li&gt;
&lt;li&gt;design requirements evolve,&lt;/li&gt;
&lt;li&gt;accessibility requirements appear,&lt;/li&gt;
&lt;li&gt;dark theme becomes mandatory,&lt;/li&gt;
&lt;li&gt;SSR breaks,&lt;/li&gt;
&lt;li&gt;bundle size becomes a problem,&lt;/li&gt;
&lt;li&gt;a component becomes abandoned,&lt;/li&gt;
&lt;li&gt;or the team simply wants another UI system.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now the entire codebase depends on thousands of scattered library-specific decisions.&lt;/p&gt;

&lt;p&gt;The UI library stops being a dependency.&lt;/p&gt;

&lt;p&gt;It becomes infrastructure.&lt;/p&gt;




&lt;h1&gt;
  
  
  Your UI Layer Is An Architectural Boundary
&lt;/h1&gt;

&lt;p&gt;Instead of using third-party components directly, create your own application-level components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;AppButton&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;AppInput&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;AppDialog&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;AppTable&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;AppDatePicker&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;AppPageLayout&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;AppEntityEditor&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Internally, these components may use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PrimeVue&lt;/li&gt;
&lt;li&gt;Vuetify&lt;/li&gt;
&lt;li&gt;Radix&lt;/li&gt;
&lt;li&gt;native browser APIs&lt;/li&gt;
&lt;li&gt;custom rendering&lt;/li&gt;
&lt;li&gt;or completely different implementations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The application itself should not care.&lt;/p&gt;

&lt;p&gt;This creates a stable internal contract between the product and external dependencies.&lt;/p&gt;

&lt;p&gt;The application talks to &lt;em&gt;your&lt;/em&gt; UI architecture.&lt;/p&gt;

&lt;p&gt;Your architecture talks to vendor libraries.&lt;/p&gt;




&lt;h1&gt;
  
  
  Wrappers Are Not "Extra Boilerplate"
&lt;/h1&gt;

&lt;p&gt;A common objection:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Why wrap everything? That's just unnecessary abstraction."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is not abstraction for abstraction's sake.&lt;/p&gt;

&lt;p&gt;It is containment.&lt;/p&gt;

&lt;p&gt;The wrapper layer centralizes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;naming conventions,&lt;/li&gt;
&lt;li&gt;defaults,&lt;/li&gt;
&lt;li&gt;accessibility,&lt;/li&gt;
&lt;li&gt;theme propagation,&lt;/li&gt;
&lt;li&gt;behavior normalization,&lt;/li&gt;
&lt;li&gt;analytics hooks,&lt;/li&gt;
&lt;li&gt;keyboard handling,&lt;/li&gt;
&lt;li&gt;validation,&lt;/li&gt;
&lt;li&gt;localization,&lt;/li&gt;
&lt;li&gt;performance workarounds,&lt;/li&gt;
&lt;li&gt;SSR compatibility,&lt;/li&gt;
&lt;li&gt;browser quirks,&lt;/li&gt;
&lt;li&gt;and migration strategy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without wrappers, all of this leaks into product code.&lt;/p&gt;

&lt;p&gt;Then every page becomes infrastructure code.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Biggest Advantage: Replaceability
&lt;/h1&gt;

&lt;p&gt;This is the most important benefit.&lt;/p&gt;

&lt;p&gt;When UI libraries are hidden behind your own components, replacing them becomes feasible.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;AppDatePicker&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Today internally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PrimeVue DatePicker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tomorrow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Flatpickr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Native browser date input
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Custom implementation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application code remains unchanged.&lt;/p&gt;

&lt;p&gt;This matters more than most teams realize.&lt;/p&gt;

&lt;p&gt;UI ecosystems move extremely fast. Libraries become abandoned surprisingly often.&lt;/p&gt;

&lt;p&gt;A thin internal UI layer dramatically reduces migration cost.&lt;/p&gt;




&lt;h1&gt;
  
  
  Mixing Multiple UI Libraries Becomes Safe
&lt;/h1&gt;

&lt;p&gt;Direct usage of multiple UI systems usually creates chaos.&lt;/p&gt;

&lt;p&gt;Different:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;design languages,&lt;/li&gt;
&lt;li&gt;spacing systems,&lt;/li&gt;
&lt;li&gt;keyboard behavior,&lt;/li&gt;
&lt;li&gt;focus handling,&lt;/li&gt;
&lt;li&gt;overlay logic,&lt;/li&gt;
&lt;li&gt;animation systems,&lt;/li&gt;
&lt;li&gt;theming models,&lt;/li&gt;
&lt;li&gt;and accessibility semantics.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But with an internal UI layer, multiple libraries can coexist safely.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one library provides excellent tables,&lt;/li&gt;
&lt;li&gt;another has better dialogs,&lt;/li&gt;
&lt;li&gt;another has superior date pickers,&lt;/li&gt;
&lt;li&gt;another has accessible comboboxes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your application never sees this complexity directly.&lt;/p&gt;

&lt;p&gt;Your wrapper layer normalizes it.&lt;/p&gt;

&lt;p&gt;This is extremely powerful.&lt;/p&gt;




&lt;h1&gt;
  
  
  Your UI Layer Is Also A Design System
&lt;/h1&gt;

&lt;p&gt;The important shift:&lt;/p&gt;

&lt;p&gt;Your design system should not belong to the UI vendor.&lt;/p&gt;

&lt;p&gt;It should belong to the product.&lt;/p&gt;

&lt;p&gt;That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;your naming,&lt;/li&gt;
&lt;li&gt;your semantics,&lt;/li&gt;
&lt;li&gt;your defaults,&lt;/li&gt;
&lt;li&gt;your UX patterns,&lt;/li&gt;
&lt;li&gt;your layout philosophy,&lt;/li&gt;
&lt;li&gt;your interaction rules.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not whatever the library authors decided.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;AppDangerButton&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is far more meaningful than:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ElButton&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"danger"&lt;/span&gt; &lt;span class="na"&gt;plain&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first expresses product semantics.&lt;/p&gt;

&lt;p&gt;The second expresses vendor implementation details.&lt;/p&gt;

&lt;p&gt;That distinction becomes critical at scale.&lt;/p&gt;




&lt;h1&gt;
  
  
  Default Configuration Reduces Massive Verbosity
&lt;/h1&gt;

&lt;p&gt;One underrated benefit of internal wrappers:&lt;/p&gt;

&lt;p&gt;They eliminate repetitive configuration.&lt;/p&gt;

&lt;p&gt;Without wrappers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ThirdPartyInput&lt;/span&gt;
    &lt;span class="na"&gt;size=&lt;/span&gt;&lt;span class="s"&gt;"small"&lt;/span&gt;
    &lt;span class="na"&gt;variant=&lt;/span&gt;&lt;span class="s"&gt;"filled"&lt;/span&gt;
    &lt;span class="na"&gt;density=&lt;/span&gt;&lt;span class="s"&gt;"compact"&lt;/span&gt;
    &lt;span class="na"&gt;clearable&lt;/span&gt;
    &lt;span class="na"&gt;rounded&lt;/span&gt;
    &lt;span class="na"&gt;theme=&lt;/span&gt;&lt;span class="s"&gt;"dark"&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repeated hundreds or thousands of times.&lt;/p&gt;

&lt;p&gt;With wrappers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;AppInput&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The wrapper already encodes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;project defaults,&lt;/li&gt;
&lt;li&gt;theme defaults,&lt;/li&gt;
&lt;li&gt;accessibility defaults,&lt;/li&gt;
&lt;li&gt;density rules,&lt;/li&gt;
&lt;li&gt;validation behavior,&lt;/li&gt;
&lt;li&gt;localization behavior,&lt;/li&gt;
&lt;li&gt;analytics integration,&lt;/li&gt;
&lt;li&gt;focus handling.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This massively reduces UI noise in application code.&lt;/p&gt;

&lt;p&gt;The application becomes more declarative.&lt;/p&gt;




&lt;h1&gt;
  
  
  Theme Propagation Becomes Centralized
&lt;/h1&gt;

&lt;p&gt;Third-party UI libraries usually bring their own theme systems.&lt;/p&gt;

&lt;p&gt;That becomes problematic when multiple libraries coexist.&lt;/p&gt;

&lt;p&gt;Each library wants:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;its own tokens,&lt;/li&gt;
&lt;li&gt;its own variables,&lt;/li&gt;
&lt;li&gt;its own dark mode implementation,&lt;/li&gt;
&lt;li&gt;its own color system.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your internal UI layer solves this.&lt;/p&gt;

&lt;p&gt;The application owns the real design tokens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--color-bg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--color-text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#111111&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--color-primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#3b82f6&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;Then wrapper components propagate these tokens into underlying libraries.&lt;/p&gt;

&lt;p&gt;Your product controls the visual system.&lt;/p&gt;

&lt;p&gt;Libraries adapt to &lt;em&gt;you&lt;/em&gt;, not the other way around.&lt;/p&gt;

&lt;p&gt;This is one of the cleanest ways to support:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;dark mode,&lt;/li&gt;
&lt;li&gt;white-labeling,&lt;/li&gt;
&lt;li&gt;custom themes,&lt;/li&gt;
&lt;li&gt;enterprise branding,&lt;/li&gt;
&lt;li&gt;runtime theme switching.&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Modern Browser APIs Make Custom UI More Realistic
&lt;/h1&gt;

&lt;p&gt;Five years ago, fully custom UI infrastructure was expensive.&lt;/p&gt;

&lt;p&gt;Today browsers provide much better primitives.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Popover API&lt;/li&gt;
&lt;li&gt;anchor positioning&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:has()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;CSS nesting&lt;/li&gt;
&lt;li&gt;container queries&lt;/li&gt;
&lt;li&gt;inert&lt;/li&gt;
&lt;li&gt;view transitions&lt;/li&gt;
&lt;li&gt;modern focus management&lt;/li&gt;
&lt;li&gt;top-layer behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Especially important are the newer overlay-related APIs.&lt;/p&gt;

&lt;p&gt;Historically, dialogs, dropdowns, popovers, tooltips, and context menus were extremely difficult because of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;z-index wars,&lt;/li&gt;
&lt;li&gt;focus traps,&lt;/li&gt;
&lt;li&gt;scroll locking,&lt;/li&gt;
&lt;li&gt;portal systems,&lt;/li&gt;
&lt;li&gt;positioning,&lt;/li&gt;
&lt;li&gt;stacking contexts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Modern browser primitives increasingly solve these problems natively.&lt;/p&gt;

&lt;p&gt;This changes the economics of UI architecture.&lt;/p&gt;

&lt;p&gt;Building internal abstractions is becoming cheaper and more maintainable.&lt;/p&gt;




&lt;h1&gt;
  
  
  UI Architecture Is More Than Form Controls
&lt;/h1&gt;

&lt;p&gt;Many teams think only about atomic components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;buttons,&lt;/li&gt;
&lt;li&gt;inputs,&lt;/li&gt;
&lt;li&gt;selects,&lt;/li&gt;
&lt;li&gt;checkboxes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the real value appears higher in the hierarchy.&lt;/p&gt;

&lt;p&gt;Your internal UI layer can orchestrate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;page layouts,&lt;/li&gt;
&lt;li&gt;entity editors,&lt;/li&gt;
&lt;li&gt;filter panels,&lt;/li&gt;
&lt;li&gt;data tables,&lt;/li&gt;
&lt;li&gt;toolbar systems,&lt;/li&gt;
&lt;li&gt;sidebars,&lt;/li&gt;
&lt;li&gt;navigation structures,&lt;/li&gt;
&lt;li&gt;CRUD workflows,&lt;/li&gt;
&lt;li&gt;master-detail layouts,&lt;/li&gt;
&lt;li&gt;save/cancel flows,&lt;/li&gt;
&lt;li&gt;loading orchestration,&lt;/li&gt;
&lt;li&gt;permissions handling.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;EntityPage&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;EntityToolbar&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;EntityFilters&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;EntityTable&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/EntityPage&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now your product develops consistent workflow patterns.&lt;/p&gt;

&lt;p&gt;Not just reusable buttons.&lt;/p&gt;

&lt;p&gt;This is where frontend architecture starts becoming a platform.&lt;/p&gt;




&lt;h1&gt;
  
  
  Internal UI Layers Improve Long-Term Stability
&lt;/h1&gt;

&lt;p&gt;Projects live longer than frontend trends.&lt;/p&gt;

&lt;p&gt;The average enterprise frontend outlives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;multiple framework trends,&lt;/li&gt;
&lt;li&gt;multiple design trends,&lt;/li&gt;
&lt;li&gt;multiple UI libraries,&lt;/li&gt;
&lt;li&gt;and sometimes even multiple frontend teams.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A dedicated UI layer creates isolation from ecosystem volatility.&lt;/p&gt;

&lt;p&gt;It reduces the blast radius of change.&lt;/p&gt;

&lt;p&gt;That is not premature abstraction.&lt;/p&gt;

&lt;p&gt;That is operational risk management.&lt;/p&gt;




&lt;h1&gt;
  
  
  This Applies Beyond Vue
&lt;/h1&gt;

&lt;p&gt;Vue examples are convenient because Vue component composition is extremely ergonomic.&lt;/p&gt;

&lt;p&gt;But the idea is framework-agnostic.&lt;/p&gt;

&lt;p&gt;The same architecture works in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React&lt;/li&gt;
&lt;li&gt;Svelte&lt;/li&gt;
&lt;li&gt;Angular&lt;/li&gt;
&lt;li&gt;Solid&lt;/li&gt;
&lt;li&gt;Web Components&lt;/li&gt;
&lt;li&gt;even server-rendered systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Any component-based frontend benefits from stable internal UI contracts.&lt;/p&gt;




&lt;h1&gt;
  
  
  A Practical Rule
&lt;/h1&gt;

&lt;p&gt;One of the first architectural tasks in a frontend project should be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Define the project's internal UI hierarchy before building product pages.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not after.&lt;/p&gt;

&lt;p&gt;Not during migration.&lt;/p&gt;

&lt;p&gt;Not after technical debt accumulates.&lt;/p&gt;

&lt;p&gt;At the beginning.&lt;/p&gt;

&lt;p&gt;Because once product code directly depends on third-party UI libraries, decoupling becomes expensive.&lt;/p&gt;




&lt;h1&gt;
  
  
  Potential Downsides
&lt;/h1&gt;

&lt;p&gt;This approach is not free.&lt;/p&gt;

&lt;p&gt;Possible drawbacks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;initial setup cost,&lt;/li&gt;
&lt;li&gt;additional maintenance,&lt;/li&gt;
&lt;li&gt;risk of over-abstraction,&lt;/li&gt;
&lt;li&gt;wrapper drift from underlying libraries,&lt;/li&gt;
&lt;li&gt;documentation burden,&lt;/li&gt;
&lt;li&gt;onboarding complexity,&lt;/li&gt;
&lt;li&gt;temptation to reinvent mature solutions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Poorly designed wrappers can absolutely become worse than direct usage.&lt;/p&gt;

&lt;p&gt;The goal is not to hide everything.&lt;/p&gt;

&lt;p&gt;The goal is to own the architectural boundary.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final Thought
&lt;/h1&gt;

&lt;p&gt;Frontend developers often discuss frameworks.&lt;/p&gt;

&lt;p&gt;But frameworks are transient.&lt;/p&gt;

&lt;p&gt;Architecture lasts much longer.&lt;/p&gt;

&lt;p&gt;A frontend codebase should not structurally depend on the internal API decisions of a third-party UI vendor.&lt;/p&gt;

&lt;p&gt;It should depend on its own UI language.&lt;/p&gt;

&lt;p&gt;That language becomes one of the most valuable assets in the entire frontend architecture.&lt;/p&gt;




&lt;p&gt;What additional advantages or disadvantages have you seen with internal UI layers and wrapper-based component systems?&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>frontend</category>
      <category>ui</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Stop Turning the Mobile Web Into a Second-Class Platform</title>
      <dc:creator>Alexander Nenashev</dc:creator>
      <pubDate>Sat, 16 May 2026 21:57:41 +0000</pubDate>
      <link>https://dev.to/alexander-nenashev/stop-turning-the-mobile-web-into-a-second-class-platform-16c0</link>
      <guid>https://dev.to/alexander-nenashev/stop-turning-the-mobile-web-into-a-second-class-platform-16c0</guid>
      <description>&lt;p&gt;There is a long-term trend in the software industry that I think developers should pay more attention to:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We are slowly replacing the open mobile web with isolated app containers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And I think this is a mistake.&lt;/p&gt;

&lt;p&gt;Not because native applications are inherently bad. Some categories absolutely need them: low-latency audio, advanced camera processing, heavy offline workloads, games, Bluetooth integrations, system-level services, etc.&lt;/p&gt;

&lt;p&gt;But a huge percentage of modern mobile apps are not really “native applications” anymore.&lt;/p&gt;

&lt;p&gt;They are portals to web content wrapped inside a native shell.&lt;/p&gt;

&lt;p&gt;And yet, by moving users from the browser into apps, we lose many of the properties that made the web powerful in the first place.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Mobile Browser Is More Powerful Than People Realize
&lt;/h2&gt;

&lt;p&gt;On desktop, most developers understand the value of the browser immediately:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tabs&lt;/li&gt;
&lt;li&gt;deep linking&lt;/li&gt;
&lt;li&gt;bookmarking&lt;/li&gt;
&lt;li&gt;extensions&lt;/li&gt;
&lt;li&gt;history&lt;/li&gt;
&lt;li&gt;multi-window workflows&lt;/li&gt;
&lt;li&gt;interoperability&lt;/li&gt;
&lt;li&gt;inspectability&lt;/li&gt;
&lt;li&gt;copyable URLs&lt;/li&gt;
&lt;li&gt;open standards&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But on mobile, many companies push users toward applications even when the actual product experience is fundamentally web-shaped.&lt;/p&gt;

&lt;p&gt;The result is fragmentation of workflows that the browser already solved decades ago.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Browser Tab Is a Better UX Primitive Than Most Mobile Apps
&lt;/h2&gt;

&lt;p&gt;This sounds controversial until you compare them directly.&lt;/p&gt;

&lt;p&gt;In a browser:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I can open 20 conversations in separate tabs.&lt;/li&gt;
&lt;li&gt;I can preserve reading state naturally.&lt;/li&gt;
&lt;li&gt;I can bookmark specific content.&lt;/li&gt;
&lt;li&gt;I can restore sessions.&lt;/li&gt;
&lt;li&gt;I can share exact URLs.&lt;/li&gt;
&lt;li&gt;I can use ad blockers.&lt;/li&gt;
&lt;li&gt;I can use browser extensions.&lt;/li&gt;
&lt;li&gt;I can search across tabs.&lt;/li&gt;
&lt;li&gt;I can keep temporary research contexts alive for weeks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In many mobile apps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;navigation state is fragile&lt;/li&gt;
&lt;li&gt;deep linking is inconsistent&lt;/li&gt;
&lt;li&gt;opening multiple contexts is awkward&lt;/li&gt;
&lt;li&gt;content is trapped inside proprietary navigation systems&lt;/li&gt;
&lt;li&gt;session management is opaque&lt;/li&gt;
&lt;li&gt;there is no universal multitasking model&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The browser already solved these problems with universal primitives.&lt;/p&gt;

&lt;p&gt;Apps often reinvent them poorly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Many Mobile Apps Are Just Embedded Websites
&lt;/h2&gt;

&lt;p&gt;A lot of modern applications are effectively:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React Native shells&lt;/li&gt;
&lt;li&gt;Electron-like architectures&lt;/li&gt;
&lt;li&gt;WebViews&lt;/li&gt;
&lt;li&gt;remote-rendered interfaces&lt;/li&gt;
&lt;li&gt;thin API clients around server-driven UI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And that is fine technically.&lt;/p&gt;

&lt;p&gt;But if the experience is fundamentally web-centric anyway, why are we abandoning the actual web platform?&lt;/p&gt;

&lt;p&gt;Especially when the browser gives users:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;portability&lt;/li&gt;
&lt;li&gt;inspectability&lt;/li&gt;
&lt;li&gt;interoperability&lt;/li&gt;
&lt;li&gt;lower installation friction&lt;/li&gt;
&lt;li&gt;better discoverability&lt;/li&gt;
&lt;li&gt;easier sharing&lt;/li&gt;
&lt;li&gt;less platform lock-in&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The “App-First” Strategy Hurts the Mobile Web
&lt;/h2&gt;

&lt;p&gt;This is the part that concerns me most.&lt;/p&gt;

&lt;p&gt;When companies prioritize native applications aggressively, something predictable happens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the mobile app becomes the primary platform&lt;/li&gt;
&lt;li&gt;the mobile website becomes “good enough”&lt;/li&gt;
&lt;li&gt;feature parity disappears&lt;/li&gt;
&lt;li&gt;browser support stagnates&lt;/li&gt;
&lt;li&gt;investment into web capabilities declines&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Eventually the mobile web experience degrades into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;limited functionality&lt;/li&gt;
&lt;li&gt;missing features&lt;/li&gt;
&lt;li&gt;constant “Open in App” interruptions&lt;/li&gt;
&lt;li&gt;intentionally degraded UX&lt;/li&gt;
&lt;li&gt;broken navigation&lt;/li&gt;
&lt;li&gt;artificial restrictions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This creates a feedback loop:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;weaker mobile web → more users forced into apps → even less incentive to improve the web&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is dangerous long term.&lt;/p&gt;




&lt;h2&gt;
  
  
  We Are Accidentally Weakening the Open Platform
&lt;/h2&gt;

&lt;p&gt;One of the biggest historical advantages of the web was openness.&lt;/p&gt;

&lt;p&gt;A browser is a universal runtime.&lt;/p&gt;

&lt;p&gt;A website can be indexed, linked, archived, shared, inspected, extended, and accessed without asking permission from a platform gatekeeper.&lt;/p&gt;

&lt;p&gt;Apps move the industry toward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;closed ecosystems&lt;/li&gt;
&lt;li&gt;proprietary distribution&lt;/li&gt;
&lt;li&gt;platform dependency&lt;/li&gt;
&lt;li&gt;duplicated implementations&lt;/li&gt;
&lt;li&gt;reduced interoperability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The web was never perfect, but it created an environment where small teams could ship globally accessible software instantly.&lt;/p&gt;

&lt;p&gt;That matters.&lt;/p&gt;

&lt;p&gt;Especially for developers.&lt;/p&gt;




&lt;h2&gt;
  
  
  Progressive Web Apps Were Supposed to Bridge This Gap
&lt;/h2&gt;

&lt;p&gt;PWAs are one of the most important ideas the industry produced in the last decade.&lt;/p&gt;

&lt;p&gt;The promise was compelling:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;installable web apps&lt;/li&gt;
&lt;li&gt;offline support&lt;/li&gt;
&lt;li&gt;push notifications&lt;/li&gt;
&lt;li&gt;background sync&lt;/li&gt;
&lt;li&gt;richer device APIs&lt;/li&gt;
&lt;li&gt;app-like UX without abandoning the web&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But mobile platforms still treat the browser as a second-class citizen in many areas.&lt;/p&gt;

&lt;p&gt;And this directly affects innovation.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Real Example: Voice Interfaces on Mobile Web
&lt;/h2&gt;

&lt;p&gt;I built a voice-command-oriented web application.&lt;/p&gt;

&lt;p&gt;On desktop Chrome, continuous speech recognition works surprisingly well. You can create fluid voice-driven workflows with near real-time interaction.&lt;/p&gt;

&lt;p&gt;But on mobile, the experience degrades dramatically.&lt;/p&gt;

&lt;p&gt;The browser often allows only short-lived microphone capture sessions for speech recognition workflows. Continuous recognition behavior is heavily restricted compared to desktop environments.&lt;/p&gt;

&lt;p&gt;As a result:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;recognition sessions restart constantly&lt;/li&gt;
&lt;li&gt;UX becomes fragmented&lt;/li&gt;
&lt;li&gt;latency increases&lt;/li&gt;
&lt;li&gt;conversational interaction becomes unreliable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Meanwhile native apps can often access far more persistent audio capabilities.&lt;/p&gt;

&lt;p&gt;This creates a structural imbalance:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;developers are pushed toward native apps not because the web model is fundamentally wrong, but because the platform capabilities are artificially constrained.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And when users stop using the mobile web, platform vendors have even less incentive to improve it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Browser Features Benefit Everyone
&lt;/h2&gt;

&lt;p&gt;The browser ecosystem creates positive externalities.&lt;/p&gt;

&lt;p&gt;When users rely on browsers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;browser vendors compete harder&lt;/li&gt;
&lt;li&gt;standards evolve faster&lt;/li&gt;
&lt;li&gt;accessibility improves&lt;/li&gt;
&lt;li&gt;performance tooling improves&lt;/li&gt;
&lt;li&gt;privacy tooling improves&lt;/li&gt;
&lt;li&gt;extension ecosystems grow&lt;/li&gt;
&lt;li&gt;cross-platform compatibility matters more&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The web encourages shared infrastructure.&lt;/p&gt;

&lt;p&gt;Apps encourage isolated silos.&lt;/p&gt;




&lt;h2&gt;
  
  
  Browsers Give Users More Control
&lt;/h2&gt;

&lt;p&gt;Permissions are usually:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;visible&lt;/li&gt;
&lt;li&gt;revocable&lt;/li&gt;
&lt;li&gt;origin-scoped&lt;/li&gt;
&lt;li&gt;standardized&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Users can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;clear site data&lt;/li&gt;
&lt;li&gt;inspect permissions&lt;/li&gt;
&lt;li&gt;block trackers&lt;/li&gt;
&lt;li&gt;isolate sessions&lt;/li&gt;
&lt;li&gt;use privacy extensions&lt;/li&gt;
&lt;li&gt;sandbox activity more predictably&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Native mobile apps often request much broader persistent access with significantly less transparency around internal behavior.&lt;/p&gt;

&lt;p&gt;Again, some applications genuinely need this access.&lt;/p&gt;

&lt;p&gt;Many do not.&lt;/p&gt;




&lt;h2&gt;
  
  
  “But Native Apps Are Faster”
&lt;/h2&gt;

&lt;p&gt;Sometimes yes.&lt;/p&gt;

&lt;p&gt;But this argument is increasingly overstated for many categories.&lt;/p&gt;

&lt;p&gt;Modern mobile browsers now support:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;hardware acceleration&lt;/li&gt;
&lt;li&gt;WebAssembly&lt;/li&gt;
&lt;li&gt;IndexedDB&lt;/li&gt;
&lt;li&gt;service workers&lt;/li&gt;
&lt;li&gt;advanced caching&lt;/li&gt;
&lt;li&gt;streaming APIs&lt;/li&gt;
&lt;li&gt;GPU APIs&lt;/li&gt;
&lt;li&gt;installable PWAs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For content-centric products, dashboards, social systems, productivity tools, knowledge platforms, AI interfaces, admin panels, marketplaces, and communication tools, the gap is often far smaller than people assume.&lt;/p&gt;

&lt;p&gt;In many cases, the biggest differences are organizational priorities rather than technical impossibilities.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Web Is Still the Most Important Software Platform Ever Created
&lt;/h2&gt;

&lt;p&gt;The web won because it was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;linkable&lt;/li&gt;
&lt;li&gt;composable&lt;/li&gt;
&lt;li&gt;open&lt;/li&gt;
&lt;li&gt;searchable&lt;/li&gt;
&lt;li&gt;decentralized&lt;/li&gt;
&lt;li&gt;low-friction&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We should be extremely careful about replacing those properties with ecosystems built around opaque application containers.&lt;/p&gt;

&lt;p&gt;Especially when many “apps” are already websites internally.&lt;/p&gt;




&lt;h2&gt;
  
  
  My Personal Rule
&lt;/h2&gt;

&lt;p&gt;If a service can function well as a mobile website, I increasingly prefer using the website over installing the app.&lt;/p&gt;

&lt;p&gt;Not because I hate apps.&lt;/p&gt;

&lt;p&gt;But because every time we choose the browser:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;we reinforce open standards&lt;/li&gt;
&lt;li&gt;we increase pressure for better browser APIs&lt;/li&gt;
&lt;li&gt;we support interoperable software&lt;/li&gt;
&lt;li&gt;we encourage better PWAs&lt;/li&gt;
&lt;li&gt;we reduce unnecessary platform fragmentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And over time, that shapes the direction of the ecosystem.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;I think developers underestimate how much collective user behavior influences platform evolution.&lt;/p&gt;

&lt;p&gt;If users continuously abandon the mobile web in favor of closed app ecosystems, companies and platform vendors will naturally invest less into browser capabilities.&lt;/p&gt;

&lt;p&gt;But if developers and users actively use and demand high-quality mobile web experiences, the opposite can happen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;better APIs&lt;/li&gt;
&lt;li&gt;stronger PWAs&lt;/li&gt;
&lt;li&gt;richer browser capabilities&lt;/li&gt;
&lt;li&gt;more powerful web runtimes&lt;/li&gt;
&lt;li&gt;less dependence on app stores&lt;/li&gt;
&lt;li&gt;more open software ecosystems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The mobile web does not become stronger automatically.&lt;/p&gt;

&lt;p&gt;It becomes stronger only if people continue using it.&lt;/p&gt;




&lt;p&gt;What do you think?&lt;/p&gt;

&lt;p&gt;Are mobile apps genuinely superior for most modern products, or are we underinvesting in the mobile web ecosystem?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>pwa</category>
      <category>mobile</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Avoid intermediate arrays (parsing strings) to make you Javascript fast</title>
      <dc:creator>Alexander Nenashev</dc:creator>
      <pubDate>Sat, 18 May 2024 16:42:30 +0000</pubDate>
      <link>https://dev.to/alexander-nenashev/avoid-intermediate-arrays-parsing-strings-to-make-you-javascript-fast-5beg</link>
      <guid>https://dev.to/alexander-nenashev/avoid-intermediate-arrays-parsing-strings-to-make-you-javascript-fast-5beg</guid>
      <description>&lt;p&gt;In the previous post there was argued why we should &lt;a href="https://dev.to/alexander-nenashev/avoid-intermediate-arrays-in-js-5bkg"&gt;avoid intermediate arrays&lt;/a&gt; for performance in cases like filter/map chains.&lt;/p&gt;

&lt;p&gt;I'd say &lt;code&gt;String#split()&lt;/code&gt; is heavily overused when parsing strings. A common scenario is to get the first word in a string. The first thought is to split a string into words and get the first one. Again, an intermediate array is created, worse, only the first element is used, the rest is just waste of space and time. Use &lt;code&gt;String#indexOf()&lt;/code&gt; and &lt;code&gt;String#slice()&lt;/code&gt; instead:&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;let&lt;/span&gt; &lt;span class="nx"&gt;$length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$length&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// @benchmark Array#split&lt;/span&gt;
&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// @benchmark String#indexOf&lt;/span&gt;
&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// @benchmark regex&lt;/span&gt;
&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;\S&lt;/span&gt;&lt;span class="sr"&gt;+/&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="s2"&gt;` Chrome/125
-----------------------------------------------------------------------------------------------
&amp;gt;                       n=1        |       n=10        |       n=100       |       n=1000      
String#indexOf   ■ 1.00x x100m 972 | ■ 1.00x x100m 959 | ■ 1.00x  x10m  94 |  ■ 1.00x x100m 967
regex              2.99x  x10m 291 |   3.06x  x10m 293 |   3.07x  x10m 289 |    2.96x  x10m 286
Array#split        6.49x  x10m 631 |  22.52x   x1m 216 | 165.96x x100k 156 | 1509.82x  x10k 146
----------------------------------------------------------------------------------------------- `&lt;/span&gt;
&lt;span class="s2"&gt;` Firefox/126
------------------------------------------------------------------------------------------------
&amp;gt;                       n=1        |       n=10        |       n=100       |       n=1000       
String#indexOf   ■ 1.00x x100m 105 | ■ 1.00x x100m 120 | ■ 1.00x x100m 108 |   ■ 1.00x x100m 109
regex             35.90x  x10m 377 |  27.83x  x10m 334 |  33.15x  x10m 358 |    32.11x  x10m 350
Array#split       39.05x  x10m 410 | 105.00x   x1m 126 | 944.44x x100k 102 | 10000.00x  x10k 109
------------------------------------------------------------------------------------------------ `&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://aleklabs.dev/js-benchmark/playground#H4sIAAAAAAAACm2Oyw6CMBBFf2WCJpRAeGwxGP0CFyytJlgGqEIx7SwwhH8XC3HFbiZzzr0zOqIv0UmdFgn2LaqaGsggOXAlemUIDOl5P2tdfMJK9x0bFyj90z4kUwDMg%2BwILqEh1wufvVTMBdebc7iKIjg9UImmK%2FRrydqZdyuJzw06tKOlr%2FFtQ8hJS1XvpCpxuFSr00qBLA5%2B%2F4XrxUZsNWqscVi8riDRsOjOee5Hts%2BZvsWGsk8EAQAA" rel="noopener noreferrer"&gt;Open in the playground&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>performance</category>
      <category>webdev</category>
      <category>ux</category>
    </item>
    <item>
      <title>Pre-allocate arrays to make Javascript fast</title>
      <dc:creator>Alexander Nenashev</dc:creator>
      <pubDate>Sat, 18 May 2024 14:05:11 +0000</pubDate>
      <link>https://dev.to/alexander-nenashev/always-pre-allocate-arrays-in-javascript-pfc</link>
      <guid>https://dev.to/alexander-nenashev/always-pre-allocate-arrays-in-javascript-pfc</guid>
      <description>&lt;p&gt;If you ever implemented a dynamic array in low level languages like C++ you allocate a chunk of memory for several elements at once, when there's no more space for new elements you allocate a new chunk. I guess this is valid and for Javascript arrays. Allocation of a memory on the heap is an expensive operation. &lt;/p&gt;

&lt;p&gt;Let's try in Chrome's console (several times, the output is the most frequent):&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;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usedJSHeapSize&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;arr&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;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;setTimeout&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;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usedJSHeapSize&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;heap allocated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;100&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;heap&lt;/span&gt; &lt;span class="nx"&gt;allocated&lt;/span&gt; &lt;span class="mi"&gt;1688&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usedJSHeapSize&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;arr&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;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;_000_000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;setTimeout&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;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usedJSHeapSize&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;heap allocated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;100&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;heap&lt;/span&gt; &lt;span class="nx"&gt;allocated&lt;/span&gt; &lt;span class="mi"&gt;4001684&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we see with 1_000_000 array length we have 3.8Mb allocated.&lt;/p&gt;

&lt;p&gt;Of course the reason behind the optimization could be different or composite, I'm not a developer of any current JS engines, but it seems that changing the length of an array is generally an expensive operation.&lt;/p&gt;

&lt;p&gt;Regardless of the actual JS implementation if we follow the rule:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When the final size of an array is known during creation of the array - allocate it:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&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;arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;knownSize&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This immediately boosts your Javascript speed:&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;let&lt;/span&gt; &lt;span class="nx"&gt;$length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&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;arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$length&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&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;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// @benchmark non pre-allocated&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&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;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="nx"&gt;result&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="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// @benchmark pre-allocated&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;for&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;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// @benchmark Array#map&lt;/span&gt;
&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="s2"&gt;` Chrome/125
-----------------------------------------------------------------------------------------------
&amp;gt;                         n=10       |      n=100       |      n=1000       |      n=10000     
pre-allocated       ■ 1.00x x10m  85 | ■ 1.00x x10m 669 | ■ 1.00x   x1m 474 | ■ 1.00x x100k 560
Array#map             1.69x x10m 144 |   1.79x  x1m 120 |   2.15x x100k 102 |   3.20x  x10k 179
non pre-allocated     3.22x x10m 274 |   3.57x  x1m 239 |   4.11x x100k 195 |   3.66x  x10k 205
----------------------------------------------------------------------------------------------- `&lt;/span&gt;
&lt;span class="s2"&gt;` Firefox/126
---------------------------------------------------------------------------------------------
&amp;gt;                         n=10       |      n=100      |      n=1000       |     n=10000     
pre-allocated       ■ 1.00x x10m 227 | ■ 1.00x x1m 162 | ■ 1.00x x100k 192 | ■ 1.00x x10k 208
non pre-allocated     1.44x x10m 327 |   1.45x x1m 235 |   1.16x x100k 222 |   1.53x x10k 319
Array#map             1.46x x10m 332 |   1.06x x1m 172 |   1.73x x100k 333 |   2.17x x10k 452
--------------------------------------------------------------------------------------------- `&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://aleklabs.dev/js-benchmark/playground#H4sIAAAAAAAACp2QwU7DMBBEf2UUerDVNi3XGiP4jjhCJnWJhbOONu4BRfl35LiIA5eKyx52Z2fe7lx18eyqUxVcwiY4%2Bkg9NB6PylAXaUqwzNB4ZbZf9YXjIOaiOv3Ilx3E2w5eQj%2FDS2XI0OGAl3dHXT9Y%2FgRFwshub0OInU3ubGg2BJQAdtM1JGg0rcrdS2SRaTw0jgoeT5mhLmEKfruV6zZum%2FV4nXphmRvfytVhyaUMlaHlL9AdMOvB4jdY%2Fout8S00Ctt9aGvuw2BHQ9l5sKPwt8dWyzfk7DtsrgEAAA%3D%3D" rel="noopener noreferrer"&gt;Open in the playground&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Keep in mind though that you create a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Indexed_collections#sparse_arrays" rel="noopener noreferrer"&gt;sparse array&lt;/a&gt; with &lt;code&gt;Array(size)&lt;/code&gt; so some methods like &lt;code&gt;map()&lt;/code&gt;, &lt;code&gt;forEach()&lt;/code&gt; wouldn't work as expected, but the iterators works fine like &lt;code&gt;[...arr]&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>performance</category>
      <category>webdev</category>
      <category>ux</category>
    </item>
    <item>
      <title>Avoid iterators/generators to make Javascript fast</title>
      <dc:creator>Alexander Nenashev</dc:creator>
      <pubDate>Fri, 17 May 2024 12:09:43 +0000</pubDate>
      <link>https://dev.to/alexander-nenashev/avoid-iteratorsgenerators-in-javascript-116n</link>
      <guid>https://dev.to/alexander-nenashev/avoid-iteratorsgenerators-in-javascript-116n</guid>
      <description>&lt;p&gt;If we look at the iterator protocol we'd notice it comes at a cost of creating &lt;code&gt;{value, done}&lt;/code&gt; objects, accessing their props, and calls of &lt;code&gt;next()&lt;/code&gt; (a function call is generally is an expensive operation). &lt;/p&gt;

&lt;p&gt;Given all the hurdles we have a quite inefficient tool to iterate our data. Moreover when we implement an iterator with a generator we suddenly notice that performance got even worse. I guess that's because of the cost of keeping a generator's scope and its state between calls.&lt;/p&gt;

&lt;p&gt;Don't get me wrong, iterators and generators are good abstractions, &lt;code&gt;for await&lt;/code&gt; is exceptionally amazing when dealing with async data and abstracting backend API calls, so generally used in business logic they bring benefits but when we descend into the realm of transforming data, generic data handling functions and algorithms we should care to avoid them when possible.&lt;/p&gt;

&lt;p&gt;Here's a benchmark of several ways to iterate an array and you see  the iterator and generator fail considerably. I'm not the only one complaining, I've seen several comments that generators' speed keeps people from using them widely.&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;Iter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;gen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&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;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;iter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&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;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&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="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;done&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;iter&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="nb"&gt;Symbol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;iterator&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;iter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;next&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="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;out&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="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nx"&gt;out&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;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;out&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;return&lt;/span&gt; &lt;span class="nx"&gt;iter&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;$chunk&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="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&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;i&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;$input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$input&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;iter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Iter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// @benchmark generator&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;e&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;iter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// @benchmark iterator&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;e&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;iter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;iter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// @benchmark for..of&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;e&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// @benchmark for&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;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// @benchmark forEach&lt;/span&gt;
&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;

&lt;span class="s2"&gt;` Chrome/125
-----------------------------------------------------------------------------------------
&amp;gt;                 n=10        |       n=100       |      n=1000       |      n=10000     
for           1.17x x100m 336 |   1.20x  x10m 354 |   1.01x   x1m 235 | ■ 1.00x x100k 218
for..of       1.19x x100m 340 |   1.21x  x10m 355 | ■ 1.00x   x1m 232 |   1.00x x100k 219
forEach     ■ 1.00x x100m 286 | ■ 1.00x  x10m 294 |   1.00x   x1m 233 |   4.54x  x10k  99
iterator     64.34x   x1m 184 |  13.71x   x1m 403 |  10.86x x100k 252 |  10.96x  x10k 239
generator    43.01x   x1m 123 |  36.05x x100k 106 |  45.69x  x10k 106 |  43.58x   x1k  95
----------------------------------------------------------------------------------------- `&lt;/span&gt;
&lt;span class="s2"&gt;` Firefox/126
-----------------------------------------------------------------------------------------
&amp;gt;                 n=10        |       n=100       |      n=1000       |      n=10000     
for         ■ 1.00x x100m 512 | ■ 1.00x  x10m 505 | ■ 1.00x   x1m 453 | ■ 1.00x x100k 449
forEach       1.62x x100m 828 |   1.48x  x10m 746 |   4.26x x100k 193 |   6.57x  x10k 295
for..of      11.07x  x10m 567 |   9.35x   x1m 472 |   9.98x x100k 452 |  10.11x  x10k 454
iterator     23.44x   x1m 120 |  10.93x   x1m 552 |  11.24x x100k 509 |  10.71x  x10k 481
generator    82.62x   x1m 423 |  64.75x x100k 327 |  66.67x  x10k 302 |  68.37x   x1k 307
----------------------------------------------------------------------------------------- `&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://aleklabs.dev/js-benchmark/playground#H4sIAAAAAAAACoVSwW6jMBD9ladoD7BBtHuFddUeetjzHkNUuWRIrDrjamLajSL%2BvbINNNUi9QKaN%2B8Nbx5zWbVuR6tq1Vp9OuGPJ8GlYeDnnjjTInkqgc4JMkseBgq3NQx%2BQ4uUlnjvDzXMej1zgbMhuwv9jdnWCRzCKz6MJ%2Fky%2B3NsqlvHJw%2FXeyhc8KZtTxW4t7bAzjFV6LQ9EYYv9DA18CcLm7%2Fn47OzZcC1d7KtkOVQd5FYTCymf35qzFLAdMgMlFJXO%2BbBURkMQMFLT0UEoj2o6K%2F%2BnEDB4XU%2FhrFeb684Qr4XDqwZHMawRmBkBMv1mN%2FQcMNp5R%2FtoecXqNH%2Fg4g%2Bl524Y3ZJliv8uh0KZE8FTNo9r2ex4deY8GZbBHNQIzQzxkSZ3uNh1OHDNze4fyZuD0ctL9gTp3Ab7pxkSUZwXdSW0w3lC9Lptywr5xNZknZOytJ1%2FykDf5meqN9d73yuCxMedXtoOEjGIqN4MkNer4YP3LYJCEYDAAA%3D" rel="noopener noreferrer"&gt;Open in the playground&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One would argue that &lt;code&gt;for..of&lt;/code&gt; in Chrome performs nice despite of using an array's iterator but that's an optimization of using a native iterator. Firefox is generally more honest in benchmarks if we want generic evaluation of Javascript code, but about that is in the next post.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>performance</category>
      <category>webdev</category>
      <category>ux</category>
    </item>
    <item>
      <title>Avoid intermediate arrays (filter/map) to make Javascript fast</title>
      <dc:creator>Alexander Nenashev</dc:creator>
      <pubDate>Wed, 15 May 2024 08:21:51 +0000</pubDate>
      <link>https://dev.to/alexander-nenashev/avoid-intermediate-arrays-in-js-5bkg</link>
      <guid>https://dev.to/alexander-nenashev/avoid-intermediate-arrays-in-js-5bkg</guid>
      <description>&lt;p&gt;If you aim to write fast Javascript avoid intermediate arrays when transforming data or making an algorithm. This could happen both with imperative and functional code. Always ask yourself a question while writing code - are there any intermediate arrays I could avoid.&lt;/p&gt;

&lt;p&gt;Often I see a common pattern &lt;code&gt;filter + map&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;arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&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="na"&gt;num&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()}));&lt;/span&gt;

&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sometimes the chain is quite long, hosting several filter/map/sort in various configurations.&lt;/p&gt;

&lt;p&gt;Replace the chain with &lt;code&gt;Array#reduce()&lt;/code&gt; which in mostly all cases will give you better performance over any chain of other methods, since you remove the array created by &lt;code&gt;Array#filter()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;r&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="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/tracygjg"&gt;@tracygjg&lt;/a&gt; asked in a comment why not to use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;n&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;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;r&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;Thank &lt;a class="mentioned-user" href="https://dev.to/tracygjg"&gt;@tracygjg&lt;/a&gt; for this one, that's another great example where you create an intermediate array, moreover an array is created for every element in the result array! &lt;/p&gt;

&lt;p&gt;So basically each time we want to add an element to the result, a bigger array is created thus the bigger the source array the slower the solution.&lt;/p&gt;

&lt;p&gt;Moreover the spread syntax is used which should be avoided too if you want a performant solution (my next post is about that). Let's benchmark all the 3 options:&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;$chunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&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="na"&gt;num&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&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;$input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt; 

&lt;span class="c1"&gt;// @benchmark filter + map&lt;/span&gt;
&lt;span class="nx"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// @benchmark reduce&lt;/span&gt;
&lt;span class="nx"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;r&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="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

&lt;span class="c1"&gt;// @benchmark Tracy Gilmore&lt;/span&gt;
&lt;span class="nx"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;n&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;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

&lt;span class="s2"&gt;` Chrome/124
-------------------------------------------------------------------------------------------
&amp;gt;                     n=10       |       n=100       |      n=1000       |     n=10000     
reduce          ■ 1.00x x10m 355 | ■ 1.00x   x1m 155 | ■ 1.00x x100k 263 | ■ 1.00x x10k 431
filter + map      1.88x x10m 669 |   1.74x   x1m 269 |   1.41x x100k 370 |   1.55x  x1k  67
Tracy Gilmore     3.32x  x1m 118 |  20.13x x100k 312 | 196.20x   x1k 516 | 893.27x  x10 385
------------------------------------------------------------------------------------------- `&lt;/span&gt;
&lt;span class="s2"&gt;` Firefox/126
-------------------------------------------------------------------------------------------
&amp;gt;                     n=10       |       n=100       |      n=1000       |     n=10000     
reduce          ■ 1.00x x10m 647 | ■ 1.00x   x1m 433 | ■ 1.00x x100k 390 | ■ 1.00x x10k 470
filter + map      1.41x  x1m  91 |   1.27x x100k  55 |   2.51x  x10k  98 |   1.32x x10k 620
Tracy Gilmore     1.90x  x1m 123 |   9.70x  x10k  42 | 175.64x   x1k 685 | 359.57x  x10 169
------------------------------------------------------------------------------------------- `&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://aleklabs.dev/js-benchmark/playground#H4sIAAAAAAAACn3OMU%2FDMBCG4b%2FyKUKVDZELSCyp0paJiY0tyWAcF0eNL9HVHqoo%2Fx1FBlohxOi70%2FN6yszQ2qzIzECngBvjIh1R4plZn9WBBy%2Bm3tJHcAUe7uccQqLcQkwUfYFXHZxiTe3ghZyl3NT05XQ0xoASVbNBTTWt19i%2FWzLOaz7i0PXBMu7g9VhTOlZpKGjxSVH02EI9SeX1eD28xePS%2BU2ybaOxP1h6CsE5KP34QmK1Aqsxnpy4kDlY5qiav%2Bw31uaMl673A%2F%2BTuCrsUCmlls13oEEBToFs%2FgTMS4rRdgEAAA%3D%3D" rel="noopener noreferrer"&gt;Open in the playground&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see with 10000 array items we could have a hundreds of times slower solution if we don't care enough about avoiding intermediate arrays.&lt;/p&gt;

&lt;p&gt;In the next post we will discuss how to avoid intermediate arrays while &lt;a href="https://dev.to/alexander-nenashev/avoid-intermediate-arrays-parsing-strings-to-make-you-javascript-fast-5beg"&gt;parsing strings&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>performance</category>
      <category>webdev</category>
      <category>ux</category>
    </item>
    <item>
      <title>Every millisecond matters in Javascript</title>
      <dc:creator>Alexander Nenashev</dc:creator>
      <pubDate>Tue, 14 May 2024 11:05:51 +0000</pubDate>
      <link>https://dev.to/alexander-nenashev/every-millisecond-matters-in-javascript-13ab</link>
      <guid>https://dev.to/alexander-nenashev/every-millisecond-matters-in-javascript-13ab</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Premature optimization is the root of all evil -- Donald Knuth&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In light of modern trends of increasing complexity of modern JS frameworks, high end developer hardware and the premature optimization mantra we ended up with some performance problems with modern web and especially modern mobile web (deserves a separate article). &lt;/p&gt;

&lt;p&gt;Probably you can't comfortably use modern web sites on a 5 year old mid/low budget device, at least the most people don't expect to upgrade their PCs used just for web surfing every year. I've ended up buying a high end android phone just to use social sites comfortably. &lt;/p&gt;

&lt;p&gt;One could tell that a user couldn't tell a difference between 1 and 5 ms, but given the fact we could have hundreds of operations on page or data loading, all these small overheads lead to a noticeable performance hit and poor user experience.&lt;/p&gt;

&lt;p&gt;So I'd say that every millisecond matters in Javascript. Premature optimization is bad when you spend significant amount of time on a code piece that is thrown away later due refactoring. But what if you could write highly performant Javascript code from the beginning? You just don't need to optimize later and the whole premature optimization thing isn't relevant anymore. So learn how to write fast Javascript, I believe it will pay off.&lt;/p&gt;

&lt;p&gt;In the next posts that would form a series I will be collecting tips how to write fast Javascript.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>ux</category>
      <category>performance</category>
    </item>
    <item>
      <title>Conditional wrap component in Vue 3 pt.3</title>
      <dc:creator>Alexander Nenashev</dc:creator>
      <pubDate>Fri, 10 May 2024 17:09:41 +0000</pubDate>
      <link>https://dev.to/alexander-nenashev/conditional-wrap-component-in-vue-3-pt3-8dp</link>
      <guid>https://dev.to/alexander-nenashev/conditional-wrap-component-in-vue-3-pt3-8dp</guid>
      <description>&lt;p&gt;Previously we observed 2 options to create a conditional wrap component in Vue 3. Now it's time for the most complex one which exploits vnodes in a deeper way.&lt;/p&gt;

&lt;p&gt;On the client side it's pretty ordinary with the wrapper provided in a &lt;code&gt;wrapper&lt;/code&gt; slot and the wrapped content in the default one. But we do some magic inside the wrap component:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We collect all leaf vnodes in the wrapper slot's vnode tree&lt;/li&gt;
&lt;li&gt;We inject our content into the leaves like they'd support a default slot. For that we should set &lt;code&gt;shapeFlag&lt;/code&gt; of the leaves as &lt;code&gt;ShapeFlags.ARRAY_CHILDREN | ShapeFlags.ELEMENT&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://play.vuejs.org/#eNp9VG1v2zYQ/is39UNswKbWrdgC1WmSdh6WIcuKNGhRVEVBS5TERiIJknJcuP7vPb5ItoO238i7547PPby7bXKpFFn3LMmShSk0VxYMs716kQtFNROWlLLoO3ew3LYMzuDknaZKMQ0P3DZgWmnNyfNc5IJ3SmoLW9CsmkEDO6i07OAE0ztAdLvo6CCpu4TgQgpjgZuQvMR3MMukoq1hUw9YpIEfMsOLZZ1qqWV4A1g8uJwZN3N3wGhvRfuAgifBoaMDXSVfQ9FSY87yJDrzZHQ/AnAhmI7JHWyRonef6+C2SI+YocFJGXBXJx1EggM4OBepM4fjqrdWCrgoWl7cu6cPFPllvCCHO1nX+B+R1CINgZjkiAJejf2COFPIIAyJEVv33IoW97WWvSjnhWylzuDJKa1QbwBFy5KLOoOnf6iNt6ykLhlCnqoNGNnyEmpNvxy45pqWvDcZPIsRHdU1FxmcYsSv3826Q0JH6v6IVlWd/igBdoYrEWtLZonFQkXFa/LZSIFd7fPlSSE7xVum/1eWY6flSQbe43y0beXDv95mdc9mg71oWHH/Hftns3G2PHmtmWF6zfJk9FmsmNngXr65YRs8j85Oln2L6J84bxkq2zuOAfYSNUDaBzjP9srPEupwZ5Yby4QZinJEHXLn8XmCs/fqJ6Xv6f5Onvk4VBRVdG2GCqJ+41C/aahif7e0NsNg58kFpk9Ng4sCO3I/4lscHCnY2xtZshGLUI8ZRr3iorxmdM0MdvZkLRA7g2I1hbMX4Jh4Cyka3pa4h85Jy0SN++bcOx+7SSX1khZNyONS7NMf5J4CaurDi1UwT3PhObGNZ16yivYtkutF4dTy22qyHeduN4Ot33i7KWroUvEKJvu5nOLWsr0WYS2SmG4SNhi+6iuPnY5lB1S8T6akw8fGCvYaRqouCxwWth3Kz4acyG+Mj58chMJPCr+Hr+5/klze3l6+//Tqn6vrv26XN/D10Le8Xv63vLnzj0L8oVFwOD8/gw8fp0T1ppkQQh4VHLjuhsKjKpFlGNtc+C5TWirXAtv98s/gpZQtowJ2zwG7MbTVHNV5NNXBcdzSRz3pRqixVpksTXuh7muCiyA9gFxg35Pf/sQNbmw0EWa6+SrMHb6XJ+Nc7L4BjTFsmA==" rel="noopener noreferrer"&gt;See on Vue SFC Playground&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ShapeFlags&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;@vue/shared&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cloneVNode&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;vue&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;findLeaves&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vnode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cb&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;vnode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; 
    &lt;span class="nx"&gt;vnode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vnode&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;findLeaves&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vnode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; 
    &lt;span class="nf"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vnode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Wrap&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;isWrapped&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;slots&lt;/span&gt;&lt;span class="p"&gt;}){&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isWrapped&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;slots&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&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;wrapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;slots&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vnode&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;cloneVNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vnode&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="nf"&gt;findLeaves&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;vnode&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;vnode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shapeFlag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ShapeFlags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ARRAY_CHILDREN&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;ShapeFlags&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="nx"&gt;vnode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="o"&gt;??=&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="nx"&gt;slots&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;Wrap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;isWrapped&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Usage:&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;wrap&lt;/span&gt; &lt;span class="na"&gt;:is-wrapped&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;#wrapper&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;"wrapper"&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;"inner-wrapper"&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;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
      I'm wrapped
    &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/wrap&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you see the DX is pretty good, with full ability to define our wrapper with any nesting fully inside a template. &lt;/p&gt;

&lt;p&gt;Now you have a choice to select a wrap component from 3 options or probably combine them into one ultimate component.&lt;/p&gt;

</description>
      <category>vue</category>
      <category>frontend</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Conditional wrap component in Vue 3 pt.2</title>
      <dc:creator>Alexander Nenashev</dc:creator>
      <pubDate>Fri, 10 May 2024 14:14:32 +0000</pubDate>
      <link>https://dev.to/alexander-nenashev/conditional-wrap-component-in-vue-3-pt2-aco</link>
      <guid>https://dev.to/alexander-nenashev/conditional-wrap-component-in-vue-3-pt2-aco</guid>
      <description>&lt;p&gt;In the previous post we create a pretty idiomatic component that would work well for simple cases. Let's explore more options. What can be done to present our wrapper as a template as more DX friendly? Of course we could compile it runtime, but for a simple 1 level wrapper we could use the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://play.vuejs.org/#eNp9U01v2zAM/SucdkgCJPaKFUPhpt3WoYcO2AfWArv4okiyq0aWBElOXRj+76Vk5+NQ9EbxPZKPFNmT79Zmu1aQgqw9c9IG8CK09rrUljqhQ8YNa5toBBmUgCuY3cvGovXsqLXCzS5LXWp0GRegBycqGKBypoEZ5o3ohP1H/gRkeXyMkcxoH0D66LGCYwFMMa+o8mKRCOt8FIaS8BEEFqdB4AtgHTVAIf1qFMOTF/1c7oAp6v1VSSadJZlAhGN7owlwN2umVvgBz/eEdY6ZxlJ5JI3mpg3BaPjGlGRbrHAq/sPhgQUfTF0fJ7XOx0BMss5P+sCnDy/I88yMPWRTRB/LbSjb1s60mq+YUcYV8PGCVjgaAEs5l7ou4OyL7ZJnYxwXSDmzHXijJIfa0ZcTaOUol60v4HyKaKirpS7gAiM+oWdIE496UAhZkoCqdCXr7MkbjWuSNJWEmcZKJdwfGyT+YEkKSEjEqFLm+WfyBdeK5d7PHgXbvuF/8l30leSvE164nSjJAQsoT4QRvr3/LTq0D2BjeKuQ/Q74T+AY2qhxpN3gHFH2CS+pvUs7irN88LddENrvm4pCI3NI/JLgTv94p/Wj3M/ZeYrDieIU407gBHF+okvXwEVFWxWgajWLadJ5zPvD9gxL6L0ywQ+LlNzhVTp9vJOvMJZMnGzKNl8A9viWP6tw2X5hhZ02HK/4GpKRsUepON55PDWUWuok1DpjPS5zf6xXwI0xSlANwyUZXgFQ72xE" rel="noopener noreferrer"&gt;See on Vue SFC Playground&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Wrap&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;isWrapped&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;slots&lt;/span&gt;&lt;span class="p"&gt;}){&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;isWrapped&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; 
    &lt;span class="nx"&gt;slots&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; 
    &lt;span class="nx"&gt;slots&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;flatMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vnode&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;vnode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;Wrap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;isWrapped&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Usage:&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;wrap&lt;/span&gt; &lt;span class="na"&gt;:is-wrapped&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;"wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
        I'm wrapped
      &lt;span class="nt"&gt;&amp;lt;/p&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;/wrap&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes, it looks non idiomatic, also it generates the wrapper's vnode every time even it's discarded, but it was an interesting case to explore for better DX.&lt;/p&gt;

&lt;p&gt;In the next post we'll explore a more idiomatic option with using 2 slots for the content and wrapper.&lt;/p&gt;

</description>
      <category>vue</category>
      <category>frontend</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Conditional wrap component in Vue 3</title>
      <dc:creator>Alexander Nenashev</dc:creator>
      <pubDate>Fri, 10 May 2024 08:45:37 +0000</pubDate>
      <link>https://dev.to/alexander-nenashev/creating-a-conditional-wrap-component-in-vue-3-18ml</link>
      <guid>https://dev.to/alexander-nenashev/creating-a-conditional-wrap-component-in-vue-3-18ml</guid>
      <description>&lt;p&gt;Often it's needed to wrap some part of a template in a wrapper &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; for example. It seems easy using &lt;code&gt;v-if&lt;/code&gt; but we have a duplication we'd like to avoid:&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;v-if=&lt;/span&gt;&lt;span class="s"&gt;"isWrapped"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- some big chunk of markup --&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;template&lt;/span&gt; &lt;span class="na"&gt;v-else&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- the same big chunk --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's explore our options how we can solve this task without the duplication. Let's call our component &lt;code&gt;&amp;lt;wrap&amp;gt;&lt;/code&gt; and our component that wraps the content a wrapper. Our wrap component is stateless so let's make it functional.&lt;/p&gt;

&lt;p&gt;Our first option would be simple. We support a wrapper component and its props:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://play.vuejs.org/#eNp9U8tu2zAQ/JUtC0Q24EgNGhSBIveJHNJDWzQBeqh6oClKZiyRBEk5Cgz9e5cU/WhQ5Lacmd0dLpc78knrdNtzkpPCMiO0A8tdr9+XUlPDpUsrxfrOB064lsMSkjvRaYweDdWam+S6lKVESBkHOzC8hhFqozpIsK5nI/cL9ZFIM3+YMpmS1oGwHtG8wgZYYlbT1vJ5EBTZZAwt4cFxbE4dxxNA4T1AHp0sS3Isc3YGSSW2SUmAtdRaJKOsJCEXs/0tfQBwm3TxPlXksoksMg9P4ap3Tkn4yFrBNv80W8KrwwHL36umOQ6oyKZELFJkJ/bxaN0T6ixTmIdIGjN2vt2Ksk1jVC+rc6ZaZXJ4fUVrnAiAplUlZJPDxTs9BGSlTMVRcqEHsKoVFTSGPp1Q54ZWorc5XMaMjppGyByuMOMNImMYtPeDRsiCOHQla9GkD1ZJ3I7gqSRMdVq03HzXTuDDlSSHwHiOtq16/BowZ3q+2ONszdnmP/iDHTxWkh+GW262vCQHzqE97ib65u4bHzA+kJ2q+hbVL5A/OY6h9x4n2WecI9o+0QW3t2E1cZb39mZwXNr9pbxRrxyDviS4yl9euPrR7tv0MuThRHGKfidwgji/wwdZP/8efAhMxWvatw7qXjLfIPyX2S6uxLiAnW2Vswugzhk7zkNvg3/VyP2mwQdYz2IcdQsIWWmsPpvPIX8OTa9fymBWG6UtLvTvZP+//1yT8S+HH2nF" rel="noopener noreferrer"&gt;See on Vue SFC Playground&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Wrap&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;slots&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;}){&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;h&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;slots&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;slots&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;Wrap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wrapper&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Usage:&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;wrap&lt;/span&gt; &lt;span class="na"&gt;:wrapper=&lt;/span&gt;&lt;span class="s"&gt;"isWrapped &amp;amp;&amp;amp; 'div'"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
      I'm wrapped
    &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/wrap&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result is pretty sufficient, you can use any wrapper component with a default slot. But a problem here is that for more complex cases like having multiple nested wrappers we should define the wrapper outside our markup since having a functional wrapper component inside &lt;code&gt;:wrapper&lt;/code&gt; property looks pretty messy. In the next posts of this series we'll explore other options of defining our conditional wrap component.&lt;/p&gt;

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