Breakpoints

Reactive breakpoint matching using matchMedia. Track responsive breakpoints with Angular signals.

Loading demo...

Usage

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

@Component({
  template: `
    <div [class]="layoutClass()">
      @if (bp.mobile()) {
        <mobile-nav />
      } @else {
        <desktop-nav />
      }
    </div>
  `,
})
export class ResponsiveLayout {
  readonly bp = breakpoints({
    mobile: '(max-width: 767px)',
    tablet: '(min-width: 768px) and (max-width: 1023px)',
    desktop: '(min-width: 1024px)',
  });
  
  readonly layoutClass = computed(() => {
    if (this.bp.mobile()) return 'layout-mobile';
    if (this.bp.tablet()) return 'layout-tablet';
    return 'layout-desktop';
  });
}

Parameters

ParameterTypeDescription
mapRecord<string, string>Object mapping breakpoint names to media queries
optionsBreakpointsOptions<T>Optional configuration (see Options below)

Options

The BreakpointsOptions<T> extends WithInjector:

OptionTypeDefaultDescription
initialValuePartial<Record<keyof T, boolean>>-Initial breakpoint values (useful for SSR)
injectorInjector-Optional injector for DI context

Return Value

Returns an object with a signal for each breakpoint key, plus utility signals:

PropertyTypeDescription
[key]Signal<boolean>Whether the breakpoint matches
currentSignal<string[]>Array of currently matching breakpoint names

Examples

Tailwind-style breakpoints

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

const tailwindBreakpoints = {
  sm: '(min-width: 640px)',
  md: '(min-width: 768px)',
  lg: '(min-width: 1024px)',
  xl: '(min-width: 1280px)',
  '2xl': '(min-width: 1536px)',
};

@Component({
  template: `
    <div [style.columns]="columns()">
      <ng-content />
    </div>
  `,
})
export class ResponsiveGrid {
  readonly bp = breakpoints(tailwindBreakpoints); 
  
  readonly columns = computed(() => {
    if (this.bp['2xl']()) return 4;
    if (this.bp.xl()) return 3;
    if (this.bp.md()) return 2;
    return 1;
  });
}

Show/hide elements

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

@Component({
  template: `
    @if (bp.desktop()) {
      <sidebar />
    }
    
    <main>
      <ng-content />
    </main>
    
    @if (bp.mobile()) {
      <bottom-nav />
    }
  `,
})
export class AppLayout {
  readonly bp = breakpoints({
    mobile: '(max-width: 767px)',
    desktop: '(min-width: 768px)',
  });
}

Dynamic image loading

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

@Component({
  selector: 'responsive-image',
  template: `<img [src]="imageSrc()" [alt]="alt()" />`,
})
export class ResponsiveImage {
  readonly src = input.required<string>();
  readonly alt = input('');
  
  readonly bp = breakpoints({
    small: '(max-width: 639px)',
    medium: '(min-width: 640px) and (max-width: 1023px)',
    large: '(min-width: 1024px)',
  });
  
  readonly imageSrc = computed(() => {
    const base = this.src();
    if (this.bp.small()) return base.replace('.jpg', '-sm.jpg');
    if (this.bp.medium()) return base.replace('.jpg', '-md.jpg');
    return base;
  });
}

SSR Compatibility

On the server, all breakpoints initialize with false by default. You can provide initial values:

typescript
const bp = breakpoints(
  { mobile: '(max-width: 767px)', desktop: '(min-width: 768px)' },
  { initialValue: { mobile: false, desktop: true } }
);

Type Definitions

typescript
interface BreakpointsOptions<T extends Record<string, string>> extends WithInjector {
  readonly initialValue?: Partial<Record<keyof T, boolean>>;
}

type BreakpointsRef<T extends Record<string, string>> = {
  readonly [K in keyof T]: Signal<boolean>;
} & {
  readonly current: Signal<(keyof T)[]>;
};

function breakpoints<T extends Record<string, string>>(
  breakpoints: T,
  options?: BreakpointsOptions<T>
): BreakpointsRef<T>;
Edit this page on GitHub Last updated: Mar 19, 2026, 23:28:23