Today’s issue: PNG nostalgia bait, Angular’s fight against AI slop, and my Tailwind-inspired campaign slogan for student body treasurer.
Welcome to #404.
Me deciding if I should make another 'rhymes-with-feet' joke
Have you noticed that the JavaScript framework wars feel a lot less war-like these days?
There are a few different reasons for that, but if you’re looking for one thing to blame for the lack of drama, I’d start with Vite.
The build tool/dev server/plugin platform now forms the shared foundation for nearly every modern JS framework not named Next.js
or Remix
(though Remix has optional Vite support). And with fewer build tools to fight over, along with plugins and infrastructure that can be shared across projects, there’s just not as much framework beef as there used to be 😔.
That’s probably going to become a new normal, because Vite’s dominance is still growing. They just released Vite 7.0 on Tuesday and announced that their weekly npm downloads have almost doubled to 31 million since their last major release in November.
Compared to the feature-packed Vite 6, this v7 release feels more like a transitional update that’s laying the groundwork for some bigger things to come – but there’s a few noteworthy improvements.
The biggest under-the-hood upgrade is experimental support for Rolldown, their new Rust-powered bundler that’ll eventually replace Rollup as Vite’s default engine. It’s still opt-in only for now, but we already wrote about how early adopters have seen some big perf wins.
Here are a few more highlights:
Node 18 is out – Vite 7 requires Node 20.19+ or 22.12+, and ships as ESM-only with no CommonJS fallbacks. That might ruffle a few feathers for legacy holdouts, but it sets the stage for a leaner, more modern ecosystem.
The default browser target changed from modules
to baseline-widely-available
– improving performance and compatibility with Chrome 107+, Safari 16+, and friends.
A new buildApp
hook lands in the Environment API, giving plugin authors more control over how apps are bundled and served.
Bottom Line: OK fine, Vite might not have eliminated all drama from the JavaScript ecosystem (thank goodness). But it’s undoubtedly shifting the landscape of where the fiercest competition is taking place, while simultaneously making it easier than ever for JS libraries and frameworks to actually work together.
![]() ![]() ![]() ![]() |
Trying to fix the backend of the app you vibe-coded
Most AI coding tools can whip up a decent frontend, but the backend is always a boulevard of broken dreams struggle.
That’s why Convex built Chef – an AI agent that turns your prompts into a production-ready app with a functional backend, database, auth, file storage, cron jobs, and a deployed frontend.
How though? Because it’s built on Convex’s AI-optimized reactive database, where everything (frontend and backend) is written in pure TypeScript. This enables Chef to generate high-quality, type-safe code that just works on the first try.
Here’s what else Chef gets you:
One-click deploys with a convex.app link
Built-in auth with zero config required
Beautiful dashboards for managing data, logs, and storage
Try it for free – and start vibe-coding full-stack apps that all the scary backend engineers you know would be proud of.
They created this App Builder Product Brief that breaks down how your team can automate manual engineering processes at scale, create self-service apps for specific functions, and lots more.
function eventuallyFail(time) {
setTimeout(() => {
throw Error("Oops!")
}, time)
}
try {
eventuallyFail(1000)
} catch (error) {
console.error(error.message)
}
Darius Cepulis wrote about how Tailwind is the worst form of CSS, except for all the others, which is why you should embrace it. Coincidentally, this was the exact campaign slogan I used when I ran for student body treasurer in high school. (I lost horribly.)
Turbopack is now passing 100% of Next.js test suites, which means that yes, we are officially turbo yet.
Optimeist automatically optimizes your AWS Lambdas to reduce costs. You just drop in their generated API key, and it instantly detects your Lambdas and fine-tunes their memory, timeout, and configs based on real usage – no manual tweaks required. It’s basically free money. [sponsored]
After 20 years, a new PNG spec was just released in the same week we got announcements for Ratatouille 2 and The Social Network 2. Nostalgia bait remains undefeated.
Cloudflare Containers are now in public beta, allowing you to run new kinds of apps alongside your Workers.
Warp’s new coding agent just landed the #1 spot for Terminal Bench (over 20% ahead of Claude Code) and clocked a 71% on SWE-Bench Verified. Their engineering team wrote this article about how they did it. [sponsored]
The Next.js team released a new guide to linking and navigating.
The Angular team is doing their best to reduce the AI slop in the world by curating content and resources for more accurate code generation for Angular and LLMs. Thanks for fighting the good fight, brothers.
Prettier 3.6 comes with a new experimental high-performance CLI and new plugins for OXC and Hermes.
Kyle Shevlin wrote about preferring gaps to margins, which he says “is so obvious to me that it’s honestly challenging to write about.” Now you know how I feel writing rhymes-with-feet jokes every six months.
function eventuallyFail (time) {
setTimeout(() => {
throw Error("Oops!")
}, time)
}
try {
eventuallyFail(1000)
} catch (error) {
console.error(error.message)
}
try/catch
is synchronous. By the time our error is throw
n, the try/catch
block will be long gone - leaving us with Uncaught Error: Oops!
. To fix this, we want to rely on promises which allow us more async control.
function eventuallyFail (time) {
return new Promise((res, rej) => {
setTimeout(() => {
rej(Error("Oops!"))
}, time)
})
}
eventuallyFail(1000)
.catch((reason) => console.error(reason.message))