
Angular is already a powerful framework — but over time, I’ve found a few tricks that help me really get the most out of it. Whether it’s squeezing out more performance or just writing cleaner code, here are some Angular hacks I’ve come to rely on in my day-to-day development.
1. 🧠 Use Signals Instead of RxJS (Angular 16+)
With Signals now built into Angular, I’ve started replacing a lot of my reactive state management with them. They’re much simpler than BehaviorSubjects or Subjects for most use cases.
import { signal, computed } from '@angular/core';
class CounterComponent {
count = signal(0);
double = computed(() => this.count() * 2);
increment() {
this.count.set(this.count() + 1);
}
}
Why I love this:
- Less boilerplate compared to RxJS
- Built-in reactivity without needing subscriptions
- Feels more like modern frontend frameworks (like Solid.js or Vue)
2. 🚀 Lazy Load Routes Dynamically
Instead of loading every feature module upfront, I load them only when needed. This really helps keep the initial bundle light.
this.router.navigateByUrl('/feature', { skipLocationChange: true })
.then(() => this.router.navigate(['/feature']));
Why this matters:
- Faster app startup
- Better performance on slower networks
3. ⚡️ Use OnPush
Change Detection Wherever Possible
If your component doesn’t need to track global state changes or events, switching to ChangeDetectionStrategy.OnPush
is a no-brainer.
@Component({
selector: 'app-optimized',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `<h2>{{ data.title }}</h2>`
})
export class OptimizedComponent {
@Input() data: { title: string };
}
Why it’s a win:
- Reduces unnecessary rendering
- Especially helpful in big lists or heavy UIs
4. 🔄 Always Use trackBy
with ngFor
Whenever I loop over lists, I use trackBy
to avoid Angular re-rendering the entire list on every change.
<div *ngFor="let user of users; trackBy: trackById">
{{ user.name }}
</div>
trackById(index: number, user: any) {
return user.id;
}
The benefit:
- Prevents flickering
- Keeps UI smooth during list updates
5. 💉 Replace Constructor Injection with inject()
(Angular 14+)
Instead of cluttering constructors, I now use the inject()
function for dependency injection, especially in services or standalone classes.
import { inject } from '@angular/core';
export class UserService {
private http = inject(HttpClient);
getUser() {
return this.http.get('/api/user');
}
}
Why I use this:
- Cleaner code
- Works well in functions, not just classes
- More flexibility
6. 🧹 Use takeUntil
to Prevent Memory Leaks
For components that use subscriptions, I avoid memory leaks by combining takeUntil
with a Subject
.
private destroy$ = new Subject<void>();
ngOnInit() {
this.service.getData()
.pipe(takeUntil(this.destroy$))
.subscribe(console.log);
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
Why it’s important:
- Avoids leftover subscriptions after the component is gone
- Saves memory, especially in long-running apps
7. 🧪 Profile Your App with ng.profiler
Need to know what’s slowing your app down? The Angular dev tools and profiler help me dig into what’s triggering change detection.
.profiler.timeChangeDetection();
When I use this:
- Performance tuning
- Debugging unexpected re-renders
8. ✨ Create Reusable Directives for Repetitive Logic
Instead of repeating the same logic or styles across templates, I just bundle them up in a directive.
@Directive({ selector: '[appHighlight]' })
export class HighlightDirective {
constructor(private el: ElementRef) {
this.el.nativeElement.style.backgroundColor = 'yellow';
}
}
<p appHighlight>This is highlighted!</p>
Why I like this:
- DRY (Don’t Repeat Yourself) principle
- Keeps HTML templates clean and readable
💡 Final Thoughts
These Angular tips have saved me countless hours and headaches. Whether you’re optimizing performance with OnPush
, reducing bundle sizes with lazy loading, or simplifying code with inject()
— every little boost adds up.
Let me know — what’s your favorite Angular trick? Drop a comment and share it — I’m always looking to learn something new. 👇