How I became a conference speaker (and how you can too!)
This is how I got into conference speaking as a software engineer, and how you can too.
Developer happiness, aiming for the next level!
Frank Merema - 7 min read
Angular 19 has just been released and there are many nice features to explore. This time they spent again a lot of effort improving developers experience and listening carefully to all the wishes coming from the community. Get ready for a deep-dive first in the Dev experience improvements.
Let's start looking at which features actually help improve the developer experience in Angular 19.
Starting from Angular 19 all components are now by default standalone and you need to explicitly say standalone: false
to make them part of a module.
When using the Angular CLI the ng update
command will automatically add or remove the standalone flag based on the current status of the component.
If it's standalone already, remove the flag. If a module component, add the flag with the value false.
Starting with looking at the current status of the signals API. We see that many APIs have been promoted to be stable, and some new kids on the block.
The following have reached the stable status:
With APIs reaching stable there is room for new APIs to take the stage, so let's take a look at the new kids.
You can use linkedSignal to create local state just like with Signal. The difference is that this state is linked to a computed expression which provides an initial value. You can write new values to linkedSignal just like a regular signal. But when the linked computed expression changes, the linkedSignal returns to that computed value. More info on linkedSignal at angular.dev/guide/signals/linked-signal
@Component({/* ... */}) export class ShippingMethodPicker { shippingOptions: Signal<ShippingMethod[]> = getShippingOptions(); // Select the first shipping option by default. selectedOption = signal(this.shippingOptions()[0]); changeShipping(newOptionIndex: number) { this.selectedOption.set(this.shippingOptions()[newOptionIndex]); } } // Source Angular.dev
The resource function lets you work with asynchronous values, such as data from your server as part of Angular's signal graph. When you create a resource you specify a computed expression that returns a request object. This expression produces a new value whenever its signal dependencies change. This request object then goes to the specified async loader function, which can use any data client or similar asynchronous API. Resources provide you both the result of the request and the resource status as signals, meaning they work seamlessly with the rest of Angular's reactivity system. More info on the resource API at angular.dev/guide/signals/resource
import {resource, Signal} from '@angular/core'; const userId: Signal<string> = getUserId(); const userResource = resource({ // Define a reactive request computation. // The request value recomputes whenever any read signals change. request: () => ({id: userId()}), // Define an async loader that retrieves data. // The resource calls this function every time the `request` value changes. loader: ({request}) => fetchUser(request), }); // Created a computed based on the result of the resource's loader function. const firstName = computed(() => userResource.value().firstName); // Source angular.dev
With Angular 19, the option to decide how the server should handle the rendering of a route is shipped in developer preview. By default, Angular will server-side render all the parametrized routes in your app and prerender all routes without parameters.
When defining routes you can choose to use the ServerRoute
interface provided by the framework and specify how routes should be rendered.
export const serverRoutes: ServerRoute[] = [ { path: '/home', mode: RenderMode.Client }, { path: '/login', mode: RenderMode.Server }, { path: '/**', mode: RenderMode.Prerender }, ];
For the above code we will specify how different routes should be rendered. The home path should be rendered only on the client side, whereas the login route should always be rendered on the server side. All other routes will be pre-rendered More info on route-level render mode at angular.dev/guide/hybrid-rendering
The signal input migration: converts inputs using the decorator API to the new signal-based inputs.
ng g @angular/core:signal-input-migration
The signal queries migration: swaps queries like viewChild and ContentChild with their signal-based equivalent.
ng g @angular/core:signal-queries-migration
The output migration: migrates components to the new function-based Output API.
ng g @angular/core:output-migration
To run them all at once there is this magic script but run with caution! If you have a big project, it might impact code that you wouldn't expect, making it hard to debug and track the issues. When doing it one-by-one it's easier to track the changes and impact.
ng generate @angular/core:signals
All these features are adopted in the Angular Language Service. This lets you run refactoring actions on your code directly in your favorite IDE.
In the newest release, the theming system is significantly simplified. Resulting in less code for creating
custom themes and better to maintain code that’s easier to understand. What used to take hundreds of lines can
now be accomplished with just one call to mat.theme
. This new API uses a handful of application-wide CSS variables
to control the styles of all the Angular Material components. It enables you to change styles at the application
level by using mat.theme
override mixin.
@use '@angular/material' as mat; html { @include mat.theme(( color: ( primary: $fv-orange, ), )); }
When you need even more fine-grained control, you have mixins available for all individual Angular Material components.
html { @include mat.card-overrides(( elevated-container-color: red, elevated-container-shape: 32px, title-text-size: 2rem, )); }
Maybe you want to create your own theme? A new theme color schematic is available which generates your own color palette. Also new is this beginner-friendly theming guide, providing a clear and easy path covering how to theme your applications. More info on More info on theming your apps at material.angular.io
ng g @angular/material:color-theme
One small side note: if you want to use all these awesome new features, you need to be on Angular 19 and using Material 3.
Finally, after ages there is this brand new time-picker component, ready to be used.
Let's start with a small look back on the previous releases before deep-diving into Incremental Hydration:
Full application hydration requires your application to be hydrated all at once, which also means that the JavaScript that supports the HTML is also present. The Deferrable views reduce some of that JavaScript, but the trade-off is that the rendered placeholder block content is different from the eventual main content.
To render the contents on the server instead of the placeholder would mean we still need to fetch all JavaScript needed to render the contents and that negates the entire purpose of defer blocks. Incremental Hydration beats this challenge by rendering the template on the server side to look how it should after the defer block is triggered. Then, on the client side, leave that content dehydrated, skipping the need to fetch the JavaScript.
Defer blocks have a set of triggers, which you’re already probably familiar with — immediate, idle, timer, interaction, viewport and hover
.
So in your template you can add @defer
and add one of these triggers. This trigger specifies when the deferrable view dependencies are fetched.
Once they’ve been fetched the placeholder content is replaced by the main content. Incremental Hydration builds on this functionality and adds a new set of triggers,
Hydrate Triggers
. These are exactly the same as the ones you’re already familiar with, except one new trigger: never
.
@defer (hydrate on viewport) { <large-cmp /> } @placeholder { <div>Large component placeholder</div> }
This signals to the server that the main block should be rendered and not the placeholder, resulting in the server fetching the defer block dependencies. Specifically, do all of this on the server but not on the client. Angular will leave the block dehydrated until the hydrate trigger fires. So it’s loaded eagerly on the server, but it’s deferred on the client. That results in your client-side JavaScript bundle being much smaller and faster to bootstrap while also having no temporary placeholder block contents. Finally, when your content hydrates, there’s no layout shift or flickering that occurs, just like full-application hydration.
Knowing this, one question pops up: “what happens if a user wants to interact with the content that’s dehydrated?”. Here Event Replay comes into play. Event Replay captures events and queues them up to be replayed later. This ensures that all interactions with the dehydrated content are captured, trigger hydration, get replayed when your content is ready and none of your user actions are left behind.
We already briefly touched the brand new never
trigger, lets check this one out a bit more. There may be cases where
you have content that doesn’t need to be hydrated, like static content from a blog. It ensures that the content inside
that block never triggers hydration and, because of that, never needs to ship any JavaScript on the initial load.
This gives you a new, great way to reduce the overhead of the static content in your apps. One small note: during a subsequent
client-side render, a @defer
block with hydrate never
would still fetch dependencies, as hydration only applies to
initial load of server-side rendered content. To start using it, just add withIncrementalHydration()
to your
provideClientHydration()
call and you're all set. More info on incremental hydration at angular.dev/guide/incremental-hydration
I'm really looking forward playing around with all these new features and seeing how the automatic schemas will transform my app into a full standalone, signal based application in a breeze! Let's hope there will be more awesome new features that land in the Angular ecosystem in the coming years!