Documentation · Live demo · Why ngx-mq?
A responsive value is just a signal: read it in the template, compose it, and never wire up cleanup.
import { Component } from '@angular/core';
import { up } from 'ngx-mq';
@Component({
selector: 'app-root',
template: `
@if (isDesktop()) {
<app-sidebar />
}
`,
})
export class AppComponent {
readonly isDesktop = up('lg');
}
unsubscribe, cleanup is automatic.and / or / not.npm i ngx-mq # Angular 20-22
Angular 19 -> ngx-mq@2 · Angular 16-18 -> ngx-mq@1
Then register your breakpoints once, at bootstrap:
import { provideBreakpoints } from 'ngx-mq';
bootstrapApplication(AppComponent, {
providers: [provideBreakpoints({ sm: 640, md: 768, lg: 1024 })],
// or a preset: provideTailwindBreakpoints() / provideBootstrapBreakpoints() / provideMaterialBreakpoints()
});
Call the helpers inside an injection context: a component field, a constructor, or a DI factory.
readonly isMobile = down('md');
readonly isTablet = between('md', 'lg');
readonly isDesktop = up('lg');
readonly prefersDark = colorScheme('dark');
// `hover()` has no direct inverse, so compose it
readonly isTouchLike = not(hover());
// Large screen, in landscape, with a hover-capable pointer
readonly isLandscapeDesktop = and(up('lg'), orientation('landscape'), hover());
// Small screens OR a reduced-motion preference
readonly prefersSimpleUi = or(down('md'), reducedMotion());
readonly reduceMotion = reducedMotion();
readonly isRetina = matchMediaSignal('(min-resolution: 2dppx)');
Angular's CDK ships BreakpointObserver, which works well but is built around RxJS and raw query strings. ngx-mq is built for the signals era: read a value in the template, subscribe to nothing, clean up automatically.
ngx-mq |
CDK BreakpointObserver |
|
|---|---|---|
| Reactivity | Signal<boolean> |
Observable<BreakpointState> |
| Cleanup | Automatic via DestroyRef |
Manual (takeUntilDestroyed) |
| Named breakpoints | Tailwind / Bootstrap / Material or your own | Material breakpoints or raw strings |
| Media-feature helpers | colorScheme, hover, pointer, ... |
Raw query strings |
| Composition | and / or / not |
RxJS operators |
| SSR | Configurable static value | Handle it yourself |
| Footprint | ~1.9 kB standalone | Part of @angular/cdk |
Spin it up in seconds on StackBlitz, no setup required.
Full API reference, guides and recipes live at martsinlabs.github.io/ngx-mq.
Every query helper returns a Signal<boolean> and accepts an optional options argument
(CreateMediaQueryOptions).
Breakpoints
| Helper | Arguments | true when |
|---|---|---|
up |
bp |
viewport width >= bp |
down |
bp |
viewport width < bp (exclusive) |
between |
minBp, maxBp |
viewport width is in [minBp, maxBp) |
down and between upper bounds are exclusive: a small epsilon (default 0.02, set via provideBreakpointEpsilon) is subtracted from the max so adjacent ranges never overlap.
Media features
| Helper | Arguments | true when |
|---|---|---|
orientation |
'portrait' | 'landscape' |
the screen orientation matches |
colorScheme |
'light' | 'dark' |
the system color scheme matches |
displayMode |
DisplayModeOption |
the display mode matches (PWA detection) |
reducedMotion |
none | the user prefers reduced motion |
prefersContrast |
'more' | 'less' | 'no-preference' | 'custom' |
the user's contrast preference matches |
hover |
none | the primary pointer can hover |
anyHover |
none | any available pointer can hover |
pointer |
'fine' | 'coarse' | 'none' |
the primary pointer matches |
anyPointer |
'fine' | 'coarse' | 'none' |
any available pointer matches |
colorGamut |
'srgb' | 'p3' | 'rec2020' |
the display covers the gamut |
Composition
| Helper | Arguments | true when |
|---|---|---|
and |
...conditions: Signal<boolean>[] |
every condition is true (empty: true) |
or |
...conditions: Signal<boolean>[] |
any condition is true (empty: false) |
not |
condition: Signal<boolean> |
the condition is false |
Custom queries
| Helper | Arguments | Description |
|---|---|---|
matchMediaSignal |
query: string |
A signal for any raw CSS media query |
Providers
| Provider | Argument | Description |
|---|---|---|
provideBreakpoints |
bps: MqBreakpoints |
Registers a custom breakpoint map |
provideTailwindBreakpoints |
none | Registers the Tailwind preset |
provideBootstrapBreakpoints |
none | Registers the Bootstrap preset |
provideMaterialBreakpoints |
none | Registers the Material 2 preset |
provideBreakpointEpsilon |
epsilon: number |
Sets the exclusive-bound epsilon (default 0.02) |
provideSsrValue |
value: boolean |
Sets the value signals report during SSR (default false) |
Options and types
interface CreateMediaQueryOptions {
ssrValue?: boolean; // value reported during SSR; overrides provideSsrValue
debugName?: string; // shown for the signal in Angular DevTools
}
type MqBreakpoints = Record<string, number>;
type DisplayModeOption =
| 'browser' | 'fullscreen' | 'standalone'
| 'minimal-ui' | 'window-controls-overlay' | 'picture-in-picture';
matchMedia does not exist on the server, so each signal returns a static value during SSR and switches to the live result after hydration. Set the default with provideSsrValue(true), or override per call with up('lg', { ssrValue: true }).
Contributions are welcome. See CONTRIBUTING.md and ARCHITECTURE.md.
MIT © Martsin Labs
|
Sentry
|