MediaQuery

Reactive wrapper around matchMedia. Track a single media query match state with Angular signals.

Loading demo...

Usage

angular-ts
import { Component } from '@angular/core';
import { mediaQuery } from '@signality/core';

@Component({
  template: `
    <p>Dark mode: {{ prefersDark() ? 'Yes' : 'No' }}</p>
  `,
})
export class ThemeDetector {
  readonly prefersDark = mediaQuery('(prefers-color-scheme: dark)'); 
}

Parameters

ParameterTypeDescription
queryMaybeSignal<string>CSS media query string (see Using media queries)
optionsMediaQueryOptionsOptional configuration (see Options below)

Options

The MediaQueryOptions extends Angular's CreateSignalOptions<boolean> and WithInjector:

OptionTypeDescription
initialValuebooleanInitial value for SSR (default: false)
equalValueEqualityFn<boolean>Custom equality function. See Signal equality functions
debugNamestringDebug name for the signal (development only)
injectorInjectorOptional injector for DI context

Return Value

Returns a Signal<boolean> containing true when the media query matches, false otherwise.

Examples

Responsive component

angular-ts
import { Component, computed } from '@angular/core';
import { mediaQuery } from '@signality/core';

@Component({
  template: `
    <nav [class]="navClass()">
      @if (isMobile()) {
        <button (click)="toggleMenu()">☰</button>
      }
      <ng-content />
    </nav>
  `,
})
export class ResponsiveNav {
  readonly isMobile = mediaQuery('(max-width: 768px)'); 
  
  readonly navClass = computed(() => 
    this.isMobile() ? 'nav-mobile' : 'nav-desktop'
  );
  
  toggleMenu() {
    // Toggle mobile menu
  }
}

Reduced motion

angular-ts
import { Component, computed } from '@angular/core';
import { mediaQuery } from '@signality/core';

@Component({
  template: `
    <div [class]="animationClass()">
      <ng-content />
    </div>
  `,
})
export class AnimatedBox {
  readonly reducedMotion = mediaQuery('(prefers-reduced-motion: reduce)');
  
  readonly animationClass = computed(() => 
    this.reducedMotion() ? 'no-animation' : 'animated'
  );
}

High contrast mode

angular-ts
import { Component, effect, inject, DOCUMENT } from '@angular/core';
import { mediaQuery } from '@signality/core';

@Component({ /* ... */ })
export class AccessibilitySettings {
  readonly document = inject(DOCUMENT);
  readonly prefersHighContrast = mediaQuery('(prefers-contrast: more)');
  
  constructor() {
    effect(() => {
      if (this.prefersHighContrast()) {
        this.document.body.classList.add('high-contrast');
      } else {
        this.document.body.classList.remove('high-contrast');
      }
    });
  }
}

Dynamic query

angular-ts
import { Component, signal, computed } from '@angular/core';
import { mediaQuery } from '@signality/core';

@Component({
  template: `
    <input 
      type="number" 
      [value]="breakpoint()" 
      (input)="breakpoint.set(+$any($event.target).value)" 
    />
    <p>Matches: {{ matches() }}</p>
  `,
})
export class DynamicBreakpoint {
  readonly breakpoint = signal(768);
  readonly query = computed(() => `(min-width: ${this.breakpoint()}px)`); 
  readonly matches = mediaQuery(this.query);
}

SSR Compatibility

On the server, the signal initializes with false by default. You can provide an initial value:

typescript
const isDark = mediaQuery('(prefers-color-scheme: dark)', {
  initialValue: true
});

Type Definitions

typescript
interface MediaQueryOptions extends CreateSignalOptions<boolean>, WithInjector {
  readonly initialValue?: boolean;
}

function mediaQuery(
  query: MaybeSignal<Union<MediaQueryFeature, string>>,
  options?: MediaQueryOptions
): Signal<boolean>;
Edit this page on GitHub Last updated: Mar 19, 2026, 23:28:23