Angular 20.1 has landed, bringing along a handful of interesting additions. Here's a rundown of the most significant updates:

🧠 Signals in DevTools

The Angular DevTools operates at the component level. That means you first pick a component, enable the SignalGraph, and only then can you inspect the signals alongside their contextual metadata.

This covers signal(), computed(), linkedSignal(), and even effect().

Keep in mind, this is still in an experimental phase. As such, the resource isn't shown as a single entity but rather as a cluster of individual signals.

There's also the option to label your signals using the debugName property.

Example:

// Define a signal with a debug name for easier DevTools debugging
const number = signal(1, {
  debugName: 'number',
});

// Create a computed signal that doubles the `number` signal
const double = computed(() => number() * 2, {
  debugName: 'doubleNumber',
});

// Set up an effect to log the value of `double` whenever it changes
effect(() => console.log(double()), {
  debugName: 'logger',
});
Enter fullscreen mode Exit fullscreen mode

🌐 Enhanced httpResource & HttpClient

Both the HttpClient and, by extension, the httpResource (which relies on HttpClient internally) got a welcome boost.

They now support passing through options exposed by the native fetch function. That includes handling for timeouts, caching, redirects, and more.

Example:

// Define an httpResource with native fetch options passed through
httpResource(() => ({
  url: `/holiday/1/quiz`,
  priority: 'high',
  redirect: 'follow',
  cache: 'no-cache',
  credentials: 'same-origin',
  mode: 'no-cors',
}));

// Equivalent call using HttpClient directly with fetch-like options
this.httpClient.get('/holiday/1/quiz', {
  priority: 'high',
  redirect: 'follow',
  cache: 'no-cache',
  credentials: 'same-origin',
  mode: 'no-cors',
});
Enter fullscreen mode Exit fullscreen mode

🧪 Testing with Bindings

Testing a component that relies on property bindings previously meant either crafting a wrapper component to mimic a parent or going through ComponentRef.setInput().

The new TestBed.createComponent() method now accepts a second argument to handle bindings directly.

This works for more than just property bindings — event bindings and two-way bindings are covered as well.


Take a component that has two property bindings, timeLeft and status:

export class QuizStatusComponent {
  timeLeft = input.required<number>();
  status = input.required<{ correct: number; incorrect: number }>();
}
Enter fullscreen mode Exit fullscreen mode

The previous approach involved using the componentRef:

it('should show the time left', async () => {
  TestBed.configureTestingModule({
    providers: [provideZonelessChangeDetection()],
  }).createComponent(QuizStatusComponent);

  // Setting the inputs
  fixture.componentRef.setInput('timeLeft', 10);
  fixture.componentRef.setInput('status', { correct: 0, incorrect: 0 });

  const timeLeft = await screen.findByLabelText('Time remaining');
  expect(timeLeft.textContent).toContain('Time Left: 10 seconds');
});
Enter fullscreen mode Exit fullscreen mode

With Angular 20.1, we can achieve this far more cleanly through createComponent:

it('should show the time left', async () => {
  TestBed.configureTestingModule({
    providers: [provideZonelessChangeDetection()],
  }).createComponent(QuizStatusComponent, {
    bindings: [
      inputBinding('timeLeft', () => 10),
      inputBinding('status', () => ({ correct: 0, incorrect: 0 })),
    ],
  });

  const timeLeft = await screen.findByLabelText('Time remaining');
  expect(timeLeft.textContent).toContain('Time Left: 10 seconds');
});
Enter fullscreen mode Exit fullscreen mode

🤖 Angular CLI and MCP

For AI-driven development workflows — which by now we're all presumably using — the Angular CLI ships with an MCP server.

After registering it in your IDE, you can start generating Angular code or ask Angular-specific questions straight from the editor.

Depending on your setup, registering the MCP server in Cursor involves something like this:

mcp.json

{
  "mcpServers": {
    "angular-cli": {
      "command": "npx",
      "args": ["@angular/cli", "mcp"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

🧩 Odds and Ends

There are moments when you need to update or assign property values upon a specific event. If that's a quick one-liner, you can now do it right in the template — thanks to a syntax extension that accommodates various assignment operators.

Given that signals represent the recommended path and logic has no place in templates, this is likely an uncommon scenario.

Finally, TypeScript now allows importing image files, which can then be base64-encoded and used in templates.

Before:

@Component({
  template: `
    <img alt="Eternal" src="assets/logo.png" />
  `
})
export class Header {}
Enter fullscreen mode Exit fullscreen mode

After:

import logo from '../../../assets/logo.png' with { loader: 'base64' };

@Component({
  template: `
    <img alt="Eternal" [src]="logo" />
  `,
})
export class Header {
  protected readonly logo = `data:image/png;base64,${logo}`;
}
Enter fullscreen mode Exit fullscreen mode

Admittedly, the older form is more concise — however, the new approach bakes the image straight into the component, eliminating the need for a separate load.