Core Concepts
Understanding a few key concepts will help you get the most out of NG Reactive Utils.
Signals vs Observables
Angular's modern reactivity is built on signals, which provide synchronous, glitch-free updates. Observables (RxJS) are asynchronous and stream-based.
When to use signals:
- Component state that needs to trigger UI updates
- Derived/computed values
- Simple reactive state management
When to use observables:
- Async operations (HTTP requests, timers)
- Complex event streams with operators
- When you need precise control over timing and cancellation
When to use NG Reactive Utils:
- Converting observables to signals (forms, routes)
- Reading browser state reactively (window size, mouse position, storage)
Composables
Composables return reactive values (signals) that you can use in your templates and logic:
import { useWindowSize, useRouteParam } from 'ng-reactive-utils';
export class MyComponent {
windowSize = useWindowSize();
userId = useRouteParam('id');
// Use in template: {{ windowSize().width }}, {{ userId() }}
}Common composables:
useWindowSize()- Track window dimensionsuseRouteParam()- Read route parametersuseFormState()- Get form state as signalsusePreviousSignal()- Track the previous value of a signaluseLocalStorage()- Two-way signal sync with localStoragewhenTrue()/whenFalse()- Run side effects when a signal becomes truthy or falsy
When to Use NG Reactive Utils vs Vanilla Angular
Use NG Reactive Utils when:
✅ Converting form observables to signals
// Instead of: toSignal(form.valueChanges, { initialValue: form.value })
formState = useFormState(this.form);✅ Converting route observables to signals
// Instead of: toSignal(route.params.pipe(map(...)), { initialValue: ... })
userId = useRouteParam('id');✅ Reacting to browser state
// Instead of: manual fromEvent(window, 'resize') with toSignal()
windowSize = useWindowSize();
isMobile = computed(() => this.windowSize().width < 768);✅ Running side effects when a signal reaches a specific state
// Instead of: effect() + manual untracked() to avoid unintended subscriptions
onOpen = whenTrue(this.isOpen, () => {
this.copy.set(cloneDeep(this.data()));
});
onClose = whenFalse(this.isOpen, () => {
this.copy.set(null);
});Type Safety
All utilities are fully typed with TypeScript:
interface UserForm {
email: string;
age: number;
}
// Type inference works automatically
formState = useFormState<UserForm>(this.form);
formState.value(); // { email: string; age: number }
// Route params with types
params = useRouteParams<{ id: string; postId: string }>();
params(); // { id: string; postId: string }Memory Management
Signals created by NG Reactive Utils are automatically cleaned up when the component is destroyed:
export class MyComponent {
// Automatically cleaned up on component destroy
windowSize = useWindowSize();
theme = useLocalStorage('theme', 'light');
}No manual cleanup needed — Angular handles it through the injection context.
Next Steps
- Explore Browser Composables
- Check out Form Composables