Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Modern Angular Apps in 2026

Modern Angular Apps in 2026

Angular has changed significantly in recent years—signals, zoneless change detection, standalone components, typed forms, new router APIs, the signal store, and much more have revolutionized the way we build Angular applications.

But what will a modern Angular app really look like in 2026? Which concepts are “best practice” today – and which belong in the past?

In this talk, we'll take a look at the latest features of the Angular ecosystem and show how they work together to develop high-performance, scalable, and maintainable applications.

Using practical examples, you'll learn:

- How modern architectural approaches work with routes, standalone components, and feature modules
- How signals and the signal store simplify state management
- How to get the most out of your codebase with the new Angular tools, Nx, and TypeScript

Whether you want to modernize existing projects or build new applications that are future-proof, after this talk you will know what modern Angular apps will look like in 2026.

Avatar for Fabian Gosebrink

Fabian Gosebrink

January 30, 2026
Tweet

More Decks by Fabian Gosebrink

Other Decks in Programming

Transcript

  1. export const appConfig: ApplicationConfig = { providers: [ provideZoneChangeDetection({ eventCoalescing:

    true }), provideRouter( routes, withComponentInputBinding(), withViewTransitions() ), provideHttpClient(), provideToastr({ positionClass: 'toast-bottom-right' }), // ... ], }; 1 2 3 4 5 6 7 8 9 10 11 12 13
  2. export const appConfig: ApplicationConfig = { providers: [ provideRouter( routes,

    withComponentInputBinding(), withViewTransitions() ), provideToastr({ positionClass: 'toast-bottom-right' }), // ... ], }; 1 2 3 4 5 6 7 8 9 10 11
  3. @for (item of items(); track item.name) { <li> {{ item.name

    }}</li> } @empty { <li> There are no items. </li> } 1 2 3 4 5
  4. @if (a > b) { {{a}} is greater than {{b}}

    } @else if (b > a) { {{a}} is less than {{b}} } @else { {{a}} is equal to {{b}} } 1 2 3 4 5 6 7
  5. $count Number of items in a collection iterated over $index

    Index of the current row $first Whether the current row is the first row $last Whether the current row is the last row $even Whether the current row index is even $odd Whether the current row index is odd 1 2 3 4 5 6
  6. @Component({ // ... }) export class ProductComponent { product =

    input.required<Product>(); clicked = output<string>(); cartClicked = output<Product>(); } 1 2 3 4 5 6 7 8 9
  7. functional guards export const authGuard: CanActivateFn = () => {

    const auth = inject(AuthService); return auth.isAuthenticated(); }; 1 2 3 4 5
  8. input binding // routes { path: 'user/:id', component: UserDetail }

    // app.config.ts provideRouter(routes, withComponentInputBinding()) // Component @Component({...}) export class UserDetail { id = input(''); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14
  9. @Component({ selector: 'lib-dog-detail', imports: [RouterLink, NgOptimizedImage, DatePipe, DecimalPipe], providers: [...],

    templateUrl: './dog-detail.component.html', styleUrls: ['./dog-detail.component.scss'], }) export class DogDetailComponent { // ... } 1 2 3 4 5 6 7 8 9 10
  10. @Component({ selector: 'lib-main-dog', templateUrl: './main-dog.component.html', styleUrls: ['./main-dog.component.scss'], providers: [...], imports:

    [DogListComponent, DogRateComponent], }) export class MainDogComponent { // ... } 1 2 3 4 5 6 7 8 9 10
  11. feature-based src/ ├── features/ │ ├── user/ │ ├── products/

    │ └── dashboard/ └── shared/ ├── .../ 1 2 3 4 5 6 7 8
  12. feature structure feature └── user ├── container ├── presentational ├──

    domain │ ├── state │ ├── models │ └── ... └── utils 1 2 3 4 5 6 7 8 9
  13. { path: 'dogs', loadChildren: () => import('@dog-rating/dogs/feature').then((m) => m.DOGS_ROUTES), },

    { path: 'about', loadChildren: () => import('@dog-rating/about/feature').then((m) => m.ABOUT_ROUTES), }, { path: '**', pathMatch: 'full', redirectTo: 'dogs', }, export const APP_ROUTES: Routes = [ 1 { 2 path: '', 3 component: LayoutComponent, 4 children: [ 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ], 21 }, 22 ]; 23
  14. children: [ { path: 'products', loadComponent: () => import( './features/products/container/products/products.component'

    ).then((c) => c.ProductsComponent), }, { path: 'products/:id', loadComponent: () => import( './features/product-detail/container/product-detail/product-detail.component' ).then((c) => c.ProductDetailComponent), }, { path: 'checkout', loadComponent: () => import( './features/checkout/container/checkout/checkout.component' ).then((c) => c.CheckoutComponent), }, { path: '**', pathMatch: 'full', redirectTo: 'products', }, ], export const routes: Routes = [ 1 { 2 path: '', 3 component: ShellComponent, 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 }, 33 ]; 34
  15. @Component({ selector: 'lib-single-dog', imports: [RouterLink, DecimalPipe, DatePipe], templateUrl: './single-dog.component.html', styleUrls:

    ['./single-dog.component.scss'], }) export class SingleDogComponent { dog = input<Dog | null>(); dogDeleted = output<Dog>(); } 1 2 3 4 5 6 7 8 9 10 11
  16. container manage the data react to child events entry point

    for feature # less than presentationals
  17. @Component({ selector: 'lib-main-dog', templateUrl: './main-dog.component.html', styleUrls: ['./main-dog.component.scss'], providers: [MainDogStore], imports:

    [DogListComponent, DogRateComponent], }) export class MainDogComponent { dogId = input(''); store = inject(MainDogStore); constructor() { this.store.selectDog(this.dogId); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
  18. todos = signal<Todo[]>([]); count = computed(() => todos().length); effect(() =>

    { console.log('Todos count changed to ', count()); }); 1 2 3 4 5 6 7
  19. todos = signal<Todo[]>([]); count = computed(() => todos().length); effect(() =>

    { console.log('Todos count changed to ', count()); }); 1 2 3 4 5 6 7 count = computed(() => todos().length); todos = signal<Todo[]>([]); 1 2 3 4 effect(() => { 5 console.log('Todos count changed to ', count()); 6 }); 7
  20. todos = signal<Todo[]>([]); count = computed(() => todos().length); effect(() =>

    { console.log('Todos count changed to ', count()); }); 1 2 3 4 5 6 7 count = computed(() => todos().length); todos = signal<Todo[]>([]); 1 2 3 4 effect(() => { 5 console.log('Todos count changed to ', count()); 6 }); 7 effect(() => { console.log('Todos count changed to ', count()); }); todos = signal<Todo[]>([]); 1 2 count = computed(() => todos().length); 3 4 5 6 7
  21. todos = signal<Todo[]>([]); console.log('Todos: ' + todos()); // Reactive in

    TS effect(() => { console.log('Todos', todos()); }); // Reactive in HTML <div>{{ todos() }}</div> 1 2 3 4 5 6 7 8 9 10 11
  22. todos = signal<Todo[]>([]); console.log('Todos: ' + todos()); // Reactive in

    TS effect(() => { console.log('Todos', todos()); }); // Reactive in HTML <div>{{ todos() }}</div> 1 2 3 4 5 6 7 8 9 10 11 // Reactive in TS effect(() => { console.log('Todos', todos()); }); // Reactive in HTML <div>{{ todos() }}</div> todos = signal<Todo[]>([]); 1 2 console.log('Todos: ' + todos()); 3 4 5 6 7 8 9 10 11
  23. todos = signal<Todo[]>([]); count = computed(() => todos().length); effect(() =>

    { console.log('Todos count changed to ', count()); }); 1 2 3 4 5 6 7
  24. todos = signal<Todo[]>([]); count = computed(() => todos().length); effect(() =>

    { console.log('Todos count changed to ', count()); }); 1 2 3 4 5 6 7 count = computed(() => todos().length); todos = signal<Todo[]>([]); 1 2 3 4 effect(() => { 5 console.log('Todos count changed to ', count()); 6 }); 7
  25. todos = signal<Todo[]>([]); count = computed(() => todos().length); effect(() =>

    { console.log('Todos count changed to ', count()); }); 1 2 3 4 5 6 7 count = computed(() => todos().length); todos = signal<Todo[]>([]); 1 2 3 4 effect(() => { 5 console.log('Todos count changed to ', count()); 6 }); 7 effect(() => { console.log('Todos count changed to ', count()); }); todos = signal<Todo[]>([]); 1 2 count = computed(() => todos().length); 3 4 5 6 7
  26. @Component({ selector: 'app-root', template: ` <button (click)="increaseCounter()"> Increase counter </button>

    {{ counter() }} `, }) export class App { counter = signal(0); increaseCounter(){ this.counter.update((counter) => counter+1) } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
  27. @Component({ selector: 'app-root', template: ` <button (click)="increaseCounter()"> Increase counter </button>

    {{ counter() }} `, }) export class App { counter = signal(0); increaseCounter(){ this.counter.update((counter) => counter+1) } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 {{ counter() }} counter = signal(0); @Component({ 1 selector: 'app-root', 2 template: ` 3 <button (click)="increaseCounter()"> 4 Increase counter 5 </button> 6 7 `, 8 }) 9 export class App { 10 11 12 increaseCounter(){ 13 this.counter.update((counter) => counter+1) 14 } 15 } 16
  28. export class TodoMainComponent { todos: Todo[] = []; addTodo(todo: Todo)

    { this.todos.push(todo); } deleteTodo(todoId: number) { this.todos = this.todos.filter(...); } } 1 2 3 4 5 6 7 8 9 10 11
  29. src/ └── app/ ├── features/ │ ├── dashboard/ │ │

    └── ... │ └── profile/ │ └── ... └── shared/ ├── util-auth └── ... 1 2 3 4 5 6 7 8 9 10
  30. export class ProductsComponent implements OnInit { products = signal<{ category:

    string; products: Product[] }[]>([]); private readonly productsService = inject(ProductsService); private readonly checkoutService = inject(CheckoutService); private readonly toastrService = inject(ToastrService); private readonly router = inject(Router); ngOnInit() { this.productsService.loadProducts().subscribe((products) => { const categoryProductMap = products.reduce( (result: Record<string, Product[]>, product) => { const category = product.category; if (!result[category]) { result[category] = []; } result[category].push(product); return result; }, {}, 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
  31. export class ProductsComponent implements OnInit { products = signal<{ category:

    string; products: Product[] }[]>([]); private readonly productsService = inject(ProductsService); private readonly checkoutService = inject(CheckoutService); private readonly toastrService = inject(ToastrService); private readonly router = inject(Router); ngOnInit() { this.productsService.loadProducts().subscribe((products) => { const categoryProductMap = products.reduce( (result: Record<string, Product[]>, product) => { const category = product.category; if (!result[category]) { result[category] = []; } result[category].push(product); return result; }, {}, 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 products = signal<{ category: string; products: Product[] }[]>([]); private readonly productsService = inject(ProductsService); private readonly checkoutService = inject(CheckoutService); private readonly toastrService = inject(ToastrService); private readonly router = inject(Router); export class ProductsComponent implements OnInit { 1 2 3 4 5 6 7 ngOnInit() { 8 this.productsService.loadProducts().subscribe((products) => { 9 const categoryProductMap = products.reduce( 10 (result: Record<string, Product[]>, product) => { 11 const category = product.category; 12 13 if (!result[category]) { 14 result[category] = []; 15 } 16 17 result[category].push(product); 18 19 return result; 20 }, 21 {}, 22
  32. export class ProductsComponent implements OnInit { products = signal<{ category:

    string; products: Product[] }[]>([]); private readonly productsService = inject(ProductsService); private readonly checkoutService = inject(CheckoutService); private readonly toastrService = inject(ToastrService); private readonly router = inject(Router); ngOnInit() { this.productsService.loadProducts().subscribe((products) => { const categoryProductMap = products.reduce( (result: Record<string, Product[]>, product) => { const category = product.category; if (!result[category]) { result[category] = []; } result[category].push(product); return result; }, {}, 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 products = signal<{ category: string; products: Product[] }[]>([]); private readonly productsService = inject(ProductsService); private readonly checkoutService = inject(CheckoutService); private readonly toastrService = inject(ToastrService); private readonly router = inject(Router); export class ProductsComponent implements OnInit { 1 2 3 4 5 6 7 ngOnInit() { 8 this.productsService.loadProducts().subscribe((products) => { 9 const categoryProductMap = products.reduce( 10 (result: Record<string, Product[]>, product) => { 11 const category = product.category; 12 13 if (!result[category]) { 14 result[category] = []; 15 } 16 17 result[category].push(product); 18 19 return result; 20 }, 21 {}, 22 this.productsService.loadProducts().subscribe((products) => { const categoryProductMap = products.reduce( (result: Record<string, Product[]>, product) => { const category = product.category; if (!result[category]) { result[category] = []; } result[category].push(product); return result; }, {}, ); const groupedByCategory = Object.keys(categoryProductMap).map( (category) => ({ category, products: categoryProductMap[category], }), ); 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
  33. export class ProductsComponent implements OnInit { products = signal<{ category:

    string; products: Product[] }[]>([]); private readonly productsService = inject(ProductsService); private readonly checkoutService = inject(CheckoutService); private readonly toastrService = inject(ToastrService); private readonly router = inject(Router); ngOnInit() { this.productsService.loadProducts().subscribe((products) => { const categoryProductMap = products.reduce( (result: Record<string, Product[]>, product) => { const category = product.category; if (!result[category]) { result[category] = []; } result[category].push(product); return result; }, {}, 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 products = signal<{ category: string; products: Product[] }[]>([]); private readonly productsService = inject(ProductsService); private readonly checkoutService = inject(CheckoutService); private readonly toastrService = inject(ToastrService); private readonly router = inject(Router); export class ProductsComponent implements OnInit { 1 2 3 4 5 6 7 ngOnInit() { 8 this.productsService.loadProducts().subscribe((products) => { 9 const categoryProductMap = products.reduce( 10 (result: Record<string, Product[]>, product) => { 11 const category = product.category; 12 13 if (!result[category]) { 14 result[category] = []; 15 } 16 17 result[category].push(product); 18 19 return result; 20 }, 21 {}, 22 this.productsService.loadProducts().subscribe((products) => { const categoryProductMap = products.reduce( (result: Record<string, Product[]>, product) => { const category = product.category; if (!result[category]) { result[category] = []; } result[category].push(product); return result; }, {}, export class ProductsComponent implements OnInit { 1 products = signal<{ category: string; products: Product[] }[]>([]); 2 private readonly productsService = inject(ProductsService); 3 private readonly checkoutService = inject(CheckoutService); 4 private readonly toastrService = inject(ToastrService); 5 private readonly router = inject(Router); 6 7 ngOnInit() { 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 onProductClicked(id: string): void { this.router.navigate(['products', id]); } onCartClicked(product: Product): void { this.checkoutService.addToCart(product).subscribe(() => { this.toastrService.success('Item Added to Cart'); }); } ); 23 24 const groupedByCategory = Object.keys(categoryProductMap).map( 25 (category) => ({ 26 category, 27 products: categoryProductMap[category], 28 }), 29 ); 30 31 this.products.set(groupedByCategory); 32 }); 33 } 34 35 36 37 38 39 40 41 42 43 44
  34. ngrx classic export class ProductsComponent implements OnInit { private readonly

    store = inject(Store); readonly productsByCategories = this.store.selectSignal( selectProductsByCategories, ); ngOnInit(): void { this.store.dispatch(ProductsActions.loadProducts()); } onProductClicked(id: string): void { this.store.dispatch(ProductsActions.navigateToDetail({ id })); } onCartClicked(product: Product): void { this.store.dispatch(CheckoutActions.addProduct({ product })); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
  35. ngrx classic export class ProductsComponent implements OnInit { private readonly

    store = inject(Store); readonly productsByCategories = this.store.selectSignal( selectProductsByCategories, ); ngOnInit(): void { this.store.dispatch(ProductsActions.loadProducts()); } onProductClicked(id: string): void { this.store.dispatch(ProductsActions.navigateToDetail({ id })); } onCartClicked(product: Product): void { this.store.dispatch(CheckoutActions.addProduct({ product })); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 readonly productsByCategories = this.store.selectSignal( selectProductsByCategories, ); this.store.dispatch(ProductsActions.loadProducts()); this.store.dispatch(ProductsActions.navigateToDetail({ id })); this.store.dispatch(CheckoutActions.addProduct({ product })); export class ProductsComponent implements OnInit { 1 private readonly store = inject(Store); 2 3 4 5 6 7 ngOnInit(): void { 8 9 } 10 11 onProductClicked(id: string): void { 12 13 } 14 15 onCartClicked(product: Product): void { 16 17 } 18 } 19
  36. ngrx signal store export class ProductsComponent implements OnInit { private

    readonly store = inject(ProductsStore); readonly productsByCategories = this.store.productsByCategories(); ngOnInit(): void { this.store.loadProducts(); } onProductClicked(id: string): void { this.store.navigateToDetail({ id }); } onCartClicked(product: Product): void { this.store.addProduct({ product }); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
  37. export const BookingStore = signalStore( //... withState(...), withComputed(...), withMethods((store, service

    = inject(...)) => ({ loadBooking: rxMethod<number>( exhaustMap((id) => service.load(id).pipe( tapResponse({ next: (booking) => patchState(store, { booking }), error: (err) => console.error(err) }) )) }), ); 1 2 3 4 5 6 7 8 9 10 11 12 13 14
  38. export const BookingStore = signalStore( // ... withState(...), withComputed(...), withMethods((store,

    service = ...) => ({ async loadBooking(id): Promise<number> { const booking = await service.load(id); patchState(store, { booking }); }, })), ); 1 2 3 4 5 6 7 8 9 10 11
  39. module boundaries rules: { '@nx/enforce-module-boundaries': [ 'error', { enforceBuildableLibDependency: true,

    allow: ['^.*/eslint(\\.base)?\\.config\\.[cm]?[jt]s$'], depConstraints: [ { sourceTag: 'scope:dog-rating-app', onlyDependOnLibsWithTags: [ 'scope:dogs', 'scope:about', 'scope:shared', ], }, { sourceTag: 'scope:about', onlyDependOnLibsWithTags: ['scope:about', 'scope:shared'], }, { sourceTag: 'scope:dogs', onlyDependOnLibsWithTags: ['scope:dogs', 'scope:shared'], }, // ... ], }, ], }, 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
  40. module boundaries rules: { '@nx/enforce-module-boundaries': [ 'error', { enforceBuildableLibDependency: true,

    allow: ['^.*/eslint(\\.base)?\\.config\\.[cm]?[jt]s$'], depConstraints: [ { sourceTag: 'scope:dog-rating-app', onlyDependOnLibsWithTags: [ 'scope:dogs', 'scope:about', 'scope:shared', ], }, { sourceTag: 'scope:about', onlyDependOnLibsWithTags: ['scope:about', 'scope:shared'], }, { sourceTag: 'scope:dogs', onlyDependOnLibsWithTags: ['scope:dogs', 'scope:shared'], }, // ... ], }, ], }, 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 sourceTag: 'scope:dog-rating-app', onlyDependOnLibsWithTags: [ 'scope:dogs', 'scope:about', 'scope:shared', ], rules: { 1 '@nx/enforce-module-boundaries': [ 2 'error', 3 { 4 enforceBuildableLibDependency: true, 5 allow: ['^.*/eslint(\\.base)?\\.config\\.[cm]?[jt]s$'], 6 depConstraints: [ 7 { 8 9 10 11 12 13 14 }, 15 { 16 sourceTag: 'scope:about', 17 onlyDependOnLibsWithTags: ['scope:about', 'scope:shared'], 18 }, 19 { 20 sourceTag: 'scope:dogs', 21 onlyDependOnLibsWithTags: ['scope:dogs', 'scope:shared'], 22 }, 23 // ... 24 ], 25 }, 26 ], 27 }, 28
  41. module boundaries rules: { '@nx/enforce-module-boundaries': [ 'error', { enforceBuildableLibDependency: true,

    allow: ['^.*/eslint(\\.base)?\\.config\\.[cm]?[jt]s$'], depConstraints: [ { sourceTag: 'scope:dog-rating-app', onlyDependOnLibsWithTags: [ 'scope:dogs', 'scope:about', 'scope:shared', ], }, { sourceTag: 'scope:about', onlyDependOnLibsWithTags: ['scope:about', 'scope:shared'], }, { sourceTag: 'scope:dogs', onlyDependOnLibsWithTags: ['scope:dogs', 'scope:shared'], }, // ... ], }, ], }, 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 sourceTag: 'scope:dog-rating-app', onlyDependOnLibsWithTags: [ 'scope:dogs', 'scope:about', 'scope:shared', ], rules: { 1 '@nx/enforce-module-boundaries': [ 2 'error', 3 { 4 enforceBuildableLibDependency: true, 5 allow: ['^.*/eslint(\\.base)?\\.config\\.[cm]?[jt]s$'], 6 depConstraints: [ 7 { 8 9 10 11 12 13 14 }, 15 { 16 sourceTag: 'scope:about', 17 onlyDependOnLibsWithTags: ['scope:about', 'scope:shared'], 18 }, 19 { 20 sourceTag: 'scope:dogs', 21 onlyDependOnLibsWithTags: ['scope:dogs', 'scope:shared'], 22 }, 23 // ... 24 ], 25 }, 26 ], 27 }, 28 { sourceTag: 'scope:about', onlyDependOnLibsWithTags: ['scope:about', 'scope:shared'], }, rules: { 1 '@nx/enforce-module-boundaries': [ 2 'error', 3 { 4 enforceBuildableLibDependency: true, 5 allow: ['^.*/eslint(\\.base)?\\.config\\.[cm]?[jt]s$'], 6 depConstraints: [ 7 { 8 sourceTag: 'scope:dog-rating-app', 9 onlyDependOnLibsWithTags: [ 10 'scope:dogs', 11 'scope:about', 12 'scope:shared', 13 ], 14 }, 15 16 17 18 19 { 20 sourceTag: 'scope:dogs', 21 onlyDependOnLibsWithTags: ['scope:dogs', 'scope:shared'], 22 }, 23 // ... 24 ], 25 }, 26 ], 27 }, 28
  42. state management give your data a place to live pattern

    shapes architecture provides guidance
  43. resource signals user.value() // The data user.status() // 'idle' |

    'loading' | 'resolved' | 'error' user.isLoading() // Boolean user.hasValue() // Type guard 1 2 3 4
  44. reactive params sort = signal('asc'); users = httpResource<User[]>( () =>

    `/users?sort=${this.sort()}` ); // Auto-reloads when sort changes! 1 2 3 4 5 6 7
  45. basic form @Component({ selector: 'app-login', imports: [FormField], template: ` <input

    type="email" [formField]="loginForm.email" /> <input type="password" [formField]="loginForm.password" /> ` }) export class LoginComponent { loginModel = signal({ email: '', password: '' }) loginForm = form(this.loginModel) } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
  46. Do ✅ feature-thinking, route-drive, state-driven, ... ✅ standalone components +

    lazy-loaded ✅ move to signals ✅ use rxjs, promises where it fits best ✅ think about your state ✅ manage your state ✅ think about using nx
  47. Don't ❌ inherit components ❌ neglect forms ❌ spread state

    across the whole app ❌ make everything global state ❌ hide state in random services/subjects ❌ subscribe manually everywhere
  48. Name Link Fabian Gosebrink - Angular Signals under the Hood

    | NG Belgrade Conf 2024 https://www.youtube.com/watch? v=8N_TDbZuF7M Ebook - Creating Modern Apps With Nx, Angular & Ngrx Signal Store https://offering.solutions/ebook-modern- angular-apps-ngrx/ Angular Deep Dive: Modern State with Signals and SignalStore https://app.pluralsight.com/ilx/video- courses/angular-deep-dive-modern-state- signals-signal-store/course-overview Angular Deep Dive: Monorepos with Nx https://app.pluralsight.com/ilx/video- courses/angular-deep-dive-monorepos- nx/course-overview Angular Signals vs RxJS – Answering the Questions Developers Ask https://offering.solutions/blog/articles/2025/ 06/14/angular-signals-vs-rxjs/