ngx-responsive-signals

February 22, 2026 · View on GitHub

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-signals

Quick 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

ScreenWidth
Mobile<= 480px
Tablet481 – 768px
LargeTablet769 – 1024px
Desktop1025 – 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, LargeDesktop

responsive.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, LargeTablet

responsive.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, LargeDesktop

responsive.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, LargeTablet

responsive.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') { }
ModifierEquivalent service method
(none)is()
:leastisAtLeast()
:mostisAtMost()
:largerisLarger()
:smallerisSmaller()

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

ExportDescription
ResponsiveServiceCore service — is, isAtLeast, isAtMost, isLarger, isSmaller
ResponsivePipePipe that resolves a screen + modifier to boolean
provideResponsiveRegisters custom breakpoints in app.config
ScreenEnum — Mobile, Tablet, LargeTablet, Desktop, LargeDesktop
SCREEN_ORDEROrdinal map used internally for comparisons
RESPONSIVE_BREAKPOINTSInjectionToken for the breakpoints config
DEFAULT_BREAKPOINTSDefault breakpoint values
ResponsiveBreakpointsConfig interface for provideResponsive
ResponsiveModifier'least' | 'most' | 'larger' | 'smaller'
ResponsivePipeArgUnion 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 any computed or effect
  • Zero setup — works without provideResponsive(), custom breakpoints are opt-in
  • SSR-safe — no window access 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