<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en" xmlns:fh="http://purl.org/syndication/history/1.0"><title>Álvaro Cuesta | All</title><id>https://alvaro.cuesta.dev/atom.xml</id><updated>2026-04-12T00:00:00Z</updated><subtitle>Álvaro Cuesta's personal website</subtitle><generator version="1.0.0">Xenon SSG</generator><link href="https://alvaro.cuesta.dev/" rel="alternate" type="text/html"/><link href="https://alvaro.cuesta.dev/atom.xml" rel="self" type="application/atom+xml; charset=utf-8"/><author><name>Álvaro Cuesta</name><uri>https://alvaro.cuesta.dev</uri></author><entry><title>Why I built my own timeline</title><id>https://alvaro.cuesta.dev/blog/why-i-built-my-own-timeline/</id><published>2026-04-12T00:00:00Z</published><updated>2026-04-12T00:00:00Z</updated><link href="https://alvaro.cuesta.dev/blog/why-i-built-my-own-timeline/" rel="alternate" type="text/html"/><content type="html">&lt;p&gt;I recently launched my &lt;a href="https://alvaro.cuesta.dev/timeline/"&gt;timeline&lt;/a&gt; or, as we used to call it, my &lt;em&gt;microblog&lt;/em&gt;. It is not all that different from &lt;del&gt;Twitter&lt;/del&gt; X or Mastodon, which raises an obvious question: why build this instead of using an existing social media platform?&lt;/p&gt;
&lt;p&gt;It was not really a conscious choice so much as something that... happened.&lt;/p&gt;

&lt;h3 id="making-my-personal-notes-public" class="autolink-heading"&gt;Making my personal notes public&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/why-i-built-my-own-timeline/#making-my-personal-notes-public"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I have been building my own website for a while now (with Xenon SSG&lt;sup&gt;&lt;a href="https://alvaro.cuesta.dev/blog/why-i-built-my-own-timeline/#user-content-fn-xenon-ssg" id="user-content-fnref-xenon-ssg" data-footnote-ref="true" aria-describedby="footnote-label"&gt;1&lt;/a&gt;&lt;/sup&gt;), and I wanted to have a place where I could share my thoughts and updates in a more casual way, without the pressure of writing a full blog post.&lt;/p&gt;
&lt;p&gt;My goal was clear: &lt;strong&gt;I wanted a ledger of my thoughts, ideas, and updates&lt;/strong&gt;. Nothing more. Nothing less. But... why not share it with the world in the process?&lt;/p&gt;
&lt;p&gt;Since I had already done a lot of work on the &lt;a href="https://alvaro.cuesta.dev/blog/"&gt;blog&lt;/a&gt; system, adding a new section for &lt;a href="https://alvaro.cuesta.dev/timeline/"&gt;my timeline&lt;/a&gt; was not a big deal. But it is still more work than posting on an established platform. So, is it worth it?&lt;/p&gt;
&lt;h3 id="asocial-media" class="autolink-heading"&gt;Asocial media&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/why-i-built-my-own-timeline/#asocial-media"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;&quot;Why not Twitter&quot;&lt;/em&gt; may be obvious to some, but Mastodon or BlueSky are often framed as the alternative. I dislike both, though not only for the reasons you might expect (censorship, moderation, addictive design, etc.).&lt;/p&gt;
&lt;p&gt;I think &lt;strong&gt;the &lt;em&gt;social&lt;/em&gt; angle fundamentally taints the experience&lt;/strong&gt;. Followers, likes, retweets, and all that jazz create a dynamic that is not conducive to genuine self-expression. They create a feedback loop where you are constantly trying to optimize for engagement rather than simply sharing your thoughts and updates. &lt;em&gt;Gamification&lt;/em&gt; at its finest: say what you are supposed to say, and earn those &lt;em&gt;juicy Internet Points™&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Social&lt;/em&gt; is not inherently bad, and you might think you are immune to it, but humans interact with websites the way they are designed to be used, just as we behave in public the way we are expected to. We play a role, and we do so subconsciously. If you give us a platform designed for social interaction, we will use it socially. If you give us a platform designed for reflection, we will reflect on the ideas presented.&lt;/p&gt;
&lt;h3 id="friction-is-good" class="autolink-heading"&gt;Friction is good&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/why-i-built-my-own-timeline/#friction-is-good"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Social media platforms are designed to be as frictionless as possible, to keep you engaged and coming back for more. That only exacerbates the &lt;em&gt;social&lt;/em&gt; problem. It becomes too easy to react without giving ideas the thought they deserve. You do not &lt;em&gt;need&lt;/em&gt; to react, but you are absolutely &lt;em&gt;encouraged&lt;/em&gt; to do so.&lt;/p&gt;
&lt;p&gt;I believe that a little friction can be a good thing. It gives us a moment to pause and reflect before we react. It allows us to think about what we want to say rather than blurting out the first thing that comes to mind. Limitations breed creativity, and &lt;strong&gt;friction breeds quality&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In a world flooded with information and constant updates, having a space that encourages thoughtful reflection can be refreshing. It can help us to slow down and appreciate the content we are consuming, rather than just mindlessly scrolling through an endless feed.&lt;/p&gt;
&lt;p&gt;And I built my &lt;a href="https://alvaro.cuesta.dev/timeline/"&gt;timeline&lt;/a&gt; with that in mind. It is easy for me to post, but not &lt;em&gt;too easy&lt;/em&gt;.&lt;/p&gt;
&lt;h3 id="my-content-is-mine" class="autolink-heading"&gt;My content is mine&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/why-i-built-my-own-timeline/#my-content-is-mine"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;...and I am obsessed with my data.&lt;/p&gt;
&lt;p&gt;My mind is busy (well, whose isn&amp;#x27;t?) Over time, I built systems to keep myself organized. As a software engineer, I naturally gravitate toward Git&lt;sup&gt;&lt;a href="https://alvaro.cuesta.dev/blog/why-i-built-my-own-timeline/#user-content-fn-git" id="user-content-fnref-git" data-footnote-ref="true" aria-describedby="footnote-label"&gt;2&lt;/a&gt;&lt;/sup&gt; as a platform. It has become my second brain: a place where I store notes, ideas, and updates, where I can look back and see how my thoughts evolve over time.&lt;/p&gt;
&lt;p&gt;So it has always felt wrong to keep part of my thoughts and updates on a platform I do not control. &lt;strong&gt;I want all my content in one place&lt;/strong&gt;, where I can access it, edit it, and share it as I see fit. I do not want to be at the mercy of algorithms or platforms that can change the rules at any time, or worry about my content being deleted, censored, or lost.&lt;/p&gt;
&lt;p&gt;I have been migrating more and more of my infrastructure and workflows to Git repositories. Deploying a new domain is as simple as changing a few lines in a config file and pushing to GitHub, and automated workflows handle the rest. I can easily back up my content, and I have a clear history of every update. It is a system that works well for me.&lt;/p&gt;
&lt;p&gt;So it felt natural to build my blog and my timeline on top of it. All the &lt;a href="https://github.com/alvaro-cuesta/alvaro.cuesta.dev/tree/master/alvaro.cuesta.dev/blog"&gt;source files for my blog&lt;/a&gt; and &lt;a href="https://github.com/alvaro-cuesta/alvaro.cuesta.dev/tree/master/alvaro.cuesta.dev/microblog"&gt;my timeline&lt;/a&gt; are just Markdown files in a Git repository. I can edit them, organize them, and publish them from GitHub&amp;#x27;s web UI or my local Git client.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&quot;But isn&amp;#x27;t GitHub just another established platform?&quot;&lt;/em&gt; Well, yes, but Git itself gives me an exit. I can choose to store my data on GitHub and publish it on Amazon&amp;#x27;s servers, and if GitHub ever becomes a problem, I can migrate to another Git hosting service or even self-host it. That is the nice thing about Git being a distributed version control system: the repositories on my computer are just as valid as the ones on GitHub&amp;#x27;s servers.&lt;/p&gt;
&lt;h3 id="the-indieweb" class="autolink-heading"&gt;The IndieWeb&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/why-i-built-my-own-timeline/#the-indieweb"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Turns out that my goals are not very different from the IndieWeb movement:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The IndieWeb is a people-focused alternative to the “corporate web”.&lt;/p&gt;
&lt;p&gt;We are a community of independent and personal websites based on the principles of: owning your domain and using it as your primary online identity, publishing on your own site first (optionally elsewhere), and owning your content.&lt;/p&gt;
&lt;p&gt;— &lt;a href="https://indieweb.org/"&gt;indieweb.org&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That framing resonates with me because it treats a personal website not as a portfolio or a business card, but as an actual place to live online. A place that can be messy, opinionated, evolving, and fully yours. Not every thought needs to be optimized for reach. Not every update needs to be packaged for a feed algorithm. Sometimes you just want a small corner of the web that answers to you.&lt;/p&gt;
&lt;p&gt;That is also why the IndieWeb idea of publishing on your own site first feels so right to me. I want the URL to be mine, the content to be mine, and the surrounding context to be mine too. No algorithmic timeline, no engagement bait, no platform reshaping what the post is supposed to be. Just a home for the most precious thing I have and what makes me, &lt;em&gt;me&lt;/em&gt;: my thoughts and ideas.&lt;/p&gt;
&lt;p&gt;My timeline is a tiny expression of that. It is not an attempt to build the next social network. Quite the opposite: it is an attempt to have less network and more personal space. A simpler, quieter, more durable way to publish small things without handing them over to platforms whose incentives do not match mine.&lt;/p&gt;
&lt;p&gt;And I hope you enjoy it.&lt;/p&gt;

&lt;section data-footnotes="true" class="footnotes"&gt;&lt;h3 class="autolink-heading" id="footnote-label"&gt;Footnotes&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/why-i-built-my-own-timeline/#footnote-label"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li id="user-content-fn-xenon-ssg"&gt;
&lt;p&gt;&lt;a href="https://github.com/alvaro-cuesta/alvaro.cuesta.dev/"&gt;Xenon SSG&lt;/a&gt; is a React static site generator that aims to radically challenge how websites can be built and maintained. It powers this very website! &lt;a href="https://alvaro.cuesta.dev/blog/why-i-built-my-own-timeline/#user-content-fnref-xenon-ssg" data-footnote-backref="" aria-label="Back to reference 1" class="data-footnote-backref"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="user-content-fn-git"&gt;
&lt;p&gt;&lt;a href="https://git-scm.com/"&gt;Git&lt;/a&gt; is a distributed version control system that allows you to track changes in your files and collaborate with others. Think of it as Dropbox for code, &lt;em&gt;on steroids&lt;/em&gt;. It is the backbone of many modern software development workflows, and it has become a powerful tool for personal organization and content management as well. &lt;a href="https://alvaro.cuesta.dev/blog/why-i-built-my-own-timeline/#user-content-fnref-git" data-footnote-backref="" aria-label="Back to reference 2" class="data-footnote-backref"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content><author><name>Álvaro Cuesta</name><uri>https://alvaro.cuesta.dev</uri></author><category term="timeline"/><category term="indie-web"/><category term="social-media"/><category term="publishing"/></entry><entry><title>What if you don't need MCP at all?  Is #…</title><id>https://alvaro.cuesta.dev/timeline/202604111321/</id><published>2026-04-11T13:21:00Z</published><updated>2026-04-11T13:21:00Z</updated><link href="https://alvaro.cuesta.dev/timeline/202604111321/" rel="alternate" type="text/html"/><content type="html">&lt;p&gt;&lt;a href="https://mariozechner.at/posts/2025-11-02-what-if-you-dont-need-mcp/"&gt;What if you don&amp;#x27;t need MCP at all?&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Is &lt;a href="https://alvaro.cuesta.dev/timeline/tags/mcp/"&gt;#mcp&lt;/a&gt; necessary? How &lt;a href="https://alvaro.cuesta.dev/timeline/tags/llm/"&gt;#llms&lt;/a&gt; already speak the language of code, and what that means for the future of &lt;a href="https://alvaro.cuesta.dev/timeline/tags/ai/"&gt;#ai&lt;/a&gt;.&lt;/p&gt;</content><author><name>Álvaro Cuesta</name><uri>https://alvaro.cuesta.dev</uri></author><category term="ai"/><category term="llm"/><category term="mcp"/></entry><entry><title>Hello, world! This is the first post on …</title><id>https://alvaro.cuesta.dev/timeline/202604111147/</id><published>2026-04-11T11:47:00Z</published><updated>2026-04-11T11:47:00Z</updated><link href="https://alvaro.cuesta.dev/timeline/202604111147/" rel="alternate" type="text/html"/><content type="html">&lt;p&gt;Hello, world! This is the first post on the timeline.&lt;/p&gt;
&lt;p&gt;Short-form thoughts, quick updates, and random musings will live here.&lt;/p&gt;</content><author><name>Álvaro Cuesta</name><uri>https://alvaro.cuesta.dev</uri></author></entry><entry><title>What is AI good for in 2026?</title><id>https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/</id><published>2026-03-14T00:00:00Z</published><updated>2026-03-15T19:00:00Z</updated><link href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/" rel="alternate" type="text/html"/><content type="html">&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; this post has been hand-crafted by me (and only me), with only very minor editorial assistance from AI, to improve wording and clarity. All &lt;em&gt;em-dashes&lt;/em&gt; are mine, and only mine 😉&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id="me-myself-and-ai" class="autolink-heading"&gt;Me, myself and AI&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#me-myself-and-ai"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I have always been suspicious of &lt;strong&gt;AI hype&lt;/strong&gt;, but I am not the kind of engineer who dismisses things outright. I have been following AI for years through hands-on use and plenty of reading across forums and social media. I have seen multiple hype waves come and go, and I have also seen real progress in the field.&lt;/p&gt;
&lt;p&gt;What has always bothered me is how many &lt;strong&gt;AI people&lt;/strong&gt;&lt;sup&gt;&lt;a href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#user-content-fn-ai-people" id="user-content-fnref-ai-people" data-footnote-ref="true" aria-describedby="footnote-label"&gt;1&lt;/a&gt;&lt;/sup&gt; make huge claims about LLMs for software engineering without giving practical examples. You hear &lt;em&gt;&quot;AI is amazing, it can do X, Y, and Z&quot;&lt;/em&gt;, but rarely see &lt;strong&gt;concrete evidence&lt;/strong&gt; in a real project.&lt;/p&gt;
&lt;p&gt;So I kept asking myself:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Are the hype and claims justified?&lt;/li&gt;
&lt;li&gt;Is it only useful on small projects?&lt;/li&gt;
&lt;li&gt;Is it mainly for junior developers, or can it help experienced engineers too?&lt;/li&gt;
&lt;li&gt;Is it just boilerplate generation, or can it solve non-trivial problems?&lt;/li&gt;
&lt;li&gt;Does it make experienced developers faster or slower?&lt;/li&gt;
&lt;li&gt;Does it make them better engineers, or only faster typists?&lt;/li&gt;
&lt;li&gt;Will it hurt long-term maintainability?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In short: &lt;strong&gt;are LLMs actually useful&lt;/strong&gt; in real projects for experienced engineers, or are they mostly just another buzzword in the current AI startup wave?&lt;/p&gt;
&lt;p&gt;I wanted to find out with &lt;strong&gt;experience, not vibes&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id="combating-ai-hype-with-experience" class="autolink-heading"&gt;Combating AI hype with experience&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#combating-ai-hype-with-experience"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I had already used LLMs in personal and professional work, but mostly in limited, tactical ways. I had never really tested them end-to-end, so I gave it a fair shot.&lt;/p&gt;
&lt;p&gt;Because I am an open source advocate, and my work is public on GitHub, I am in a good position to share evidence. My goal was simple: fight &lt;em&gt;AI hype&lt;/em&gt; with &lt;em&gt;hard data&lt;/em&gt; and document where these tools help, and where they still fail.&lt;/p&gt;
&lt;p&gt;Here is what the experiment looks like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I have a personal project (&lt;a href="https://lambda.cuesta.dev/"&gt;Lambda Musika&lt;/a&gt;, code available on &lt;a href="https://github.com/alvaro-cuesta/lambda-musika"&gt;GitHub&lt;/a&gt;) that I do not think can be classified as common, regurgitated&lt;sup&gt;&lt;a href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#user-content-fn-regurgitation" id="user-content-fnref-regurgitation" data-footnote-ref="true" aria-describedby="footnote-label"&gt;2&lt;/a&gt;&lt;/sup&gt;, or just-another-CRUD&lt;sup&gt;&lt;a href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#user-content-fn-crud" id="user-content-fnref-crud" data-footnote-ref="true" aria-describedby="footnote-label"&gt;3&lt;/a&gt;&lt;/sup&gt;-slop.&lt;/li&gt;
&lt;li&gt;The project is a web-based music production environment, scriptable with JavaScript and running completely in the browser.&lt;/li&gt;
&lt;li&gt;It is still in the early stages, but already a non-trivial codebase with a few thousand lines and multiple features. It is not a toy project, but it is also not a large-scale production system.&lt;/li&gt;
&lt;li&gt;Written in TypeScript with React.&lt;/li&gt;
&lt;li&gt;My tooling was just GitHub Copilot in VSCode, using GPT-5.3-Codex. I have not tried other models or tools, so I cannot speak for them (yet).&lt;/li&gt;
&lt;li&gt;The project is set up with extensive linting and automated testing.&lt;/li&gt;
&lt;li&gt;My workflow uses agent mode plus a medium-sized &lt;code&gt;copilot-instructions.md&lt;/code&gt;. I will be in the loop at all times (no vibe coding).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I hope this experiment is useful for other skeptical engineers who still want to test whether AI can actually help in real projects.&lt;/p&gt;
&lt;h3 id="where-ai-surprised-me" class="autolink-heading"&gt;Where AI surprised me&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#where-ai-surprised-me"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id="experiment-1-replace-the-editor-library" class="autolink-heading"&gt;Experiment 1: replace the editor library&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#experiment-1-replace-the-editor-library"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Lambda Musika features a code editor that allows users to write scripts that drive the music generation. I had originally implemented it with the &lt;a href="https://ace.c9.io/"&gt;Ace editor&lt;/a&gt;, which is mature and widely used. However, I wanted to experiment with migrating to VSCode&amp;#x27;s &lt;a href="https://microsoft.github.io/monaco-editor/"&gt;Monaco editor&lt;/a&gt;, which is more modern and has better TypeScript support.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(Note that this is currently a branch in progress and unmaintained, so the code is not publicly available yet. I will update this post with links once it is ready.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;To achieve feature parity during the migration, I asked Copilot to generate a full inventory of existing editor features and quirks. Then I asked it to (1) migrate to Monaco while preserving behavior and configuration and (2) integrate TypeScript type-checking into the editor, including types from my internal library. It got &lt;strong&gt;surprisingly close on the first try&lt;/strong&gt;.&lt;/p&gt;
&lt;h4 id="experiment-2-refactor-internal-library-into-a-separate-package" class="autolink-heading"&gt;Experiment 2: refactor internal library into a separate package&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#experiment-2-refactor-internal-library-into-a-separate-package"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;As part of the preparation for &lt;a href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#experiment-1-replace-the-editor-library"&gt;Experiment 1&lt;/a&gt;, I wanted to share types from an internal library with the editor package. This required a significant refactor of the project structure, including setting up a monorepo, migrating to &lt;a href="https://pnpm.io/"&gt;pnpm&lt;/a&gt;, and configuring TypeScript paths and build scripts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Copilot struggled with this one&lt;/strong&gt;, often getting lost in the details and making mistakes in the configuration, and I had to stop Copilot in its tracks. However, after migrating the project structure and setting up the monorepo&amp;#x27;s first couple packages myself, it still provided a &lt;strong&gt;huge amount of help&lt;/strong&gt; migrating additional packages.&lt;/p&gt;
&lt;h4 id="experiment-3-build-a-vite-plugin-for-the-editor-types" class="autolink-heading"&gt;Experiment 3: build a Vite plugin for the editor types&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#experiment-3-build-a-vite-plugin-for-the-editor-types"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;After the refactor in &lt;a href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#experiment-2-refactor-internal-library-into-a-separate-package"&gt;Experiment 2&lt;/a&gt;, I needed to build a custom &lt;a href="https://vitejs.dev/"&gt;Vite&lt;/a&gt; plugin to serve the internal library types to the editor across packages. This was a non-trivial task that involved understanding Vite&amp;#x27;s plugin API, configuring TypeScript compilation, and handling different behavior in development and production modes.&lt;/p&gt;
&lt;p&gt;In particular, the plugin needed to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Compile the internal library types using TSC and make it available via &lt;code&gt;import musikaTypes from &quot;@lambda-musika/musika?types&quot;&lt;/code&gt; (note the &lt;code&gt;?types&lt;/code&gt; suffix).&lt;/li&gt;
&lt;li&gt;Serve and hot-reload the compiled types from a virtual Vite import in development mode.&lt;/li&gt;
&lt;li&gt;Emit a static asset with the compiled types in production mode.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Then I needed to integrate the output of this plugin into the editor setup to provide type-checking for user scripts.&lt;/p&gt;
&lt;p&gt;Copilot got &lt;strong&gt;most of this right on the first attempt&lt;/strong&gt; with little assistance. I had to correct some details and add some missing pieces, but overall it was a &lt;strong&gt;huge time saver&lt;/strong&gt; for a complex task.&lt;/p&gt;
&lt;h4 id="experiment-4-accessibility-improvements" class="autolink-heading"&gt;Experiment 4: accessibility improvements&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#experiment-4-accessibility-improvements"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;As part of my previous work in &lt;a href="https://deedmob.com/"&gt;Deedmob&lt;/a&gt;, I had done extensive accessibility audits and improvements, learning a lot in the process. I had a good mental model of common &lt;a href="https://www.w3.org/WAI/standards-guidelines/aria/"&gt;ARIA&lt;/a&gt; issues and best practices for web accessibility.&lt;/p&gt;
&lt;p&gt;Accessibility is surprisingly complex and nuanced, and it requires a good understanding of both the technical aspects and the user experience. I wanted to see whether Copilot could help me identify and fix accessibility issues in my project.&lt;/p&gt;
&lt;p&gt;One particular aspect that is often overlooked is the accessibility of dropdown menus and other &quot;expandable&quot; UI elements. These can be tricky to get right, especially when it comes to keyboard navigation and screen reader support. There are lots of nuances, like attaching &lt;code&gt;aria-expanded&lt;/code&gt; and &lt;code&gt;aria-controls&lt;/code&gt; to the right element, managing focus properly, and ensuring that the expanded content is accessible to screen readers.&lt;/p&gt;
&lt;p&gt;Lambda Musika features a bottom bar with multiple expandable panels, and I wanted to improve their accessibility. I asked Copilot to audit the existing implementation and suggest improvements, then implement them. &lt;strong&gt;It got most of the ARIA attributes right on the first try&lt;/strong&gt;, including focus management and keyboard navigation (which I did not even ask for!)&lt;/p&gt;
&lt;p&gt;It also helped spot some redundant attributes and text content that would have added noise for screen reader users, as well as some missing group roles, which was a nice bonus.&lt;/p&gt;
&lt;p&gt;You can see the implementation in commits &lt;a href="https://github.com/alvaro-cuesta/lambda-musika/commit/0b3795c4776277a2cb119f3af545347a62425717"&gt;&lt;code&gt;0b3795c&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/alvaro-cuesta/lambda-musika/commit/49123ce6240d2dc7d30fb755267060af8671abfa"&gt;&lt;code&gt;49123ce&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id="experiment-5-end-to-end-feature-implementation" class="autolink-heading"&gt;Experiment 5: end-to-end feature implementation&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#experiment-5-end-to-end-feature-implementation"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;I wanted to implement a new feature from scratch, starting with the design and user experience, then moving on to the implementation and testing.&lt;/p&gt;
&lt;p&gt;The feature was to add support for metadata in user scripts, which would allow users to add custom information (like title, author, description...) to their scripts and have it displayed in the UI. This was a non-trivial feature because the scripting system is a bit quirky due to browser limitations, and it required changes in multiple parts of the codebase, including script parsing, editor integration, and the UI for displaying the metadata.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(Code is publicly available in branch &lt;a href="https://github.com/alvaro-cuesta/lambda-musika/tree/feat/metadata"&gt;&lt;code&gt;feat/metadata&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://github.com/alvaro-cuesta/lambda-musika/pull/7"&gt;PR#7&lt;/a&gt;.)&lt;/em&gt;&lt;/p&gt;
&lt;h5 id="experiment-51-metadata-in-comments" class="autolink-heading"&gt;Experiment 5.1: metadata in comments&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#experiment-51-metadata-in-comments"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;The first iteration I asked for was to &lt;a href="https://strudel.cc/learn/metadata/"&gt;extract metadata from script comments&lt;/a&gt;, which is a common approach in many scripting environments like &lt;a href="https://strudel.cc/"&gt;Strudel&lt;/a&gt;. &lt;strong&gt;Copilot got the implementation right on its first attempt&lt;/strong&gt;, but I quickly realized that the approach was suboptimal for my use case.&lt;/p&gt;
&lt;h5 id="experiment-52-metadata-as-exports" class="autolink-heading"&gt;Experiment 5.2: metadata as exports&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#experiment-52-metadata-as-exports"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;This was a perfect opportunity to test the model&amp;#x27;s ability to adapt and iterate based on feedback. I explained the issues with the comment-based approach and asked it to come up with a better solution. Copilot&amp;#x27;s response was to migrate the scripts to a &lt;a href="https://nodejs.org/api/modules.html#modules-commonjs-modules"&gt;CommonJS&lt;/a&gt;-like interface (which is exactly what I had in mind!), where the metadata would be defined as an export from the script file.&lt;/p&gt;
&lt;p&gt;This meant a significant refactor of the existing scripts (which were mostly written in a simple, ad-hoc style) to support this new module system.&lt;/p&gt;
&lt;p&gt;I decided to keep all the existing metadata-handling code and let the model iterate on top of its previous implementation. This way, I could test its ability to adapt and improve without starting from scratch.&lt;/p&gt;
&lt;p&gt;The model &lt;strong&gt;got the new implementation mostly right on the first try&lt;/strong&gt;, but after the refactor there was a lot of leftover code from the previous implementation that I had to explicitly ask to be removed. Iteration also generated very similar functions that should have been part of the same metadata pipeline. For example, it created ad-hoc functions for each metadata field instead of a unified approach that handled all fields consistently. This was a bit of a mess, but it was still a huge time saver compared to doing the whole thing myself, since the model was able to fix the issues once I pointed them out.&lt;/p&gt;
&lt;p&gt;Additionally, the model did not really handle the user-facing documentation updates properly: it generated documentation, but often forgot to update it as we iterated on the implementation, and it ended up reflecting implementation details that were irrelevant to users.&lt;/p&gt;
&lt;h5 id="experiment-53-use-metadata-for-filename-generation" class="autolink-heading"&gt;Experiment 5.3: use metadata for filename generation&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#experiment-53-use-metadata-for-filename-generation"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;After the migration to the new metadata system, I wanted to make use of the metadata for something practical. One idea I had was to use the title from the metadata to generate more human-friendly filenames for the scripts when they are exported or saved. For example, instead of exporting a script as &lt;code&gt;script-20260314.musika&lt;/code&gt;, it could be exported as &lt;code&gt;Author - Title.musika&lt;/code&gt; based on its metadata.&lt;/p&gt;
&lt;p&gt;This was a relatively simple change, but it required understanding the new metadata structure and integrating it into the export logic. I asked Copilot to implement this feature, and it &lt;strong&gt;got it right on the first try without any guidance&lt;/strong&gt;, which was a nice surprise. It handled all the edge cases gracefully (like missing metadata fields, multiple metadata fields like multiple authors, etc.), which showed an impressive understanding of the task.&lt;/p&gt;
&lt;p&gt;One aspect that left me a bit unsatisfied was that the model opted to implement filename sanitization manually instead of reusing an existing library.&lt;/p&gt;
&lt;h5 id="experiment-54-add-metadata-panel-in-the-ui" class="autolink-heading"&gt;Experiment 5.4: add metadata panel in the UI&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#experiment-54-add-metadata-panel-in-the-ui"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;Finally, I asked Copilot to add a human-friendly metadata panel in the UI to display this information. The feature is currently stuck in a draft PR because the generated UI was not up to my standards, and I have not yet found the time to iterate on it. The implementation was mostly correct, but the visual design was not good enough for my taste, and I did not want to merge it as is.&lt;/p&gt;
&lt;p&gt;The generated UI was functionally correct but visually poor, which is not surprising given LLMs&amp;#x27; limitations with visual design. Iterating on the visual issues did not work well, and it often generated ad-hoc solutions that were inconsistent with the rest of the UI or were contrary to best practices.&lt;/p&gt;
&lt;h3 id="what-ai-still-gets-wrong" class="autolink-heading"&gt;What AI still gets wrong&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#what-ai-still-gets-wrong"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id="good-coder-bad-engineer" class="autolink-heading"&gt;Good coder, bad engineer&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#good-coder-bad-engineer"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;LLMs are very good at writing code, but still weak at &lt;em&gt;engineering&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;What I mean is: they can produce working code, but they often miss the bigger picture: (1) how code fits into the system, (2) how it will be maintained, (3) how it will evolve, and therefore (4) which architectural and design tradeoffs to make.&lt;/p&gt;
&lt;p&gt;Some actual drawbacks I experienced:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It had trouble with architectural decisions, often making suboptimal choices that I had to correct as seen in &lt;a href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#experiment-2-refactor-internal-library-into-a-separate-package"&gt;Experiment 2&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;It often duplicates logic instead of abstracting it. I frequently saw near-identical code blocks side by side with tiny variations that I had to abstract manually or ask the model to abstract.&lt;/li&gt;
&lt;li&gt;It often writes tests that assert too many concerns at once. Asking it to split tests and improve naming helps, but still requires supervision.&lt;/li&gt;
&lt;li&gt;Comments and test names often explain &lt;em&gt;&quot;what&quot; but not &quot;why&quot;&lt;/em&gt;&lt;sup&gt;&lt;a href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#user-content-fn-what-and-not-why" id="user-content-fnref-what-and-not-why" data-footnote-ref="true" aria-describedby="footnote-label"&gt;4&lt;/a&gt;&lt;/sup&gt;. Newer models generate fewer useless comments (good), but often swing too far toward under-commenting (bad).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I wonder if these are particular tendencies in GPT-5.3-Codex, Copilot, or if it&amp;#x27;s a common issue in the current state of AI-assisted coding.&lt;/p&gt;
&lt;h4 id="diligent-but-not-smart" class="autolink-heading"&gt;Diligent but not smart&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#diligent-but-not-smart"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Current models follow instructions very well, but they do not reliably reason about context and consequences. They execute what you ask, often literally, without strong critical judgment about what to avoid. That means they can produce valid code that is still &lt;strong&gt;the wrong strategic choice&lt;/strong&gt;.&lt;/p&gt;
&lt;h4 id="sloppy-before-lazy" class="autolink-heading"&gt;Sloppy before lazy&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#sloppy-before-lazy"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Models tend to reimplement code that already exists in the project instead of reusing it. Sometimes that is fine, but often it creates duplicate logic and avoidable technical debt.&lt;/p&gt;
&lt;p&gt;They also show a kind of NIH &lt;em&gt;(Not Invented Here)&lt;/em&gt;&lt;sup&gt;&lt;a href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#user-content-fn-nih" id="user-content-fnref-nih" data-footnote-ref="true" aria-describedby="footnote-label"&gt;5&lt;/a&gt;&lt;/sup&gt; behavior with ecosystem tooling, rebuilding things that mature libraries already solve. Adding dependencies is not always right, but this is a tradeoff call LLMs still struggle to make.&lt;/p&gt;
&lt;h4 id="not-visual-thinkers" class="autolink-heading"&gt;Not visual thinkers&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#not-visual-thinkers"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;They are weak at visual work. They can generate UI code, but they are still poor at making interfaces actually look good or improving them through iteration. Feeding them images of the current state and asking for improvements does &lt;em&gt;not&lt;/em&gt; work well.&lt;/p&gt;
&lt;p&gt;This limitation showed up very clearly in &lt;a href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#experiment-54-add-metadata-panel-in-the-ui"&gt;Experiment 5.4&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I wonder if this is a fundamental limitation of LLMs, GPT-5.3-Codex in particular, or just a sign that we haven&amp;#x27;t yet found the right prompting strategy.&lt;/p&gt;
&lt;h4 id="when-documenting-is-not-documentation" class="autolink-heading"&gt;When &quot;documenting&quot; is not &quot;documentation&quot;&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#when-documenting-is-not-documentation"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;They can output documentation, but they are still inconsistent at producing &lt;em&gt;useful&lt;/em&gt; documentation. Without guidance, they often produce too much or too little, and they miss key project-specific details. Unimportant quirks and implementation details are often over-documented, while important design decisions and tradeoffs are under-documented or not documented at all. In user-facing documentation, they often focus on implementation details instead of user needs and mental models.&lt;/p&gt;
&lt;p&gt;In particular, I noticed they tend to replicate and reword your original instructions instead of documenting the actual information that would help newcomers understand the code, the project, or the rationale behind decisions. This is a problem because it creates a false sense of security, and wastes the reader&amp;#x27;s time.&lt;/p&gt;
&lt;h3 id="best-practices-i-learned" class="autolink-heading"&gt;Best practices I learned&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#best-practices-i-learned"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id="static-types-are-a-must" class="autolink-heading"&gt;Static types are a must&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#static-types-are-a-must"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;LLMs are much more effective when working with statically typed languages. The type system provides a strong safety net that allows the model to experiment and iterate with &lt;strong&gt;less risk&lt;/strong&gt;. It also provides a &lt;strong&gt;clear contract&lt;/strong&gt;, which helps the model &lt;strong&gt;understand structure and intent&lt;/strong&gt; more reliably.&lt;/p&gt;
&lt;p&gt;TypeScript in particular proved essential for this experiment, and working with it made me appreciate it even more than I already did.&lt;/p&gt;
&lt;h4 id="copilot-instructions" class="autolink-heading"&gt;Copilot instructions&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#copilot-instructions"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Having a good &lt;code&gt;copilot-instructions.md&lt;/code&gt; file is a &lt;strong&gt;game changer&lt;/strong&gt;. It lets you set up shared context and guidelines for the model, which improves consistency and reduces the need for repeated instructions. I highly recommend it for any project using Copilot.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Does the model keep repeating the same mistake over and over?&lt;/strong&gt; Add a section in the instructions about it, with examples of how to do it right and how to avoid doing it wrong.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Is the model wasting effort in understanding the project structure and conventions?&lt;/strong&gt; Add a section in the instructions about it, with an inventory of existing patterns, key files and directories, and examples of how to follow them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Do you want the model to follow a certain workflow or process?&lt;/strong&gt; Add a section in the instructions about it, with a step-by-step guide and examples of how to execute it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To top it off: you can ask the model to improve the instructions themselves! This is a great way to keep them up to date and relevant as the project evolves.&lt;/p&gt;
&lt;p&gt;See the &lt;a href="https://github.com/alvaro-cuesta/lambda-musika/blob/main/.github/copilot-instructions.md"&gt;latest version of Lambda Musika&amp;#x27;s &lt;code&gt;copilot-instructions.md&lt;/code&gt;&lt;/a&gt; file for a practical example of how I use it in my project.&lt;/p&gt;
&lt;h4 id="affordances-and-guardrails" class="autolink-heading"&gt;Affordances and guardrails&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#affordances-and-guardrails"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;It is very important to have good affordances and guardrails when using LLMs. This means having a &lt;strong&gt;solid test suite, good linting&lt;/strong&gt;, and, in general, a workflow that allows you to &lt;strong&gt;quickly catch mistakes&lt;/strong&gt; and prevent wasteful detours.&lt;/p&gt;
&lt;p&gt;These tools are not just safety nets, but also enablers. They allow the model to experiment and iterate without your explicit supervision on every change. They also make it easier to trust the model, which can lead to faster development and more ambitious use of AI assistance.&lt;/p&gt;
&lt;p&gt;This was especially visible in &lt;a href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#experiment-5-end-to-end-feature-implementation"&gt;Experiment 5&lt;/a&gt;, where linting and tests helped catch regressions quickly while we iterated across parser, editor, and UI layers.&lt;/p&gt;
&lt;h4 id="human-in-the-loop" class="autolink-heading"&gt;Human in the loop&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#human-in-the-loop"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;LLMs are not yet at the point where you can just ask them to do something and forget about it. You need to &lt;strong&gt;be in the loop&lt;/strong&gt;, reviewing their output, guiding them with feedback, and making &lt;strong&gt;strategic decisions&lt;/strong&gt; about when to accept or reject their suggestions.&lt;/p&gt;
&lt;p&gt;Always keep human review and judgment in the decision path, especially for architectural choices, risk acceptance, and quality tradeoffs.&lt;/p&gt;
&lt;p&gt;Take every opportunity to steer the model toward better choices. Do not just ask it to do something and hope for the best. Review its output, give feedback, and iterate as it goes. Otherwise, the model might get trapped in a local minimum of bad choices, &lt;strong&gt;wasting time and effort&lt;/strong&gt; on suboptimal solutions that a bit of guidance could have avoided.&lt;/p&gt;
&lt;p&gt;The shift from &lt;a href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#experiment-51-metadata-in-comments"&gt;Experiment 5.1&lt;/a&gt; to &lt;a href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#experiment-52-metadata-as-exports"&gt;Experiment 5.2&lt;/a&gt; is a good example: the first solution worked, but only human review surfaced that it was the wrong fit for user experience.&lt;/p&gt;
&lt;h4 id="preparation-before-execution" class="autolink-heading"&gt;Preparation before execution&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#preparation-before-execution"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;One strategy I found useful for end-to-end, large-scale tasks is to first &lt;strong&gt;ask the model to explore the code, prepare, and document a guide for the task&lt;/strong&gt;. Spend some time reviewing that guide, giving feedback, and iterating until you are happy with it.&lt;/p&gt;
&lt;p&gt;Once the guide is ready, &lt;strong&gt;start the session from scratch&lt;/strong&gt; (to clear the model&amp;#x27;s context window) and let it execute the task following the guide. This way, you can ensure that the model has a clear plan and understanding of the task before it starts writing code, which can lead to better results and fewer mistakes along the way.&lt;/p&gt;
&lt;p&gt;Context pollution&lt;sup&gt;&lt;a href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#user-content-fn-context-pollution" id="user-content-fnref-context-pollution" data-footnote-ref="true" aria-describedby="footnote-label"&gt;6&lt;/a&gt;&lt;/sup&gt; is a real issue, and starting with a clean slate after preparation can help mitigate it.&lt;/p&gt;
&lt;p&gt;This strategy was really helpful in &lt;a href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#experiment-1-replace-the-editor-library"&gt;Experiment 1&lt;/a&gt;, where the task was complex and required a good understanding of the project and the desired outcome.&lt;/p&gt;
&lt;h4 id="copilot-as-co-reviewer" class="autolink-heading"&gt;Copilot as Co-Reviewer&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#copilot-as-co-reviewer"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;One workflow I brought over from my day job is using Copilot in GitHub PRs to review code, whether written by me or generated by the model. This creates an interesting meta-dynamic: you are asking AI to review AI-generated code. It sounds circular, but in practice it still catches real issues you would otherwise miss.&lt;/p&gt;
&lt;p&gt;Copilot is nowhere near a perfect reviewer. It is hit-or-miss, and will sometimes insist on suggestions that are odd (or plain wrong). But it has caught genuine bugs and inconsistencies that I (and other human reviewers) had missed. When the same wrong suggestion keeps coming back, &lt;code&gt;copilot-instructions.md&lt;/code&gt; (&lt;a href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#copilot-instructions"&gt;see above&lt;/a&gt;) is the right tool to reduce noise, and one-off bad suggestions are fast enough to dismiss that the net cost is still positive.&lt;/p&gt;
&lt;p&gt;Does it replace human reviewers? Not at all. But it works well as a &lt;strong&gt;first-pass filter&lt;/strong&gt;, catching the obvious issues early so that human review time can focus on what actually requires human judgment. At work, this pattern has measurably shortened our PR async review cycles.&lt;/p&gt;
&lt;h3 id="conclusion" class="autolink-heading"&gt;Conclusion&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#conclusion"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;My current take: AI is already a strong coding assistant, but still a weak software engineer.&lt;/p&gt;
&lt;p&gt;I used to be firmly in the &lt;em&gt;&quot;AI is impressive but it might be making me slower&quot;&lt;/em&gt; camp. That is changing. &lt;strong&gt;Rapidly.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you are skeptical, my advice is simple: run your own controlled experiment on a real project following the &lt;a href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#best-practices-i-learned"&gt;best practices above&lt;/a&gt;. That is where the signal is, and how you will find out for yourself what works and what does not.&lt;/p&gt;
&lt;p&gt;I expect these tools to keep improving quickly, but today they still need an experienced human in the loop. Let&amp;#x27;s see if that changes in the next few months and I get the opportunity to turn this post into a series!&lt;/p&gt;

&lt;section data-footnotes="true" class="footnotes"&gt;&lt;h3 class="autolink-heading" id="footnote-label"&gt;Footnotes&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#footnote-label"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li id="user-content-fn-ai-people"&gt;
&lt;p&gt;Not AI researchers or practitioners, but rather those who loudly praise LLMs without backing it up. &lt;a href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#user-content-fnref-ai-people" data-footnote-backref="" aria-label="Back to reference 1" class="data-footnote-backref"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="user-content-fn-regurgitation"&gt;
&lt;p&gt;The criticism that LLMs are only useful for boilerplate and repetitive tasks since they only regurgitate their training data. &lt;a href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#user-content-fnref-regurgitation" data-footnote-backref="" aria-label="Back to reference 2" class="data-footnote-backref"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="user-content-fn-crud"&gt;
&lt;p&gt;Acronym for &lt;em&gt;&lt;strong&gt;C&lt;/strong&gt;reate, &lt;strong&gt;R&lt;/strong&gt;ead, &lt;strong&gt;U&lt;/strong&gt;pdate, &lt;strong&gt;D&lt;/strong&gt;elete&lt;/em&gt;. Apps that are mostly boilerplate, with no logic beyond basic database operations. &lt;a href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#user-content-fnref-crud" data-footnote-backref="" aria-label="Back to reference 3" class="data-footnote-backref"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="user-content-fn-what-and-not-why"&gt;
&lt;p&gt;The &lt;em&gt;&quot;what&quot;&lt;/em&gt; is usually visible in the code itself. The &lt;em&gt;&quot;why&quot;&lt;/em&gt; (and especially &lt;em&gt;&quot;why not&quot;&lt;/em&gt;) is what actually drives maintainability, and is consistently missing from both AI-generated and junior-written comments. &lt;a href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#user-content-fnref-what-and-not-why" data-footnote-backref="" aria-label="Back to reference 4" class="data-footnote-backref"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="user-content-fn-nih"&gt;
&lt;p&gt;The tendency to rebuild solutions from scratch just because they were not created in-house. &lt;a href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#user-content-fnref-nih" data-footnote-backref="" aria-label="Back to reference 5" class="data-footnote-backref"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="user-content-fn-context-pollution"&gt;
&lt;p&gt;Long sessions accumulate stale, irrelevant, or conflicting details that gradually degrade model performance and decision quality. &lt;a href="https://alvaro.cuesta.dev/blog/what-is-ai-good-for-in-2026/#user-content-fnref-context-pollution" data-footnote-backref="" aria-label="Back to reference 6" class="data-footnote-backref"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content><author><name>Álvaro Cuesta</name><uri>https://alvaro.cuesta.dev</uri></author><category term="ai"/><category term="llm"/><category term="experiments"/><category term="programming"/></entry><entry><title>Your React refs might be breaking someone else's code…</title><id>https://alvaro.cuesta.dev/blog/your-react-refs-might-be-breaking-someone-elses-code/</id><published>2025-08-23T00:00:00Z</published><updated>2026-03-15T19:00:00Z</updated><link href="https://alvaro.cuesta.dev/blog/your-react-refs-might-be-breaking-someone-elses-code/" rel="alternate" type="text/html"/><content type="html">&lt;p&gt;...and someone else&amp;#x27;s refs might be breaking &lt;strong&gt;yours&lt;/strong&gt;! 😱&lt;/p&gt;

&lt;h3 id="react-ref-callback-guarantees" class="autolink-heading"&gt;React &lt;code&gt;ref&lt;/code&gt; callback guarantees&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/your-react-refs-might-be-breaking-someone-elses-code/#react-ref-callback-guarantees"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://react.dev/reference/react-dom/components/common#ref-callback"&gt;React&amp;#x27;s &lt;code&gt;ref&lt;/code&gt; callbacks&lt;/a&gt; provide an &lt;a href="https://react.dev/reference/react-dom/components/common#caveats"&gt;important guarantee&lt;/a&gt; (emphasis mine):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;When you pass a different &lt;code&gt;ref&lt;/code&gt; callback, React will call the previous callback&amp;#x27;s cleanup function if provided.&lt;/strong&gt; If no cleanup function is defined, the &lt;code&gt;ref&lt;/code&gt; callback will be called with &lt;code&gt;null&lt;/code&gt; as the argument. The next function will be called with the DOM node.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This means that the following code:&lt;/p&gt;
&lt;a id="logging-buggy"&gt;&lt;/a&gt;
&lt;figure class="code-with-caption"&gt;&lt;pre&gt;&lt;code class="language-tsx"&gt;&lt;span class="pl-k"&gt;function&lt;/span&gt; &lt;span class="pl-en"&gt;MyDiv&lt;/span&gt;({ &lt;span class="pl-v"&gt;children&lt;/span&gt; }&lt;span class="pl-k"&gt;:&lt;/span&gt; { &lt;span class="pl-v"&gt;children&lt;/span&gt;&lt;span class="pl-k"&gt;?:&lt;/span&gt; &lt;span class="pl-en"&gt;ReactNode&lt;/span&gt; }) {
  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-en"&gt;ref&lt;/span&gt; &lt;span class="pl-k"&gt;=&lt;/span&gt; (&lt;span class="pl-v"&gt;div&lt;/span&gt;&lt;span class="pl-k"&gt;:&lt;/span&gt; &lt;span class="pl-en"&gt;HTMLDivElement&lt;/span&gt;) &lt;span class="pl-k"&gt;=&gt;&lt;/span&gt; {
    &lt;span class="pl-c1"&gt;console&lt;/span&gt;.&lt;span class="pl-c1"&gt;log&lt;/span&gt;(&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;&quot;&lt;/span&gt;Inserted&lt;span class="pl-pds"&gt;&quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="pl-smi"&gt;div&lt;/span&gt;);
    &lt;span class="pl-k"&gt;return&lt;/span&gt; () &lt;span class="pl-k"&gt;=&gt;&lt;/span&gt; {
      &lt;span class="pl-c1"&gt;console&lt;/span&gt;.&lt;span class="pl-c1"&gt;log&lt;/span&gt;(&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;&quot;&lt;/span&gt;Removed&lt;span class="pl-pds"&gt;&quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="pl-smi"&gt;div&lt;/span&gt;);
    };
  };

  &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;&lt;span class="pl-ent"&gt;div&lt;/span&gt; &lt;span class="pl-e"&gt;ref&lt;/span&gt;&lt;span class="pl-k"&gt;=&lt;/span&gt;&lt;span class="pl-pse"&gt;{&lt;/span&gt;&lt;span class="pl-smi"&gt;ref&lt;/span&gt;&lt;span class="pl-pse"&gt;}&lt;/span&gt;&gt;&lt;span class="pl-pse"&gt;{&lt;/span&gt;&lt;span class="pl-smi"&gt;children&lt;/span&gt;&lt;span class="pl-pse"&gt;}&lt;/span&gt;&lt;/&lt;span class="pl-ent"&gt;div&lt;/span&gt;&gt;;
}

&lt;span class="pl-k"&gt;function&lt;/span&gt; &lt;span class="pl-en"&gt;App&lt;/span&gt;() {
  &lt;span class="pl-k"&gt;const&lt;/span&gt; [&lt;span class="pl-c1"&gt;count&lt;/span&gt;, &lt;span class="pl-c1"&gt;setCount&lt;/span&gt;] &lt;span class="pl-k"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;useState&lt;/span&gt;(&lt;span class="pl-c1"&gt;0&lt;/span&gt;);
  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-en"&gt;increaseCount&lt;/span&gt; &lt;span class="pl-k"&gt;=&lt;/span&gt; () &lt;span class="pl-k"&gt;=&gt;&lt;/span&gt; {
    &lt;span class="pl-en"&gt;setCount&lt;/span&gt;((&lt;span class="pl-v"&gt;count&lt;/span&gt;) &lt;span class="pl-k"&gt;=&gt;&lt;/span&gt; &lt;span class="pl-smi"&gt;count&lt;/span&gt; &lt;span class="pl-k"&gt;+&lt;/span&gt; &lt;span class="pl-c1"&gt;1&lt;/span&gt;);
  };

  &lt;span class="pl-k"&gt;return&lt;/span&gt; (
    &lt;&lt;span class="pl-ent"&gt;div&lt;/span&gt;&gt;
      &lt;&lt;span class="pl-c1"&gt;MyDiv&lt;/span&gt;&gt;Clicked &lt;span class="pl-pse"&gt;{&lt;/span&gt;&lt;span class="pl-smi"&gt;count&lt;/span&gt;&lt;span class="pl-pse"&gt;}&lt;/span&gt; times.&lt;/&lt;span class="pl-c1"&gt;MyDiv&lt;/span&gt;&gt;
      &lt;&lt;span class="pl-ent"&gt;button&lt;/span&gt; &lt;span class="pl-e"&gt;onClick&lt;/span&gt;&lt;span class="pl-k"&gt;=&lt;/span&gt;&lt;span class="pl-pse"&gt;{&lt;/span&gt;&lt;span class="pl-smi"&gt;increaseCount&lt;/span&gt;&lt;span class="pl-pse"&gt;}&lt;/span&gt;&gt;Increase&lt;/&lt;span class="pl-ent"&gt;button&lt;/span&gt;&gt;
    &lt;/&lt;span class="pl-ent"&gt;div&lt;/span&gt;&gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;&lt;figcaption&gt;&lt;p&gt;&lt;a href="https://codesandbox.io/p/sandbox/example-1-qq5qyk?file=%2Fsrc%2FApp.tsx"&gt;CodeSandbox&lt;/a&gt;&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;...will log:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Inserted&lt;/code&gt; on initial mount, when the &lt;code&gt;&lt;div&gt;&lt;/code&gt; HTML element is inserted into the DOM.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Removed&lt;/code&gt; and then &lt;code&gt;Inserted&lt;/code&gt; on every &lt;code&gt;&lt;button&gt;&lt;/code&gt; click (or any other re-render). &lt;strong&gt;This happens regardless of actual DOM insertions/removals.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Removed&lt;/code&gt; on unmount, when the &lt;code&gt;&lt;div&gt;&lt;/code&gt; HTML element is removed from the DOM.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We can fix this issue by making the function reference stable across renders, for example, by wrapping it in &lt;code&gt;useCallback&lt;/code&gt;:&lt;/p&gt;
&lt;a id="logging-fixed"&gt;&lt;/a&gt;
&lt;figure class="code-with-caption"&gt;&lt;pre&gt;&lt;code class="language-tsx"&gt;&lt;span class="pl-k"&gt;function&lt;/span&gt; &lt;span class="pl-en"&gt;MyDiv&lt;/span&gt;({ &lt;span class="pl-v"&gt;children&lt;/span&gt; }&lt;span class="pl-k"&gt;:&lt;/span&gt; { &lt;span class="pl-v"&gt;children&lt;/span&gt;&lt;span class="pl-k"&gt;?:&lt;/span&gt; &lt;span class="pl-en"&gt;ReactNode&lt;/span&gt; }) {
  &lt;span class="pl-c"&gt;// Now `ref` will be stable across renders since`useCallback` memoizes it&lt;/span&gt;
  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-c1"&gt;ref&lt;/span&gt; &lt;span class="pl-k"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;useCallback&lt;/span&gt;((&lt;span class="pl-v"&gt;div&lt;/span&gt;&lt;span class="pl-k"&gt;:&lt;/span&gt; &lt;span class="pl-en"&gt;HTMLDivElement&lt;/span&gt;) &lt;span class="pl-k"&gt;=&gt;&lt;/span&gt; {
    &lt;span class="pl-c1"&gt;console&lt;/span&gt;.&lt;span class="pl-c1"&gt;log&lt;/span&gt;(&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;&quot;&lt;/span&gt;Inserted&lt;span class="pl-pds"&gt;&quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="pl-smi"&gt;div&lt;/span&gt;);
    &lt;span class="pl-k"&gt;return&lt;/span&gt; () &lt;span class="pl-k"&gt;=&gt;&lt;/span&gt; {
      &lt;span class="pl-c1"&gt;console&lt;/span&gt;.&lt;span class="pl-c1"&gt;log&lt;/span&gt;(&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;&quot;&lt;/span&gt;Removed&lt;span class="pl-pds"&gt;&quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="pl-smi"&gt;div&lt;/span&gt;);
    };
  }, []);

  &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;&lt;span class="pl-ent"&gt;div&lt;/span&gt; &lt;span class="pl-e"&gt;ref&lt;/span&gt;&lt;span class="pl-k"&gt;=&lt;/span&gt;&lt;span class="pl-pse"&gt;{&lt;/span&gt;&lt;span class="pl-smi"&gt;ref&lt;/span&gt;&lt;span class="pl-pse"&gt;}&lt;/span&gt;&gt;&lt;span class="pl-pse"&gt;{&lt;/span&gt;&lt;span class="pl-smi"&gt;children&lt;/span&gt;&lt;span class="pl-pse"&gt;}&lt;/span&gt;&lt;/&lt;span class="pl-ent"&gt;div&lt;/span&gt;&gt;;
}

&lt;span class="pl-k"&gt;function&lt;/span&gt; &lt;span class="pl-en"&gt;App&lt;/span&gt;() {
  &lt;span class="pl-k"&gt;const&lt;/span&gt; [&lt;span class="pl-c1"&gt;count&lt;/span&gt;, &lt;span class="pl-c1"&gt;setCount&lt;/span&gt;] &lt;span class="pl-k"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;useState&lt;/span&gt;(&lt;span class="pl-c1"&gt;0&lt;/span&gt;);
  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-en"&gt;increaseCount&lt;/span&gt; &lt;span class="pl-k"&gt;=&lt;/span&gt; () &lt;span class="pl-k"&gt;=&gt;&lt;/span&gt; {
    &lt;span class="pl-en"&gt;setCount&lt;/span&gt;((&lt;span class="pl-v"&gt;count&lt;/span&gt;) &lt;span class="pl-k"&gt;=&gt;&lt;/span&gt; &lt;span class="pl-smi"&gt;count&lt;/span&gt; &lt;span class="pl-k"&gt;+&lt;/span&gt; &lt;span class="pl-c1"&gt;1&lt;/span&gt;);
  };

  &lt;span class="pl-k"&gt;return&lt;/span&gt; (
    &lt;&lt;span class="pl-ent"&gt;div&lt;/span&gt;&gt;
      &lt;&lt;span class="pl-c1"&gt;MyDiv&lt;/span&gt;&gt;Clicked &lt;span class="pl-pse"&gt;{&lt;/span&gt;&lt;span class="pl-smi"&gt;count&lt;/span&gt;&lt;span class="pl-pse"&gt;}&lt;/span&gt; times.&lt;/&lt;span class="pl-c1"&gt;MyDiv&lt;/span&gt;&gt;
      &lt;&lt;span class="pl-ent"&gt;button&lt;/span&gt; &lt;span class="pl-e"&gt;onClick&lt;/span&gt;&lt;span class="pl-k"&gt;=&lt;/span&gt;&lt;span class="pl-pse"&gt;{&lt;/span&gt;&lt;span class="pl-smi"&gt;increaseCount&lt;/span&gt;&lt;span class="pl-pse"&gt;}&lt;/span&gt;&gt;Increase&lt;/&lt;span class="pl-ent"&gt;button&lt;/span&gt;&gt;
    &lt;/&lt;span class="pl-ent"&gt;div&lt;/span&gt;&gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;&lt;figcaption&gt;&lt;p&gt;&lt;a href="https://codesandbox.io/p/sandbox/example-2-rlkk29?file=%2Fsrc%2FApp.tsx"&gt;CodeSandbox&lt;/a&gt;&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;...which will &lt;strong&gt;only&lt;/strong&gt; log:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Inserted&lt;/code&gt; on initial mount.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Removed&lt;/code&gt; on unmount.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Notice the callback is no longer called on button clicks.&lt;/p&gt;
&lt;h3 id="merging-refs" class="autolink-heading"&gt;Merging refs&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/your-react-refs-might-be-breaking-someone-elses-code/#merging-refs"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Just like &lt;code&gt;&lt;div&gt;&lt;/code&gt; has a &lt;code&gt;ref&lt;/code&gt; prop to receive React refs, you often need to provide &lt;code&gt;ref&lt;/code&gt; props for your own components.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-tsx"&gt;&lt;span class="pl-k"&gt;function&lt;/span&gt; &lt;span class="pl-en"&gt;TextInput&lt;/span&gt;({ &lt;span class="pl-v"&gt;ref&lt;/span&gt; }&lt;span class="pl-k"&gt;:&lt;/span&gt; { &lt;span class="pl-v"&gt;ref&lt;/span&gt;&lt;span class="pl-k"&gt;?:&lt;/span&gt; &lt;span class="pl-en"&gt;Ref&lt;/span&gt;&lt;&lt;span class="pl-en"&gt;HTMLInputElement&lt;/span&gt;&gt; }) {
  &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;&lt;span class="pl-ent"&gt;input&lt;/span&gt; &lt;span class="pl-e"&gt;type&lt;/span&gt;&lt;span class="pl-k"&gt;=&lt;/span&gt;&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;&quot;&lt;/span&gt;text&lt;span class="pl-pds"&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class="pl-e"&gt;ref&lt;/span&gt;&lt;span class="pl-k"&gt;=&lt;/span&gt;&lt;span class="pl-pse"&gt;{&lt;/span&gt;&lt;span class="pl-smi"&gt;ref&lt;/span&gt;&lt;span class="pl-pse"&gt;}&lt;/span&gt; /&gt;;
}

&lt;span class="pl-c"&gt;// Now other components can access the internal &lt;input&gt; like this:&lt;/span&gt;
&lt;span class="pl-k"&gt;function&lt;/span&gt; &lt;span class="pl-en"&gt;App&lt;/span&gt;() {
  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-c1"&gt;inputRef&lt;/span&gt; &lt;span class="pl-k"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;useRef&lt;/span&gt;(&lt;span class="pl-c1"&gt;null&lt;/span&gt;);
  &lt;span class="pl-c"&gt;/* ... do stuff with `inputRef` ... */&lt;/span&gt;

  &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;&lt;span class="pl-c1"&gt;TextInput&lt;/span&gt; &lt;span class="pl-e"&gt;ref&lt;/span&gt;&lt;span class="pl-k"&gt;=&lt;/span&gt;&lt;span class="pl-pse"&gt;{&lt;/span&gt;&lt;span class="pl-smi"&gt;ref&lt;/span&gt;&lt;span class="pl-pse"&gt;}&lt;/span&gt; /&gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Low⁠-⁠level UI components often need to use their own refs while also forwarding external ones, like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-tsx"&gt;&lt;span class="pl-k"&gt;function&lt;/span&gt; &lt;span class="pl-en"&gt;MyInput&lt;/span&gt;({ &lt;span class="pl-v"&gt;externalRef&lt;/span&gt; }&lt;span class="pl-k"&gt;:&lt;/span&gt; { &lt;span class="pl-v"&gt;externalRef&lt;/span&gt;&lt;span class="pl-k"&gt;?:&lt;/span&gt; &lt;span class="pl-en"&gt;Ref&lt;/span&gt;&lt;&lt;span class="pl-en"&gt;HTMLInputElement&lt;/span&gt;&gt; }) {
  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-c1"&gt;inputRef&lt;/span&gt; &lt;span class="pl-k"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;useRef&lt;/span&gt;&lt;&lt;span class="pl-en"&gt;HTMLInputElement&lt;/span&gt;&gt;(&lt;span class="pl-c1"&gt;null&lt;/span&gt;);
  &lt;span class="pl-c"&gt;/* ... do stuff with `inputRef` ... */&lt;/span&gt;

  &lt;span class="pl-c"&gt;// How can we pass both `inputRef` and `externalRef` here?&lt;/span&gt;
  &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;&lt;span class="pl-ent"&gt;input&lt;/span&gt; &lt;span class="pl-e"&gt;ref&lt;/span&gt;&lt;span class="pl-k"&gt;=&lt;/span&gt;&lt;span class="pl-pse"&gt;{&lt;/span&gt;&lt;span class="pl-c"&gt;/* ... */&lt;/span&gt;&lt;span class="pl-pse"&gt;}&lt;/span&gt; /&gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;React does not offer a way to set two refs inside the &lt;code&gt;ref&lt;/code&gt; property (see &lt;a href="https://github.com/facebook/react/issues/29757"&gt;facebook/react#29757&lt;/a&gt;), which means the
community has created many small utility libraries to solve this issue, like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-tsx"&gt;&lt;span class="pl-k"&gt;import&lt;/span&gt; { &lt;span class="pl-smi"&gt;mergeRefs&lt;/span&gt; } &lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;&quot;&lt;/span&gt;some-ref-merging-library&lt;span class="pl-pds"&gt;&quot;&lt;/span&gt;&lt;/span&gt;;

&lt;span class="pl-k"&gt;function&lt;/span&gt; &lt;span class="pl-en"&gt;MyInput&lt;/span&gt;({ &lt;span class="pl-v"&gt;externalRef&lt;/span&gt; }&lt;span class="pl-k"&gt;:&lt;/span&gt; { &lt;span class="pl-v"&gt;externalRef&lt;/span&gt;&lt;span class="pl-k"&gt;?:&lt;/span&gt; &lt;span class="pl-en"&gt;Ref&lt;/span&gt;&lt;&lt;span class="pl-en"&gt;HTMLInputElement&lt;/span&gt;&gt; }) {
  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-c1"&gt;inputRef&lt;/span&gt; &lt;span class="pl-k"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;useRef&lt;/span&gt;&lt;&lt;span class="pl-en"&gt;HTMLInputElement&lt;/span&gt;&gt;(&lt;span class="pl-c1"&gt;null&lt;/span&gt;);
  &lt;span class="pl-c"&gt;/* ... do stuff with `inputRef` ... */&lt;/span&gt;

  &lt;span class="pl-c"&gt;// Here we pass the combined refs to the underlying input&lt;/span&gt;
  &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;&lt;span class="pl-ent"&gt;input&lt;/span&gt; &lt;span class="pl-e"&gt;ref&lt;/span&gt;&lt;span class="pl-k"&gt;=&lt;/span&gt;&lt;span class="pl-pse"&gt;{&lt;/span&gt;&lt;span class="pl-en"&gt;mergeRefs&lt;/span&gt;([&lt;span class="pl-smi"&gt;externalRef&lt;/span&gt;, &lt;span class="pl-smi"&gt;inputRef&lt;/span&gt;])&lt;span class="pl-pse"&gt;}&lt;/span&gt; /&gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="someone-elses-refs-might-be-breaking-your-code" class="autolink-heading"&gt;Someone else&amp;#x27;s refs might be breaking your code&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/your-react-refs-might-be-breaking-someone-elses-code/#someone-elses-refs-might-be-breaking-your-code"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There are a few interesting use cases for refs. For example, we might want to focus an &lt;code&gt;&lt;input&gt;&lt;/code&gt; as soon as it is
inserted into the DOM:&lt;/p&gt;
&lt;figure class="code-with-caption"&gt;&lt;pre&gt;&lt;code class="language-tsx"&gt;&lt;span class="pl-k"&gt;function&lt;/span&gt; &lt;span class="pl-en"&gt;MyInput&lt;/span&gt;() {
  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-c1"&gt;focusInput&lt;/span&gt; &lt;span class="pl-k"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;useCallback&lt;/span&gt;((&lt;span class="pl-v"&gt;input&lt;/span&gt;&lt;span class="pl-k"&gt;:&lt;/span&gt; &lt;span class="pl-en"&gt;HTMLInputElement&lt;/span&gt; &lt;span class="pl-k"&gt;|&lt;/span&gt; &lt;span class="pl-c1"&gt;null&lt;/span&gt;) &lt;span class="pl-k"&gt;=&gt;&lt;/span&gt; {
    &lt;span class="pl-smi"&gt;input&lt;/span&gt;?.&lt;span class="pl-c1"&gt;focus&lt;/span&gt;();
  }, []);

  &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;&lt;span class="pl-ent"&gt;input&lt;/span&gt; &lt;span class="pl-e"&gt;ref&lt;/span&gt;&lt;span class="pl-k"&gt;=&lt;/span&gt;&lt;span class="pl-pse"&gt;{&lt;/span&gt;&lt;span class="pl-smi"&gt;focusInput&lt;/span&gt;&lt;span class="pl-pse"&gt;}&lt;/span&gt; &lt;span class="pl-e"&gt;type&lt;/span&gt;&lt;span class="pl-k"&gt;=&lt;/span&gt;&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;&quot;&lt;/span&gt;text&lt;span class="pl-pds"&gt;&quot;&lt;/span&gt;&lt;/span&gt; /&gt;;
}
&lt;/code&gt;&lt;/pre&gt;&lt;figcaption&gt;&lt;p&gt;&lt;a href="https://codesandbox.io/p/sandbox/example-3-4kk4n7?file=%2Fsrc%2FApp.tsx"&gt;CodeSandbox&lt;/a&gt;&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;As we saw earlier, &lt;a href="https://alvaro.cuesta.dev/blog/your-react-refs-might-be-breaking-someone-elses-code/#react-ref-callback-guarantees"&gt;React&amp;#x27;s &lt;code&gt;ref&lt;/code&gt; callback guarantees&lt;/a&gt; ensure this works flawlessly because the callback is &lt;strong&gt;only&lt;/strong&gt; called once (when the &lt;code&gt;&lt;input&gt;&lt;/code&gt; is initially inserted into the DOM). But what happens when we want to support an external &lt;code&gt;ref&lt;/code&gt; from props?&lt;/p&gt;
&lt;figure class="code-with-caption"&gt;&lt;pre&gt;&lt;code class="language-tsx"&gt;&lt;span class="pl-k"&gt;import&lt;/span&gt; { &lt;span class="pl-smi"&gt;mergeRefs&lt;/span&gt; } &lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;&quot;&lt;/span&gt;some-ref-merging-library&lt;span class="pl-pds"&gt;&quot;&lt;/span&gt;&lt;/span&gt;;

&lt;span class="pl-k"&gt;function&lt;/span&gt; &lt;span class="pl-en"&gt;MyInput&lt;/span&gt;({ &lt;span class="pl-v"&gt;externalRef&lt;/span&gt; }&lt;span class="pl-k"&gt;:&lt;/span&gt; { &lt;span class="pl-v"&gt;externalRef&lt;/span&gt;&lt;span class="pl-k"&gt;?:&lt;/span&gt; &lt;span class="pl-en"&gt;Ref&lt;/span&gt;&lt;&lt;span class="pl-en"&gt;HTMLInputElement&lt;/span&gt;&gt; }) {
  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-c1"&gt;focusInput&lt;/span&gt; &lt;span class="pl-k"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;useCallback&lt;/span&gt;((&lt;span class="pl-v"&gt;input&lt;/span&gt;&lt;span class="pl-k"&gt;:&lt;/span&gt; &lt;span class="pl-en"&gt;HTMLInputElement&lt;/span&gt; &lt;span class="pl-k"&gt;|&lt;/span&gt; &lt;span class="pl-c1"&gt;null&lt;/span&gt;) &lt;span class="pl-k"&gt;=&gt;&lt;/span&gt; {
    &lt;span class="pl-smi"&gt;input&lt;/span&gt;?.&lt;span class="pl-c1"&gt;focus&lt;/span&gt;();
  }, []);

  &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;&lt;span class="pl-ent"&gt;input&lt;/span&gt; &lt;span class="pl-e"&gt;ref&lt;/span&gt;&lt;span class="pl-k"&gt;=&lt;/span&gt;&lt;span class="pl-pse"&gt;{&lt;/span&gt;&lt;span class="pl-en"&gt;mergeRefs&lt;/span&gt;([&lt;span class="pl-smi"&gt;externalRef&lt;/span&gt;, &lt;span class="pl-smi"&gt;focusInput&lt;/span&gt;])&lt;span class="pl-pse"&gt;}&lt;/span&gt; &lt;span class="pl-e"&gt;type&lt;/span&gt;&lt;span class="pl-k"&gt;=&lt;/span&gt;&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;&quot;&lt;/span&gt;text&lt;span class="pl-pds"&gt;&quot;&lt;/span&gt;&lt;/span&gt; /&gt;;
}
&lt;/code&gt;&lt;/pre&gt;&lt;figcaption&gt;&lt;p&gt;&lt;a href="https://codesandbox.io/p/sandbox/example-4-w2yfkq?file=%2Fsrc%2FApp.tsx"&gt;CodeSandbox&lt;/a&gt;&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Since &lt;code&gt;mergeRefs&lt;/code&gt; creates a new function on &lt;strong&gt;every render&lt;/strong&gt;, its reference is by definition unstable, causing the same issue we saw earlier. Can we fix it the same way as before?&lt;/p&gt;
&lt;p&gt;Well, &lt;strong&gt;it depends&lt;/strong&gt;.&lt;/p&gt;
&lt;h4 id="external-refs-might-be-breaking-your-code" class="autolink-heading"&gt;External refs might be breaking your code&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/your-react-refs-might-be-breaking-someone-elses-code/#external-refs-might-be-breaking-your-code"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Let&amp;#x27;s fix the issue &lt;a href="https://alvaro.cuesta.dev/blog/your-react-refs-might-be-breaking-someone-elses-code/#logging-fixed"&gt;as we did earlier&lt;/a&gt;, by memoizing the merged ref so its reference is also stable across renders:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-tsx"&gt;&lt;span class="pl-k"&gt;function&lt;/span&gt; &lt;span class="pl-en"&gt;MyInput&lt;/span&gt;({ &lt;span class="pl-v"&gt;externalRef&lt;/span&gt; }&lt;span class="pl-k"&gt;:&lt;/span&gt; { &lt;span class="pl-v"&gt;externalRef&lt;/span&gt;&lt;span class="pl-k"&gt;?:&lt;/span&gt; &lt;span class="pl-en"&gt;Ref&lt;/span&gt;&lt;&lt;span class="pl-en"&gt;HTMLInputElement&lt;/span&gt;&gt; }) {
  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-c1"&gt;focusInput&lt;/span&gt; &lt;span class="pl-k"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;useCallback&lt;/span&gt;((&lt;span class="pl-v"&gt;input&lt;/span&gt;&lt;span class="pl-k"&gt;:&lt;/span&gt; &lt;span class="pl-en"&gt;HTMLInputElement&lt;/span&gt;) &lt;span class="pl-k"&gt;=&gt;&lt;/span&gt; {
    &lt;span class="pl-smi"&gt;input&lt;/span&gt;?.&lt;span class="pl-c1"&gt;focus&lt;/span&gt;();
  }, []);

  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-c1"&gt;ref&lt;/span&gt; &lt;span class="pl-k"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;useMemo&lt;/span&gt;(
    () &lt;span class="pl-k"&gt;=&gt;&lt;/span&gt; &lt;span class="pl-en"&gt;mergeRefs&lt;/span&gt;([&lt;span class="pl-smi"&gt;externalRef&lt;/span&gt;, &lt;span class="pl-smi"&gt;focusInput&lt;/span&gt;]),
    &lt;span class="pl-c"&gt;// (1)&lt;/span&gt;
    [&lt;span class="pl-smi"&gt;externalRef&lt;/span&gt;, &lt;span class="pl-smi"&gt;focusInput&lt;/span&gt;],
  );

  &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;&lt;span class="pl-ent"&gt;input&lt;/span&gt; &lt;span class="pl-e"&gt;ref&lt;/span&gt;&lt;span class="pl-k"&gt;=&lt;/span&gt;&lt;span class="pl-pse"&gt;{&lt;/span&gt;&lt;span class="pl-smi"&gt;ref&lt;/span&gt;&lt;span class="pl-pse"&gt;}&lt;/span&gt; &lt;span class="pl-e"&gt;type&lt;/span&gt;&lt;span class="pl-k"&gt;=&lt;/span&gt;&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;&quot;&lt;/span&gt;text&lt;span class="pl-pds"&gt;&quot;&lt;/span&gt;&lt;/span&gt; /&gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Can you spot the issue?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The problem with that code is that &lt;strong&gt;you don&amp;#x27;t control &lt;code&gt;externalRef&lt;/code&gt;&lt;/strong&gt; so, if it is unstable, the dependencies in &lt;code&gt;(1)&lt;/code&gt; will invalidate and the memoized function will be recreated. If someone uses your component like this:&lt;/p&gt;
&lt;figure class="code-with-caption"&gt;&lt;pre&gt;&lt;code class="language-tsx"&gt;&lt;span class="pl-k"&gt;function&lt;/span&gt; &lt;span class="pl-en"&gt;App&lt;/span&gt;() {
  &lt;span class="pl-k"&gt;const&lt;/span&gt; [&lt;span class="pl-c1"&gt;count&lt;/span&gt;, &lt;span class="pl-c1"&gt;setCount&lt;/span&gt;] &lt;span class="pl-k"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;useState&lt;/span&gt;(&lt;span class="pl-c1"&gt;0&lt;/span&gt;);
  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-en"&gt;increaseCount&lt;/span&gt; &lt;span class="pl-k"&gt;=&lt;/span&gt; () &lt;span class="pl-k"&gt;=&gt;&lt;/span&gt; {
    &lt;span class="pl-en"&gt;setCount&lt;/span&gt;((&lt;span class="pl-v"&gt;count&lt;/span&gt;) &lt;span class="pl-k"&gt;=&gt;&lt;/span&gt; &lt;span class="pl-smi"&gt;count&lt;/span&gt; &lt;span class="pl-k"&gt;+&lt;/span&gt; &lt;span class="pl-c1"&gt;1&lt;/span&gt;);
  };

  &lt;span class="pl-k"&gt;return&lt;/span&gt; (
    &lt;&lt;span class="pl-ent"&gt;div&lt;/span&gt;&gt;
      &lt;&lt;span class="pl-ent"&gt;input&lt;/span&gt; &lt;span class="pl-e"&gt;type&lt;/span&gt;&lt;span class="pl-k"&gt;=&lt;/span&gt;&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;&quot;&lt;/span&gt;text&lt;span class="pl-pds"&gt;&quot;&lt;/span&gt;&lt;/span&gt; /&gt;
      &lt;&lt;span class="pl-c1"&gt;MyInput&lt;/span&gt;
        &lt;span class="pl-c"&gt;// This ref is unstable!&lt;/span&gt;
        &lt;span class="pl-e"&gt;externalRef&lt;/span&gt;&lt;span class="pl-k"&gt;=&lt;/span&gt;&lt;span class="pl-pse"&gt;{&lt;/span&gt;(&lt;span class="pl-v"&gt;input&lt;/span&gt;) &lt;span class="pl-k"&gt;=&gt;&lt;/span&gt; {
          &lt;span class="pl-c1"&gt;console&lt;/span&gt;.&lt;span class="pl-c1"&gt;log&lt;/span&gt;(&lt;span class="pl-smi"&gt;input&lt;/span&gt;);
        }&lt;span class="pl-pse"&gt;}&lt;/span&gt;
      /&gt;
      Clicked &lt;span class="pl-pse"&gt;{&lt;/span&gt;&lt;span class="pl-smi"&gt;count&lt;/span&gt;&lt;span class="pl-pse"&gt;}&lt;/span&gt; times.
      &lt;&lt;span class="pl-ent"&gt;button&lt;/span&gt; &lt;span class="pl-e"&gt;onClick&lt;/span&gt;&lt;span class="pl-k"&gt;=&lt;/span&gt;&lt;span class="pl-pse"&gt;{&lt;/span&gt;&lt;span class="pl-smi"&gt;increaseCount&lt;/span&gt;&lt;span class="pl-pse"&gt;}&lt;/span&gt;&gt;Increase&lt;/&lt;span class="pl-ent"&gt;button&lt;/span&gt;&gt;
    &lt;/&lt;span class="pl-ent"&gt;div&lt;/span&gt;&gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;&lt;figcaption&gt;&lt;p&gt;&lt;a href="https://codesandbox.io/p/sandbox/example-5-kjv6gc?file=%2Fsrc%2FApp.tsx"&gt;CodeSandbox&lt;/a&gt;&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;...React will see a new function on every render, and &lt;strong&gt;your input will be focused every time the user clicks the &lt;em&gt;&quot;Increase&quot;&lt;/em&gt; button&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Of course you could say that your component&amp;#x27;s consumer is breaking your component&amp;#x27;s API and they should just in turn memoize their ref callback as well. That&amp;#x27;s fine, but the problem is that...&lt;/p&gt;
&lt;h4 id="components-using-your-ref-are-breaking-your-code" class="autolink-heading"&gt;Components using your ref are breaking your code&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/your-react-refs-might-be-breaking-someone-elses-code/#components-using-your-ref-are-breaking-your-code"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Since you are aware of this issue, you memoize all your callback refs to avoid breaking someone else&amp;#x27;s code:&lt;/p&gt;
&lt;figure class="code-with-caption"&gt;&lt;pre&gt;&lt;code class="language-tsx"&gt;&lt;span class="pl-k"&gt;import&lt;/span&gt; { &lt;span class="pl-smi"&gt;SomeoneElsesInput&lt;/span&gt; } &lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;&quot;&lt;/span&gt;some-ui-library&lt;span class="pl-pds"&gt;&quot;&lt;/span&gt;&lt;/span&gt;;

&lt;span class="pl-k"&gt;function&lt;/span&gt; &lt;span class="pl-en"&gt;App&lt;/span&gt;() {
  &lt;span class="pl-k"&gt;const&lt;/span&gt; [&lt;span class="pl-c1"&gt;count&lt;/span&gt;, &lt;span class="pl-c1"&gt;setCount&lt;/span&gt;] &lt;span class="pl-k"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;useState&lt;/span&gt;(&lt;span class="pl-c1"&gt;0&lt;/span&gt;);
  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-en"&gt;increaseCount&lt;/span&gt; &lt;span class="pl-k"&gt;=&lt;/span&gt; () &lt;span class="pl-k"&gt;=&gt;&lt;/span&gt; {
    &lt;span class="pl-en"&gt;setCount&lt;/span&gt;((&lt;span class="pl-v"&gt;count&lt;/span&gt;) &lt;span class="pl-k"&gt;=&gt;&lt;/span&gt; &lt;span class="pl-smi"&gt;count&lt;/span&gt; &lt;span class="pl-k"&gt;+&lt;/span&gt; &lt;span class="pl-c1"&gt;1&lt;/span&gt;);
  };

  &lt;span class="pl-c"&gt;// We are good citizens and memoize our ref callbacks&lt;/span&gt;
  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-c1"&gt;inputRef&lt;/span&gt; &lt;span class="pl-k"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;useCallback&lt;/span&gt;((&lt;span class="pl-v"&gt;input&lt;/span&gt;&lt;span class="pl-k"&gt;:&lt;/span&gt; &lt;span class="pl-en"&gt;HTMLInputElement&lt;/span&gt; &lt;span class="pl-k"&gt;|&lt;/span&gt; &lt;span class="pl-c1"&gt;null&lt;/span&gt;) &lt;span class="pl-k"&gt;=&gt;&lt;/span&gt; {
    &lt;span class="pl-c1"&gt;console&lt;/span&gt;.&lt;span class="pl-c1"&gt;log&lt;/span&gt;(&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;&quot;&lt;/span&gt;This should only log once, on mount&lt;span class="pl-pds"&gt;&quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="pl-smi"&gt;input&lt;/span&gt;);
  }, []);

  &lt;span class="pl-k"&gt;return&lt;/span&gt; (
    &lt;&lt;span class="pl-ent"&gt;div&lt;/span&gt;&gt;
      &lt;&lt;span class="pl-c1"&gt;SomeoneElsesInput&lt;/span&gt; &lt;span class="pl-e"&gt;ref&lt;/span&gt;&lt;span class="pl-k"&gt;=&lt;/span&gt;&lt;span class="pl-pse"&gt;{&lt;/span&gt;&lt;span class="pl-smi"&gt;inputRef&lt;/span&gt;&lt;span class="pl-pse"&gt;}&lt;/span&gt; /&gt;
      Clicked &lt;span class="pl-pse"&gt;{&lt;/span&gt;&lt;span class="pl-smi"&gt;count&lt;/span&gt;&lt;span class="pl-pse"&gt;}&lt;/span&gt; times.
      &lt;&lt;span class="pl-ent"&gt;button&lt;/span&gt; &lt;span class="pl-e"&gt;onClick&lt;/span&gt;&lt;span class="pl-k"&gt;=&lt;/span&gt;&lt;span class="pl-pse"&gt;{&lt;/span&gt;&lt;span class="pl-smi"&gt;increaseCount&lt;/span&gt;&lt;span class="pl-pse"&gt;}&lt;/span&gt;&gt;Increase&lt;/&lt;span class="pl-ent"&gt;button&lt;/span&gt;&gt;
    &lt;/&lt;span class="pl-ent"&gt;div&lt;/span&gt;&gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;&lt;figcaption&gt;&lt;p&gt;&lt;a href="https://codesandbox.io/p/sandbox/example-6-fm3ndz?file=%2Fsrc%2FApp.tsx"&gt;CodeSandbox&lt;/a&gt;&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Again, &lt;strong&gt;can you spot the issue?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Even if you memoize your callback refs, &lt;code&gt;SomeoneElsesInput&lt;/code&gt; might merge them with unstable callbacks (possibly even from third-party libraries) and create unstable callbacks. This pattern is common across React&amp;#x27;s ecosystem. The worst part is &lt;strong&gt;you cannot fix it&lt;/strong&gt; from userland without hacks—you would need to patch &lt;code&gt;some-ui-library&lt;/code&gt;, manually track reference equality to ignore spurious callbacks (which is not always possible) or report the issue and wait for them to fix it.&lt;/p&gt;
&lt;p&gt;Technically, merging your refs in this way could be a breaking change per &lt;a href="https://semver.org/"&gt;semver&lt;/a&gt;, but this hidden interaction often violates semantic versioning guarantees without being obvious. Adding a ref (in your own private code) and merging it with an (already-existing) external ref doesn&amp;#x27;t &lt;em&gt;seem&lt;/em&gt; to affect the public API, &lt;strong&gt;does it&lt;/strong&gt;?&lt;/p&gt;
&lt;p&gt;This is &lt;strong&gt;not&lt;/strong&gt; a theoretical concern &lt;em&gt;(guess how I found this issue?)&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="what-can-you-do-about-it" class="autolink-heading"&gt;What can you do about it?&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/your-react-refs-might-be-breaking-someone-elses-code/#what-can-you-do-about-it"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I &lt;a href="https://github.com/gregberge/react-merge-refs/issues/42"&gt;reported the issue&lt;/a&gt; to the fantastic &lt;a href="https://github.com/gregberge/react-merge-refs/"&gt;&lt;code&gt;react-merge-refs&lt;/code&gt;&lt;/a&gt; library, which is widely used in the ecosystem. Unfortunately, many &lt;a href="https://www.npmjs.com/package/merge-refs"&gt;other libraries&lt;/a&gt; implement this small utility too and, to my knowledge, &lt;strong&gt;none of them correctly handle this case&lt;/strong&gt; (many don&amp;#x27;t even support &lt;a href="https://react.dev/blog/2024/12/05/react-19#cleanup-functions-for-refs"&gt;React 19&amp;#x27;s cleanup functions for refs&lt;/a&gt;!).&lt;/p&gt;
&lt;p&gt;As we have seen, this leads to code breaking: (1) unknowingly, (2) at a distance, (3) both by the consumer and the provider, (4) in patch releases and (5) without recourse for the affected parties.&lt;/p&gt;
&lt;p&gt;So I did what any self-respecting engineer would do: &lt;strong&gt;add another library to the mix&lt;/strong&gt;. I published &lt;a href="https://github.com/alvaro-cuesta/react-best-merge-refs"&gt;&lt;code&gt;react-best-merge-refs&lt;/code&gt;&lt;/a&gt; to handle all these edge cases, with thorough &lt;a href="https://github.com/alvaro-cuesta/react-best-merge-refs/blob/main/src/index.test.tsx"&gt;unit&lt;/a&gt; and &lt;a href="https://github.com/alvaro-cuesta/react-best-merge-refs/blob/main/test/examples.test.tsx"&gt;integration&lt;/a&gt; tests.&lt;/p&gt;
&lt;p&gt;As part of this work, I designed the utility with a &lt;strong&gt;very strict API&lt;/strong&gt; to make it difficult to use incorrectly. In particular:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;I only expose &lt;code&gt;useMergeRefs&lt;/code&gt; and &lt;strong&gt;not&lt;/strong&gt; a raw &lt;code&gt;mergeRefs&lt;/code&gt; that would return an always-unstable merged ref. This makes it essentially impossible to break other people&amp;#x27;s code.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I changed the API to take a keyed object.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-ts"&gt;&lt;span class="pl-c"&gt;// This is my signature.&lt;/span&gt;
&lt;span class="pl-k"&gt;function&lt;/span&gt; &lt;span class="pl-en"&gt;useMergeRefs&lt;/span&gt;&lt;&lt;span class="pl-en"&gt;T&lt;/span&gt;&gt;(&lt;span class="pl-v"&gt;refs&lt;/span&gt;&lt;span class="pl-k"&gt;:&lt;/span&gt; &lt;span class="pl-en"&gt;Record&lt;/span&gt;&lt;&lt;span class="pl-c1"&gt;string&lt;/span&gt;, &lt;span class="pl-en"&gt;Ref&lt;/span&gt;&lt;&lt;span class="pl-en"&gt;T&lt;/span&gt;&gt;&gt;)&lt;span class="pl-k"&gt;:&lt;/span&gt; &lt;span class="pl-en"&gt;Ref&lt;/span&gt;&lt;&lt;span class="pl-en"&gt;T&lt;/span&gt;&gt;;
&lt;span class="pl-en"&gt;useMergeRefs&lt;/span&gt;({ &lt;span class="pl-smi"&gt;externalRef&lt;/span&gt;, &lt;span class="pl-smi"&gt;myRef&lt;/span&gt; }); &lt;span class="pl-c"&gt;// &lt;- usage&lt;/span&gt;

&lt;span class="pl-c"&gt;// Other libraries usually do this...&lt;/span&gt;
&lt;span class="pl-k"&gt;function&lt;/span&gt; &lt;span class="pl-en"&gt;useMergeRefs&lt;/span&gt;&lt;&lt;span class="pl-en"&gt;T&lt;/span&gt;&gt;(&lt;span class="pl-v"&gt;refs&lt;/span&gt;&lt;span class="pl-k"&gt;:&lt;/span&gt; &lt;span class="pl-en"&gt;Ref&lt;/span&gt;&lt;&lt;span class="pl-en"&gt;T&lt;/span&gt;&gt;[])&lt;span class="pl-k"&gt;:&lt;/span&gt; &lt;span class="pl-en"&gt;Ref&lt;/span&gt;&lt;&lt;span class="pl-en"&gt;T&lt;/span&gt;&gt;;
&lt;span class="pl-en"&gt;useMergeRefs&lt;/span&gt;([&lt;span class="pl-smi"&gt;externalRef&lt;/span&gt;, &lt;span class="pl-smi"&gt;myRef&lt;/span&gt;]);

&lt;span class="pl-c"&gt;// ...or this.&lt;/span&gt;
&lt;span class="pl-k"&gt;function&lt;/span&gt; &lt;span class="pl-en"&gt;useMergeRefs&lt;/span&gt;&lt;&lt;span class="pl-en"&gt;T&lt;/span&gt;&gt;(&lt;span class="pl-k"&gt;...&lt;/span&gt;&lt;span class="pl-v"&gt;refs&lt;/span&gt;&lt;span class="pl-k"&gt;:&lt;/span&gt; &lt;span class="pl-en"&gt;Ref&lt;/span&gt;&lt;&lt;span class="pl-en"&gt;T&lt;/span&gt;&gt;[])&lt;span class="pl-k"&gt;:&lt;/span&gt; &lt;span class="pl-en"&gt;Ref&lt;/span&gt;&lt;&lt;span class="pl-en"&gt;T&lt;/span&gt;&gt;;
&lt;span class="pl-en"&gt;useMergeRefs&lt;/span&gt;(&lt;span class="pl-smi"&gt;externalRef&lt;/span&gt;, &lt;span class="pl-smi"&gt;myRef&lt;/span&gt;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this design, each ref has a unique key associated with it, which allows it to be tracked over time. This makes it &lt;em&gt;very&lt;/em&gt; hard to use incorrectly and trigger a situation where an external ref might be made unstable under obscure circumstances.&lt;/p&gt;
&lt;p&gt;The problem it solves (and the solution I came up with) is similar to &lt;a href="https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key"&gt;React&amp;#x27;s &lt;code&gt;key&lt;/code&gt; prop&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;See &lt;a href="https://github.com/alvaro-cuesta/react-best-merge-refs/blob/99300615b1c6fc47d8a524b20424447b00c6047c/README.md#why-an-object-for-refs-instead-of-a-simple-array"&gt;my rationale&lt;/a&gt;, where I explain why the usual APIs offered by other libraries (either an array argument or a variadic function taking refs) feel a bit footgun-y to me.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I&amp;#x27;m not 100% convinced that &lt;code&gt;(2)&lt;/code&gt; is the perfect choice, but since the difference in usage is minimal (&lt;code&gt;{}&lt;/code&gt; instead of &lt;code&gt;[]&lt;/code&gt;), I went for it in hopes of learning more through experience. It may only impact efficiency in hot paths, but I strongly believe micro-optimizations should come after correctness. You can always expose an &lt;code&gt;unsafe_mergeRefs&lt;/code&gt; if efficiency is critical.&lt;/p&gt;
&lt;p&gt;I still believe &lt;strong&gt;&lt;a href="https://github.com/facebook/react/issues/29757"&gt;facebook/react#29757&lt;/a&gt; should be built into the framework&lt;/strong&gt;: the implementation is very easy to get wrong and can break with React updates (as happened with &lt;a href="https://react.dev/blog/2024/12/05/react-19#cleanup-functions-for-refs"&gt;React 19&amp;#x27;s introduction of cleanup functions for refs&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;If you want a safer way to merge refs today, check out &lt;a href="https://github.com/alvaro-cuesta/react-best-merge-refs"&gt;&lt;code&gt;react-best-merge-refs&lt;/code&gt;&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Will React ever fix this natively? &lt;em&gt;We can only hope.&lt;/em&gt;&lt;/p&gt;
&lt;hr/&gt;
&lt;h3 id="bonus-track-everything-is-breaking-everything" class="autolink-heading"&gt;(Bonus track) Everything is breaking everything&lt;a class="autolink-link" aria-label="(permalink)" href="https://alvaro.cuesta.dev/blog/your-react-refs-might-be-breaking-someone-elses-code/#bonus-track-everything-is-breaking-everything"&gt;&lt;span class="fas fa-link autolink-icon"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;(The plot thickens.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I&amp;#x27;ll leave these as an exercise for the reader but, &lt;em&gt;trust me&lt;/em&gt;, they are subtly buggy. &lt;strong&gt;Can you spot why?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exhibit A&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-tsx"&gt;&lt;span class="pl-k"&gt;function&lt;/span&gt; &lt;span class="pl-en"&gt;App&lt;/span&gt;() {
  &lt;span class="pl-k"&gt;const&lt;/span&gt; [&lt;span class="pl-c1"&gt;count&lt;/span&gt;, &lt;span class="pl-c1"&gt;setCount&lt;/span&gt;] &lt;span class="pl-k"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;useState&lt;/span&gt;(&lt;span class="pl-c1"&gt;0&lt;/span&gt;);
  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-en"&gt;increaseCount&lt;/span&gt; &lt;span class="pl-k"&gt;=&lt;/span&gt; () &lt;span class="pl-k"&gt;=&gt;&lt;/span&gt; {
    &lt;span class="pl-en"&gt;setCount&lt;/span&gt;((&lt;span class="pl-v"&gt;count&lt;/span&gt;) &lt;span class="pl-k"&gt;=&gt;&lt;/span&gt; &lt;span class="pl-smi"&gt;count&lt;/span&gt; &lt;span class="pl-k"&gt;+&lt;/span&gt; &lt;span class="pl-c1"&gt;1&lt;/span&gt;);
  };

  &lt;span class="pl-c"&gt;// We are good citizens and memoize our ref callbacks&lt;/span&gt;
  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-c1"&gt;inputRef&lt;/span&gt; &lt;span class="pl-k"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;useCallback&lt;/span&gt;(
    (&lt;span class="pl-v"&gt;input&lt;/span&gt;&lt;span class="pl-k"&gt;:&lt;/span&gt; &lt;span class="pl-en"&gt;HTMLInputElement&lt;/span&gt; &lt;span class="pl-k"&gt;|&lt;/span&gt; &lt;span class="pl-c1"&gt;null&lt;/span&gt;) &lt;span class="pl-k"&gt;=&gt;&lt;/span&gt; {
      &lt;span class="pl-c1"&gt;console&lt;/span&gt;.&lt;span class="pl-c1"&gt;log&lt;/span&gt;(&lt;span class="pl-smi"&gt;input&lt;/span&gt;, &lt;span class="pl-smi"&gt;count&lt;/span&gt;);
    },
    [&lt;span class="pl-smi"&gt;count&lt;/span&gt;],
  );

  &lt;span class="pl-k"&gt;return&lt;/span&gt; (
    &lt;&lt;span class="pl-ent"&gt;div&lt;/span&gt;&gt;
      &lt;&lt;span class="pl-c1"&gt;MyInput&lt;/span&gt; &lt;span class="pl-e"&gt;ref&lt;/span&gt;&lt;span class="pl-k"&gt;=&lt;/span&gt;&lt;span class="pl-pse"&gt;{&lt;/span&gt;&lt;span class="pl-smi"&gt;inputRef&lt;/span&gt;&lt;span class="pl-pse"&gt;}&lt;/span&gt; &lt;span class="pl-e"&gt;autoFocus&lt;/span&gt; /&gt;
      Clicked &lt;span class="pl-pse"&gt;{&lt;/span&gt;&lt;span class="pl-smi"&gt;count&lt;/span&gt;&lt;span class="pl-pse"&gt;}&lt;/span&gt; times.
      &lt;&lt;span class="pl-ent"&gt;button&lt;/span&gt; &lt;span class="pl-e"&gt;onClick&lt;/span&gt;&lt;span class="pl-k"&gt;=&lt;/span&gt;&lt;span class="pl-pse"&gt;{&lt;/span&gt;&lt;span class="pl-smi"&gt;increaseCount&lt;/span&gt;&lt;span class="pl-pse"&gt;}&lt;/span&gt;&gt;Increase&lt;/&lt;span class="pl-ent"&gt;button&lt;/span&gt;&gt;
    &lt;/&lt;span class="pl-ent"&gt;div&lt;/span&gt;&gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Exhibit B&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-tsx"&gt;&lt;span class="pl-k"&gt;function&lt;/span&gt; &lt;span class="pl-en"&gt;App&lt;/span&gt;() {
  &lt;span class="pl-k"&gt;const&lt;/span&gt; [&lt;span class="pl-c1"&gt;count&lt;/span&gt;, &lt;span class="pl-c1"&gt;setCount&lt;/span&gt;] &lt;span class="pl-k"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;useState&lt;/span&gt;(&lt;span class="pl-c1"&gt;0&lt;/span&gt;);
  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-en"&gt;increaseCount&lt;/span&gt; &lt;span class="pl-k"&gt;=&lt;/span&gt; () &lt;span class="pl-k"&gt;=&gt;&lt;/span&gt; {
    &lt;span class="pl-en"&gt;setCount&lt;/span&gt;((&lt;span class="pl-v"&gt;count&lt;/span&gt;) &lt;span class="pl-k"&gt;=&gt;&lt;/span&gt; &lt;span class="pl-smi"&gt;count&lt;/span&gt; &lt;span class="pl-k"&gt;+&lt;/span&gt; &lt;span class="pl-c1"&gt;1&lt;/span&gt;);
  };

  &lt;span class="pl-c"&gt;// We are good citizens and memoize our ref callbacks&lt;/span&gt;
  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-c1"&gt;inputRef&lt;/span&gt; &lt;span class="pl-k"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;useCallback&lt;/span&gt;((&lt;span class="pl-v"&gt;input&lt;/span&gt;&lt;span class="pl-k"&gt;:&lt;/span&gt; &lt;span class="pl-en"&gt;HTMLInputElement&lt;/span&gt; &lt;span class="pl-k"&gt;|&lt;/span&gt; &lt;span class="pl-c1"&gt;null&lt;/span&gt;) &lt;span class="pl-k"&gt;=&gt;&lt;/span&gt; {
    &lt;span class="pl-c1"&gt;console&lt;/span&gt;.&lt;span class="pl-c1"&gt;log&lt;/span&gt;(&lt;span class="pl-smi"&gt;input&lt;/span&gt;);
  }, []);

  &lt;span class="pl-k"&gt;return&lt;/span&gt; (
    &lt;&lt;span class="pl-ent"&gt;div&lt;/span&gt;&gt;
      &lt;&lt;span class="pl-c1"&gt;MyInput&lt;/span&gt; &lt;span class="pl-e"&gt;ref&lt;/span&gt;&lt;span class="pl-k"&gt;=&lt;/span&gt;&lt;span class="pl-pse"&gt;{&lt;/span&gt;&lt;span class="pl-smi"&gt;count&lt;/span&gt; &lt;span class="pl-k"&gt;&gt;&lt;/span&gt; &lt;span class="pl-c1"&gt;10&lt;/span&gt; &lt;span class="pl-k"&gt;?&lt;/span&gt; &lt;span class="pl-smi"&gt;inputRef&lt;/span&gt; &lt;span class="pl-k"&gt;:&lt;/span&gt; &lt;span class="pl-c1"&gt;undefined&lt;/span&gt;&lt;span class="pl-pse"&gt;}&lt;/span&gt; &lt;span class="pl-e"&gt;autoFocus&lt;/span&gt; /&gt;
      Clicked &lt;span class="pl-pse"&gt;{&lt;/span&gt;&lt;span class="pl-smi"&gt;count&lt;/span&gt;&lt;span class="pl-pse"&gt;}&lt;/span&gt; times.
      &lt;&lt;span class="pl-ent"&gt;button&lt;/span&gt; &lt;span class="pl-e"&gt;onClick&lt;/span&gt;&lt;span class="pl-k"&gt;=&lt;/span&gt;&lt;span class="pl-pse"&gt;{&lt;/span&gt;&lt;span class="pl-smi"&gt;increaseCount&lt;/span&gt;&lt;span class="pl-pse"&gt;}&lt;/span&gt;&gt;Increase&lt;/&lt;span class="pl-ent"&gt;button&lt;/span&gt;&gt;
    &lt;/&lt;span class="pl-ent"&gt;div&lt;/span&gt;&gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;</content><author><name>Álvaro Cuesta</name><uri>https://alvaro.cuesta.dev</uri></author><category term="react"/><category term="bugs"/><category term="programming"/><category term="javascript"/></entry></feed>