Media
The Media service provides a reactive API to track media query changes. It supports both responsive breakpoints and user preferences such as prefers-color-scheme. Internally, it uses the Angular CDK's MediaMatcher and Signals to expose reactive media state.
Overview
Responsive layouts and adaptive UI behavior often rely on media queries. Rather than scattering matchMedia logic throughout your components, this service centralizes and standardizes media query handling in a reactive way. It also automatically cleans up listeners and integrates with Angular’s platform detection.
- Signal-Based Media Tracking
- Creates a signal for each media query, letting you reactively respond to viewport or preference changes.
- Automatic Cleanup
Mediais provided in the root injector, so its listeners stay active for the lifetime of the application and are removed when the root injector is destroyed, not when the component that calledmatch()is destroyed.- Browser-Only Listeners
- Change listeners are only registered in the browser, so the service is safe to use during server rendering.
Provider Setup
Add provideMedia to the providers array of your application configuration:
import { provideMedia } from '@/app/core/media';
export const appConfig: ApplicationConfig = {
providers: [
provideMedia(),
// ...other providers
],
};Usage
match() returns a Signal<boolean>, so assign it to a field and read it in the template:
@Component({
selector: 'app-layout',
templateUrl: './layout.html',
})
export class Layout {
private media = inject(Media);
private isMobile = this.media.match('(max-width: 767px)');
}@if (isMobile()) {
<app-mobile-nav />
} @else {
<app-sidebar />
} Use computed() to derive state from several queries. The service can be injected into services as well as components.
private isMobile = this.media.match('(max-width: 767px)');
private prefersReducedMotion = this.media.match(
'(prefers-reduced-motion: reduce)'
);
private animateSidebar = computed(
() => !this.isMobile() && !this.prefersReducedMotion()
);How It Works
- The
match()method takes a media query string and returns aSignal<boolean>that tracks whether the query currently matches. - Calling
match()again with the same query string returns the same signal, and only one listener is registered per query. - On the client, it sets up a listener for the media query and updates the signal when it changes.
- On the server, the CDK's
MediaMatcherdoes not evaluate media queries, so the signal isfalsefor every query exceptalland no listener is registered. The server renders with that value and the browser resolves the real one, so markup that depends on a media signal can differ between the two.
Note: The service is made for a fixed set of queries, such as breakpoints and user preferences, and repeated calls with the same query share one listener no matter how many components use it. Prefer fixed query strings over ones generated at runtime from changing values, since each distinct string is kept for the lifetime of the application.