Angular 21 Savage

Issue #443.November 21, 2025.2 Minute read.
Bytes

Today’s issue: Beating Crash Bandicoot 3 with JavaScript, going full squircle, and launching a new VS Code fork every day until we get the Epstein Files.

Welcome to #443.


Eyeballs logo

The Main Thing

A fish from spongebob with glasses looking smug

The Angular team watching everyone complain about React in 2025

Angular 21 Savage

Google’s market cap increased by $170,000,000,000 this week, and I think we can all agree that yesterday’s Angular 21 release is probably the biggest reason why.

It comes with a launch video where Mark Techson gets sucked into a video game like Jumanji, a new MCP server that makes it easier for agents to write better Angular code, and a new Angular mascot that kind of looks like the Hamburger Helper logo had a baby with one of those thumb bad guys from Spy Kids. The Goog is back, baby!

But this release also ships a bunch of long-awaited features that continue to transform Angular into a surprisingly hip and modern framework in 2025. Let’s take a closer look:

  • Signal Forms (experimental) – A new library that lets you manage form state in a fully type-safe way using Signals to provide automatic synchronization between your data model and the UI.

  • Angular Aria – A new component library that prioritizes accessibility and provides 8 UI patterns with 13 unstyled components that all use modern Angular directives and are signal-based and easily customizable.

  • Default Zoneless – With Signals handling state management duties in Angular now, Zone.js and all of its features are no longer included by default in v21. This is a big W for Angular developers looking for better performance in large applications, and a big L for Angular developers who want to pretend it’s still the good old days of 2017 before their hairline betrayed them.

  • Vitest as the new test runner – It’s now stable and ready to use with the ng test command. Vite ecosystem supremacy increases.

Bottom Line: A few years ago, it felt like Angular was getting left behind by the newer cool-kid frameworks on the block. But now, you could argue that it’s more cutting edge than ever thanks to its embrace of Signals, AI tooling, and Vite. Never bet against the Goog.


QA Wolf logo

Our Friends
(With Benefits)

A guy in a devil costume talking to a little kid

The devil tempting me to ship without running my team's janky test suite

Strategies to help your team ship faster with AI in 2026 (webinar)

Capgemini found that 70% of teams don’t have the skills needed to actually benefit from AI tools – especially when it comes to E2E testing and QA automation.

That’s why Sebastian Antonucci, QA Wolf Staff Engineering Lead is hosting a free workshop next month for engineering leaders on the key strategies QA teams need to upskill with AI.

You’ll learn:

  • How the “automation cliff” affects test coverage for your team
  • How to implement prompt-driven test design and AI code review
  • How to train your team to use these tools to be more effective

RSVP for the workshop – and learn how to build a human + AI testing workflow that actually helps your team ship faster.


Spot the Bug logo

Spot the Bug

Sponsored by Apryse

They created this DOCX editor SDK buying guide that explains the technical challenges of working with DOCX, and how to implement editing with JavaScript.

Criteria:

  1. Sort the array in numerical order
  2. If the array has an even number of elements, return the average of the two middle elements
  3. If the array has an odd number of elements, return the middle element
function getMiddleValue(arr) {
  arr.sort((a, b) => a - b);

  if (arr.length % 2 === 0) {
    const mid1 = arr[arr.length / 2 - 1];
    const mid2 = arr[arr.length / 2];
    return (mid1 + mid2) / 2;
  } else {
    return arr[Math.floor(arr.length / 2) - 1];
  }
}

const numbers1 = [5, 3, 8, 4, 2];
const numbers2 = [40, 20, 60, 80, 50, 30];

console.log(getMiddleValue(numbers1)); // 4
console.log(getMiddleValue(numbers2)); // 45

Cool Bits logo

Cool Bits

  1. Kyle Petroski and Matthew Frail wrote about How their team prepares Shopify for BFCM – beyond just the typical burning of sage and all-night seances at Tobi’s penthouse in Toronto.

  2. Here are 4 React Native app ideas to get you into the Meta Horizon Start Dev Competition. Submit by Dec 9, 2025 and take home $25k. [sponsored]

  3. Bramus wrote a tutorial on faking two-phase View Transitions with the Navigation API. “They’re illusions, Michael.”

  4. Google launched Antigravity, and unlike all the other VS Code forks out there, this one is made by Google.

  5. Adam Pietrasiak made this Chrome plugin that adds squircle corners to all elements on every website. Thank you for your service.

  6. CSS Grid has been implemented into Yoga.

  7. Next.js Enterprise Boilerplate is a ready-to-deploy Next.js boilerplate for enterprise projects and fast-iterating teams. [sponsored]

  8. Firefox 145 introduces expanded fingerprint protections to shield users from hidden tracking techniques. Yes of course it’s a losing battle, but it’s still good to try.

  9. Brimstone is a new JavaScript engine written in Rust.

  10. You already tuned your AI agents with .cursorrules, CLAUDE.md, Agents.md, and Copilot-instructions. CodeRabbit reads those guideline files and uses them to enforce code quality in every PR review, so comments line up with the rules you have already written. [sponsored]

  11. The Gram Functions Framework provides a streamlined way to build MCP tools with TypeScript. Now we just need someone to make a streamlined way to use MCP tools and we’ll be set.

  12. You can make PS2 games in JavaScript now, which means I can finally find that hidden gem in Crash Bandicoot 3 by simply rebuilding the entire game myself.


Spot the Bug logo

Spot the Bug: Solution

Sponsored by Apryse

This is a classic example of an off by one error. Let’s step through the logic:

  1. numbers1 gets sorted to be [2, 3, 4, 5, 8] (an odd number of elements)
  2. To get the middle element we need to divide the length of the array by 2 (5 / 2 = 2.5)
  3. Since 2.5 is not a valid index, we need to round down to the nearest whole number, 2
  4. However subtracting 1 gives us the second element in the array, 3, instead of the middle element, 4

The Math.floor effectively did the same thing as subtracting 1, so removing it fixes the bug.

function getMiddleValue(arr) {
  arr.sort((a, b) => a - b);

  if (arr.length % 2 === 0) {
    const mid1 = arr[arr.length / 2 - 1];
    const mid2 = arr[arr.length / 2];
    return (mid1 + mid2) / 2;
  } else {
    return arr[Math.floor(arr.length / 2)]; // remove -1
  }
}

const numbers1 = [5, 3, 8, 4, 2];
const numbers2 = [40, 20, 60, 80, 50, 30];

console.log(getMiddleValue(numbers1)); // 4
console.log(getMiddleValue(numbers2)); // 45