This content originally appeared on Level Up Coding - Medium and was authored by Cristian-Florin Calina

Recently, I had a situation that involved listening to the screen width of an application.
There are a lot of methods of achieving this, but I wanted something simple, reusable & generic that did not impact performance too much.
I thought that listening to resize event on window and checking the innerWidth is way too slow especially since everything related to layout forces a reflow.
I discovered quickly while searching for solutions, the window.matchMedia() API, and it seemed like that was the best solution (it is also supported in all of the major browsers).
The only thing left to do was to implement a simple reusable and generic solution that uses this underneath. Here’s a helpful MDN link that got me started.
What I wanted was to be able to subscribe to different media queries from different components across the application, without creating new listeners.
The solution I came up with, was to create a static class (well, a class with static methods and variables), that would keep a list of queries and subscriptions for those queries (and a list of listener refs so it can be easily cleaned up).
export interface IQueryList {
[key: string]: {
query: MediaQueryList;
subscribers: Array<(matchesQuery: boolean) => void>;
};
}
interface IEventListener {
[key: string]: (event: MediaQueryListEvent) => void;
}
export class MediaQueryListener {
private static _subscribers: IQueryList = {};
private static _eventListenersRefs: IEventListener = {};
static subscribe() {
//...
}
static unsubscribe() {
//...
}
}
This class would expose 2 public methods: subscribe and unsubscribe.
The subscribe method takes a callback function that receives a boolean parameter representing if the query matches or not, and the actual query in a string format (eg: '(max-width: 768px)').
It then checks if there is already a subscription made for this query from some other place:
- if it is, it only adds the callback to the list of subscribers for that query.
- if it’s not, it adds the query to the list, with the only subscriber being the passed callback, then it starts to listen to changes for the newly created query.
Either way, it also calls the callback instantly with the current query match.
static subscribe = (cb: (matchesQuery: boolean) => void, query: string) => {
if (!this._subscribers[query]) {
this._subscribers[query] = {
query: window.matchMedia(query),
subscribers: [ cb ],
};
// keep the refs so we can remove the listeners in unsubscribe
this._eventListenersRefs[query] = event => {
this._subscribers[query]
.subscribers
.forEach(subscriber => subscriber(event.matches));
};
// create the listener only on first subscription for the query
this._subscribers[query]
.query
.addEventListener('change', this._eventListenersRefs[query]);
} else {
this._subscribers[query].subscribers.push(cb);
}
// Call the callback with the current value of the media query
cb(window.matchMedia(query).matches);
};
The unsubscribe method receives the same callback function and query in a string format, and removes the callback from the subscribers of that query. It also checks if there are no more subscribers left, and removes the media query listener so there are no hanging listeners left.
static unsubscribe = (cb: (matchesQuery: boolean) => void, query: string) => {
if (!this._subscribers[query]) {
return;
}
this._subscribers[query].subscribers = this._subscribers[query].subscribers.filter(subscriber => subscriber !== cb);
if (this._subscribers[query].subscribers.length === 0) {
this._subscribers[query].query.removeEventListener('change', this._eventListenersRefs[query]);
delete this._subscribers[query];
delete this._eventListenersRefs[query];
}
};
As I said, it’s a basic wrapper on top of matchMedia API. There are probably multiple packages out there that achieve the same thing in a way or another, I did not research specifically for those packages in advance before creating my solution since I liked the idea of writing one.
Thanks for reading the article!
Level Up Coding
Thanks for being a part of our community! Before you go:
- 👏 Clap for the story and follow the author 👉
- 📰 View more content in the Level Up Coding publication
- 🔔 Follow us: Twitter | LinkedIn | Newsletter
🚀👉 Join the Level Up talent collective and find an amazing job
Media Queries in JS/TS Done Right was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.
This content originally appeared on Level Up Coding - Medium and was authored by Cristian-Florin Calina

Cristian-Florin Calina | Sciencx (2022-12-06T12:38:48+00:00) Media Queries in JS/TS Done Right. Retrieved from https://www.scien.cx/2022/12/06/media-queries-in-js-ts-done-right/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.