@irv-labs/ngx-responsive-signals
v1.0.1
Published
Signal-based responsive breakpoints for Angular
Maintainers
Readme
ngx-responsive-signals
Signal-based responsive breakpoints for Angular. Inject the service, call a method, get a Signal<boolean> — no RxJS, no subscriptions, no boilerplate.
Requirements
- Angular 17+
Installation
npm install @irv-labs/ngx-responsive-signalsQuick Start
import { ResponsiveService, Screen } from '@irv-labs/ngx-responsive-signals';
@Component({ ... })
export class MyComponent {
private responsive = inject(ResponsiveService);
isMobile = this.responsive.is(Screen.Mobile);
showSidebar = this.responsive.isAtLeast(Screen.Tablet);
isCompact = this.responsive.isAtMost(Screen.Tablet);
}@if (isMobile()) {
<app-mobile-nav />
} @else {
<app-desktop-nav />
}Breakpoints
| Screen | Width |
| -------------- | ------------- |
| Mobile | <= 480px |
| Tablet | 481 – 768px |
| LargeTablet | 769 – 1024px |
| Desktop | 1025 – 1280px |
| LargeDesktop | > 1280px |
API
responsive.is(screen)
Returns Signal<boolean> — true only when the current screen matches exactly.
isMobile = this.responsive.is(Screen.Mobile);
isTablet = this.responsive.is(Screen.Tablet);
isDesktop = this.responsive.is(Screen.Desktop);responsive.isAtLeast(screen)
Returns Signal<boolean> — true when the current screen is that size or larger.
showSidebar = this.responsive.isAtLeast(Screen.Tablet); // Tablet, LargeTablet, Desktop, LargeDesktop
fullLayout = this.responsive.isAtLeast(Screen.Desktop); // Desktop, LargeDesktopresponsive.isAtMost(screen)
Returns Signal<boolean> — true when the current screen is that size or smaller.
isCompact = this.responsive.isAtMost(Screen.Tablet); // Mobile, Tablet
stackedNav = this.responsive.isAtMost(Screen.LargeTablet); // Mobile, Tablet, LargeTabletresponsive.isLarger(screen)
Returns Signal<boolean> — true when the current screen is strictly larger than the given one.
aboveTablet = this.responsive.isLarger(Screen.Tablet); // LargeTablet, Desktop, LargeDesktopresponsive.isSmaller(screen)
Returns Signal<boolean> — true when the current screen is strictly smaller than the given one.
belowDesktop = this.responsive.isSmaller(Screen.Desktop); // Mobile, Tablet, LargeTabletresponsive.screen
Signal<Screen> — the current screen value. Useful for the pipe or for derived logic.
screen = this.responsive.screen;Pipe
Import ResponsivePipe in your component and pass responsive.screen as input. The argument follows the format 'screen' or 'screen:modifier'.
imports: [ResponsivePipe];screen = inject(ResponsiveService).screen;<!-- Exact -->
@if (screen() | responsive:'mobile') { }
<!-- isAtLeast -->
@if (screen() | responsive:'tablet:least') { }
<!-- isAtMost -->
@if (screen() | responsive:'desktop:most') { }
<!-- isLarger -->
@if (screen() | responsive:'tablet:larger') { }
<!-- isSmaller -->
@if (screen() | responsive:'tablet:smaller') { }| Modifier | Equivalent service method |
| ---------- | ------------------------- |
| (none) | is() |
| :least | isAtLeast() |
| :most | isAtMost() |
| :larger | isLarger() |
| :smaller | isSmaller() |
Custom Breakpoints
Call provideResponsive() in app.config.ts to override the default values. Only pass what you need — the rest keeps its default.
// app.config.ts
import { provideResponsive } from '@irv-labs/ngx-responsive-signals';
export const appConfig: ApplicationConfig = {
providers: [
provideResponsive({
mobile: 640,
desktop: 1440,
// tablet and largeTablet stay at their defaults
}),
],
};Calling provideResponsive() is optional — the service works with the default breakpoints out of the box.
SSR
The service is SSR-safe. On the server, window is never accessed — the screen defaults to Mobile (width 0), which is the most conservative fallback for layout decisions.
Path Alias (optional)
If you prefer a shorter import, add a path alias in your tsconfig.json:
{
"compilerOptions": {
"paths": {
"@responsive": ["./node_modules/@irv-labs/ngx-responsive-signals"]
}
}
}// Before
import { ResponsiveService, Screen } from '@irv-labs/ngx-responsive-signals';
// After
import { ResponsiveService, Screen } from '@responsive';This is a local convenience — it does not affect the published package or teammates unless they add the same alias.
Public API
| Export | Description |
| ------------------------ | --------------------------------------------------------------------- |
| ResponsiveService | Core service — is, isAtLeast, isAtMost, isLarger, isSmaller |
| ResponsivePipe | Pipe that resolves a screen + modifier to boolean |
| provideResponsive | Registers custom breakpoints in app.config |
| Screen | Enum — Mobile, Tablet, LargeTablet, Desktop, LargeDesktop |
| SCREEN_ORDER | Ordinal map used internally for comparisons |
| RESPONSIVE_BREAKPOINTS | InjectionToken for the breakpoints config |
| DEFAULT_BREAKPOINTS | Default breakpoint values |
| ResponsiveBreakpoints | Config interface for provideResponsive |
| ResponsiveModifier | 'least' \| 'most' \| 'larger' \| 'smaller' |
| ResponsivePipeArg | Union type for all valid pipe arguments |
Philosophy
Angular Signals make reactive state simple — a boolean that updates when the window resizes should be nothing more than a computed. This library takes that idea to its logical conclusion: one service, a handful of methods, all returning signals. No Observables to subscribe to, no async pipes, no manual cleanup.
The comparison model is built on a single ordinal map — each screen maps to a number, and isAtLeast, isAtMost, isLarger, isSmaller become single-line comparisons. The result is predictable, testable, and easy to reason about.
- Signal-native — every method returns
Signal<boolean>, composable with anycomputedoreffect - Zero setup — works without
provideResponsive(), custom breakpoints are opt-in - SSR-safe — no
windowaccess on the server - No memory leaks — the resize listener is cleaned up via
DestroyRef - Dev-time safety — breakpoint order is validated at startup, not silently ignored
License
MIT
