Today’s issue: WebKit cat movies, request header fan fic, and what it feels like to be lighter than a React button.
Welcome to #381.
We choose to upgrade to Vue 3 not because it is easy, but because we were promised it would be easy
The State of Vue.js 2025 Report just came out, and I was relieved to see that Evan You has not imposed any reciprocal tariffs on the React ecosystem 🙏.
But it turns out there is a lot more happening in Vue-land these days than meets the eye. Let’s break down the good, the bad, and the surprising:
Vue 3 adoption is going great. After a harder-than-expected migration from Vue 2, 96% of survey takers say that they’ve used Vue 3 in a project in the last 12 months. And Vue 3 seems to be making good on its promise of improving performance (it’s 55% faster) and providing powerful new features that developers actually use. And yet…
Vue 3 adoption also isn’t going that great. The Vue 3 migration was still voted the #1 pain point in the Vue ecosystem by a wide margin, and 35% of survey takers admitted to still using Vue 2 in the last year. That’s nothing to be ashamed of, but it’s less than ideal, considering that Vue 2 hit EOL over 15 months ago and is no longer getting security updates.
Pinia has taken over state management. Over 80% of Vue developers now report using Pinia for global state management, which has firmly taken the crown from the previously dominant Vuex library. Pinia’s simple DX, greater type safety, and more modular approach has been a big W for the Vue crew.
Perf is improving but still needs work. Vue 3 helped address some of the framework’s performance issues, but there’s definitely still room for improvement. Developers report getting most frustrated when using Vue in large TypeScript codebases and applications with a lot of reactivity. Which brings us to our last point…
The future of Vue is Vaporware Vapor Mode. This new compilation strategy and runtime is expected to launch (experimentally) in Vue 3.6. It allows you to use the same API and write your Vue code the same way, but the Vue compiler will compile it differently for various compilation outputs. The Vue team expects this to dramatically improve rendering performance and decrease memory usage – but they’re wisely prioritizing compatibility over everything else, so they’re going slow and steady with the implementation.
Bottom Line: The Vue vibes are still going strong after 10 years, and with Vapor Mode, Evan You says he wants to prove that “we can introduce significant internal changes and innovation without a big breakage again.” Hopefully, we’ll be able to say the same thing about the world economy 6 months from now.
![]() ![]() ![]() ![]() |
When I try to vibe code at my real job
Which means you can actually use it at your real job, instead of just for vibe-coding side projects.
Augment is available in VS Code and JetBrains, and its Context Engine analyzes your entire codebase in real time, so that all of its recommendations have proper context.
That’s how it ranks #1 in various AI code quality benchmarks, while also providing multi-file edits, full PR creation, and other cool features:
Persistent Memory helps it learn your coding style, remember your previous refactors, and adjust to your infra over time.
Integrated workflows with GitHub, Jira, and 100+ MCP tools let you go from ticket ➡️ code ➡️ PR without switching tools.
Visual debugging lets you drag in a screenshot, then have Augment identify all the UI issues, suggest fixes, and run the relevant tests.
Try out the developer plan for free – it comes with all the features and unlimited usage, no credits required.
Their advanced PDF manipulation library lets you easily embed best-in-class document editing into your web, mobile, and desktop applications.
function reduceLanguages (str, lang, i) {
if (i === this.languages.length - 1) {
return `${str} and ${lang}.`;
}
return `${str} ${lang},`;
}
const user = {
name: "Tyler",
age: 27,
languages: ["JavaScript", "Ruby", "Python"],
greet() {
const hello = `Hello, my name is ${this.name} and I know`;
const langs = this.languages.reduce(reduceLanguages, "");
return hello + langs;
},
};
const greeting = user.greet()
The Safari team just unveiled Item Flow, a new concept for layout that would incorporate Grid, Masonry, and Flexbox into one unified set of properties. I’m told it has nothing to do with that creepy cat movie that won the Best Animated Film Oscar.
Datadog created this Digital Experience Monitoring Solution Brief, which shows you how to use Datadog as the single source of truth for all your frontend monitoring data – and simplify how your team runs tests and fixes issues. [sponsored]
Astro 5.6 comes with a bunch of improved Cloudflare integrations because they’re probably getting acquired soon or something.
Lean Rada wrote about how to create minimal, CSS-only blurry image placeholders.
Nue is a “standards-first” framework with a Don Draper-inspired catch phrase: “Apps lighter than a React button.” Body shaming is not a victimless crime, but we’ll let it slide this time.
Mat Biilmann wrote about 10 years of Netlify, and once again tries to teach us all what Jamstack means.
Anime.js just released v4 of its animation library with a new modular API, big perf upgrades, scroll-linked animations, and lots more.
Alex MacArthur wrote about Forbidden Request Headers, which I know sounds like Harry Potter + web dev fan-fic, but sadly it is not.
If you run the original code, you’ll get “Uncaught TypeError: Cannot read properties of undefined (reading ‘length’)“. The only place we’re using .length
is inside our reduceLanguages
function, so we can infer that this.languages
is undefined
and we’re incorrectly using the this
keyword.
Whenever you’re trying to figure out what the this
keyword is referencing, you need to look at where the function using the this
keyword is being invoked. In our example, we don’t know since it’s in the implementation of reduce
. To make sure reduceLanguages
gets invoked in the right context and therefore has the correct this
binding, we have two choices. First, the old school way - using .bind
.
greet() {
const hello = `Hello, my name is ${this.name} and I know`;
const langs = this.languages.reduce(reduceLanguages.bind(this), "");
return hello + langs;
}
Second, the better way - using an arrow function.
greet() {
const hello = `Hello, my name is ${this.name} and I know`;
const langs = this.languages.reduce((str, lang, i) => {
if (i === this.languages.length - 1) {
return `${str} and ${lang}.`;
}
return `${str} ${lang},`;
}, "");
return hello + langs;
}
The reason this works is because with arrow functions, this
is determined lexically. Arrow functions don’t have their own this
. Instead, just like with variable lookups, the JavaScript interpreter will look to the enclosing (parent) scope to determine what this
should reference.