matchMedia
is a powerful JavaScript API that allows developers to detect changes in the size and characteristics of a user’s viewport (browser window). It provides a way to listen for media queries, allowing you to dynamically adjust the content and styling of your website based on screen size, resolution, orientation, and other media features. Instead of relying on hardcoded breakpoints or separate CSS stylesheets for different devices, matchMedia
enables responsive design by offering a programmatic approach to detect and react to media query matches. This enables creating truly adaptive user experiences that seamlessly adapt to various display contexts.
Using matchMedia
offers several significant advantages for building responsive web applications:
Dynamic Content Adjustments: Modify content, layout, or functionality based on screen size, orientation, or other media features without requiring page reloads. This results in a smoother and more interactive user experience.
Improved Performance: Avoid unnecessary CSS switching or JavaScript-based solutions that might be less efficient. matchMedia
allows targeted updates, preventing unnecessary rendering overhead.
Maintainability: Centralize your responsive logic within JavaScript, making your code cleaner, more organized, and easier to maintain than managing multiple CSS stylesheets or complex conditional statements.
Feature Detection: Beyond screen size, you can use matchMedia
to detect other features like preferred color schemes (“prefers-color-scheme”), pointer types (“pointer: coarse”), and more, making your application more accessible and adaptable.
Testability: The programmatic nature of matchMedia
makes testing your responsive logic much easier compared to CSS-only approaches.
matchMedia
enjoys widespread support across modern browsers. However, older browsers might require a polyfill (a JavaScript library that provides functionality for unsupported browsers).
Browser | Support Status | Notes |
---|---|---|
Chrome | Excellent | Native support in all recent versions. |
Firefox | Excellent | Native support in all recent versions. |
Safari | Excellent | Native support in all recent versions. |
Edge | Excellent | Native support in all recent versions. |
Internet Explorer | Limited/No Support | Requires a polyfill for any meaningful usage. |
Opera | Excellent | Native support in all recent versions. |
For older browsers lacking native matchMedia
support, consider using a reliable polyfill like matchMedia.js
or a similar library readily available via a package manager like npm or yarn. Remember to check the polyfill’s license and compatibility before integrating it into your project. Modern JavaScript bundlers (like Webpack, Parcel, Rollup) can often handle polyfill inclusion automatically if configured correctly with appropriate browserlist settings.
This section details the fundamental usage of the matchMedia
API.
The core of using matchMedia
involves creating a MediaQueryList
object. This object represents a specific media query and provides information about whether that query currently matches the user’s environment. You create a MediaQueryList
using the window.matchMedia()
method, passing the media query string as an argument.
// Create a MediaQueryList for screens wider than 768px
const mq = window.matchMedia('(min-width: 768px)');
// Create a MediaQueryList for prefers-color-scheme: dark
const darkModeMq = window.matchMedia('(prefers-color-scheme: dark)');
// Create a MediaQueryList for prefers-reduced-motion: reduce
const reduceMotionMq = window.matchMedia('(prefers-reduced-motion: reduce)');
The returned mq
object is a MediaQueryList
. You can then use its properties and methods to check the current state and respond to changes.
matches
PropertyThe matches
property of the MediaQueryList
object is a boolean value indicating whether the media query currently matches the user’s environment. It’s true
if the query matches and false
otherwise.
console.log(mq.matches); // true if screen width is >= 768px, false otherwise
console.log(darkModeMq.matches); // true if the user prefers dark mode
console.log(reduceMotionMq.matches); // true if the user prefers reduced motion
This property is useful for immediately determining if a certain style or behavior should be applied.
media
PropertyThe media
property is a read-only string containing the original media query that was passed to window.matchMedia()
. This is useful for debugging or logging purposes, ensuring you’re working with the expected query.
console.log(mq.media); // Output: "(min-width: 768px)"
addListener
and removeListener
The most powerful aspect of matchMedia
is its ability to listen for changes to the media query’s matching state. This allows your application to react dynamically to changes in the user’s environment (e.g., resizing the browser window, changing device orientation, toggling dark mode).
The addListener
method adds an event listener that is called whenever the matches
property changes. The removeListener
method removes an event listener that was previously added. The listener function receives a MediaQueryList
object as an argument; you can access the matches
property within this function to determine the new state.
function handleMediaQueryChange(e) {
if (e.matches) {
// Media query now matches
console.log('Media query matches!');
// Apply styles or change behavior
document.body.classList.add('large-screen');
else {
} // Media query no longer matches
console.log('Media query does not match!');
// Apply styles or change behavior
document.body.classList.remove('large-screen');
}
}
// Add listener
.addListener(handleMediaQueryChange);
mq
// ... later, to remove the listener:
// mq.removeListener(handleMediaQueryChange);
//Example using prefers-color-scheme
function handleDarkModeChange(e){
if(e.matches){
document.body.classList.add('dark-mode');
else {
} document.body.classList.remove('dark-mode');
}
}
.addListener(handleDarkModeChange); darkModeMq
Remember to remove listeners when they are no longer needed to prevent memory leaks, especially in components that might be unmounted or reused. Consider adding the removeListener
call within a cleanup function or event handler.
This section explores more advanced usage patterns and best practices for working with matchMedia
.
onchange
event (Deprecated)While addListener
and removeListener
are the recommended approach for handling media query changes, the onchange
event is deprecated but might still be encountered in older code. It’s generally less flexible and less efficient than the addListener
/removeListener
methods. It’s strongly recommended to use addListener
and removeListener
in new code.
The onchange
event handler is assigned directly to the MediaQueryList
object. This handler is invoked whenever the matches
property changes.
// Deprecated - use addListener/removeListener instead
.onchange = function() {
mqif (this.matches) {
// ...
else {
} // ...
}; }
You can efficiently manage multiple media queries by creating separate MediaQueryList
objects for each query and attaching appropriate listeners. However, for a more organized approach, consider using a single function to handle changes from different queries, perhaps using a data structure to track them.
const mq1 = window.matchMedia('(min-width: 768px)');
const mq2 = window.matchMedia('(orientation: landscape)');
function handleMultipleQueries(e) {
console.log(`Query '${e.matches ? e.media : "N/A"}' changed to ${e.matches}`);
// Apply logic based on which query changed and its new state
if(e.media === '(min-width: 768px)') {
//handle mq1 changes
else if (e.media === '(orientation: landscape)') {
} //handle mq2 changes
}
}
.addListener(handleMultipleQueries);
mq1.addListener(handleMultipleQueries); mq2
This approach avoids code duplication and provides better organization when dealing with numerous media queries.
Avoid unnecessary listeners: Only add listeners when necessary and remove them when the component or functionality is no longer active. Unnecessary listeners consume resources and can impact performance.
Efficient event handling: Keep your event handlers concise and performant. Avoid expensive operations (e.g., DOM manipulations) within the handler unless absolutely necessary. Consider debouncing or throttling event handlers if they’re called frequently due to rapid changes in the media query state.
Minimize DOM manipulation: Instead of constantly updating the DOM within event handlers, consider caching DOM elements and manipulating their properties directly when the media query changes.
Use CSS where possible: For simple visual adjustments based on screen size, CSS media queries are often more efficient than using matchMedia
in JavaScript. Reserve matchMedia
for more complex or dynamic adaptations requiring JavaScript control.
matchMedia
supports different media types in your queries. You can target specific output media like screen
, print
, or others using media type descriptors in your query string.
const screenMq = window.matchMedia('screen and (min-width: 600px)'); //For screen devices wider than 600px
const printMq = window.matchMedia('print and (color)'); //For color printers
.addListener(function(e){
screenMqif(e.matches){
//Apply styles for screen
else {
} //Remove styles
}
})
.addListener(function(e){
printMqif(e.matches){
//Apply styles for color printers
else {
} //Remove styles
} })
This allows you to create styles and behaviors specifically tailored for print previews or other output media in addition to screen-based displays. Remember that print
media queries only apply when the print dialog is open. Be mindful of this when testing.
This section provides practical examples demonstrating matchMedia
in various scenarios.
A fundamental use case for matchMedia
is creating responsive designs. Instead of relying solely on CSS media queries, matchMedia
allows for dynamic JavaScript-driven adaptations based on screen size.
const mq = window.matchMedia('(min-width: 768px)');
function handleResize(e) {
if (e.matches) {
// Larger screens (tablets and desktops)
document.getElementById('main-content').classList.add('desktop-layout');
document.getElementById('sidebar').style.display = 'block';
else {
} // Smaller screens (mobiles)
document.getElementById('main-content').classList.remove('desktop-layout');
document.getElementById('sidebar').style.display = 'none';
}
}
.addListener(handleResize);
mqhandleResize(mq); // Initial check on page load
This example switches between different layouts based on screen width, potentially hiding a sidebar on smaller screens. Note the initial call to handleResize(mq)
to apply the initial layout based on the page load screen size.
Use matchMedia
to dynamically load different images based on screen resolution or device pixel ratio. This helps optimize image loading and reduce bandwidth usage.
const mq = window.matchMedia('(min-resolution: 2dppx)'); // Check for high-resolution screens
function handleImageChange(e) {
const img = document.getElementById('myImage');
.src = e.matches ? 'high-resolution.jpg' : 'low-resolution.jpg';
img
}
.addListener(handleImageChange);
mqhandleImageChange(mq);
This loads a high-resolution image only when the device supports it; otherwise, a lower-resolution image is loaded.
matchMedia
can be used to control CSS custom properties (variables), providing a more elegant way to manage styles based on media conditions.
const mq = window.matchMedia('(prefers-color-scheme: dark)');
function handleDarkMode(e) {
document.documentElement.style.setProperty('--bg-color', e.matches ? '#333' : '#fff');
document.documentElement.style.setProperty('--text-color', e.matches ? '#fff' : '#333');
}
.addListener(handleDarkMode);
mqhandleDarkMode(mq);
This example sets different background and text colors based on the user’s preferred color scheme (dark mode or light mode). This keeps styling centralized in CSS but enables dynamic switching based on user preferences or device capabilities.
Beyond screen size, matchMedia
can detect various device capabilities and features, enabling conditional rendering of content or components.
const touchMq = window.matchMedia('(pointer: coarse)'); // Check for touch devices
function handleTouchDevice(e) {
if (e.matches) {
// Show touch-friendly interface elements
document.getElementById('touch-controls').style.display = 'block';
else {
} // Show alternative interface elements for mouse/keyboard
document.getElementById('mouse-controls').style.display = 'block';
}
}
.addListener(handleTouchDevice);
touchMqhandleTouchDevice(touchMq);
This example displays different interface controls based on whether the device primarily uses touch input (like a mobile phone) or mouse/keyboard input. This approach makes your application more accessible and usable across different input methods. Remember to always provide alternative ways for users to interact with your application if a feature is not available on their device.
This section addresses common problems encountered when working with matchMedia
.
Incorrectly written media queries are a frequent source of problems. Double-check your syntax carefully. Common mistakes include:
Typos: Ensure that media feature names (e.g., min-width
, orientation
) and values are spelled correctly. Case sensitivity matters.
Incorrect Units: Use the appropriate units for values (e.g., px
, em
, rem
for lengths, dpi
for resolution). Omitting units can lead to unexpected results.
Logical Errors: Review your media query logic, including the use of and
, or
, and not
operators. Make sure your conditions accurately reflect the intended behavior.
Conflicting Queries: Ensure that your media queries don’t inadvertently conflict with each other. Prioritize media queries appropriately and avoid overlapping conditions that might produce unpredictable results.
Debugging Techniques:
Console Logging: Use console.log(mq.media)
to verify that the media query string passed to matchMedia()
is what you expect. Log the mq.matches
value to see if the query matches or not under various conditions.
Browser Developer Tools: Use your browser’s developer tools (usually accessible by pressing F12) to inspect the CSS styles applied to your elements. Check the computed styles to make sure the media queries are being applied as expected.
Simplify: If you have complex media queries, try simplifying them to isolate the problem. Test individual parts of the query separately to pinpoint the source of the error.
While matchMedia
has broad browser support, minor inconsistencies may exist. To ensure reliable cross-browser functionality:
Polyfills: For older browsers lacking native matchMedia
support, use a robust polyfill. This ensures consistent behavior across a wider range of browsers. Thoroughly test your application after adding a polyfill.
Feature Detection: Use feature detection rather than relying on browser sniffing. This involves checking whether a specific feature (like matchMedia
) is available instead of making assumptions about the browser itself. This approach produces cleaner and more maintainable code that adapts more readily to future browser updates.
Testing: Test your code across different browsers and devices to ensure consistent behavior. Automated browser testing tools can be very helpful in this process.
Graceful Degradation: Design your application to gracefully degrade in cases where matchMedia
is unavailable or behaves unexpectedly. This might involve providing fallback mechanisms or alternative UI elements.
Problems with listeners can manifest as unexpected behavior or missing updates when media query conditions change.
Listener Scope: Double-check the scope of your listener function, especially if using this
within the function. Ensure that this
refers to the expected object. Using arrow functions (() => {}
) can help manage the scope correctly.
Multiple Listeners: Avoid adding the same listener multiple times to the same MediaQueryList
object. This can lead to the function being called repeatedly.
Removing Listeners: Always remove event listeners when they’re no longer needed (e.g., when a component unmounts). Failing to remove listeners can cause memory leaks and unpredictable behavior.
Event Order: Be aware that the order in which listeners are added doesn’t guarantee the order of execution.
Debugging Listener Issues:
Logging: Add console logs inside your listener function to confirm that it’s being called when the media query changes.
Breakpoints: Use breakpoints in your debugger to step through the listener function and inspect the state of variables and objects.
Temporary Removal: Temporarily remove some listeners to see if one listener is interfering with another. This can help isolate the source of the problem.
By systematically investigating these areas, you can resolve most matchMedia
related issues efficiently. Remember to utilize your browser’s developer tools extensively to assist your debugging workflow.
While matchMedia
is a powerful tool, certain scenarios might benefit from alternative approaches.
For simple responsive design adjustments, relying solely on CSS media queries can be sufficient and often more efficient than using matchMedia
. CSS media queries are directly handled by the browser, avoiding the overhead of JavaScript execution.
Advantages:
Disadvantages:
Example: A simple responsive layout change can be entirely handled in CSS:
/* Styles for screens smaller than 768px */
@media (max-width: 767px) {
#sidebar {
display: none;
}#main-content {
width: 100%;
}
}
/* Styles for screens larger than or equal to 768px */
@media (min-width: 768px) {
#sidebar {
display: block;
width: 20%;
}#main-content {
width: 80%;
} }
This avoids JavaScript altogether. However, more complex logic requiring dynamic content adjustments or conditional rendering would be challenging to implement purely in CSS.
Several JavaScript libraries and frameworks provide features that overlap with or extend the functionality of matchMedia
. These might offer additional benefits depending on your project’s needs. However, it’s important to carefully weigh the trade-offs: Adding external libraries increases your project’s size and dependencies.
Frameworks (React, Vue, Angular, etc.): Modern JavaScript frameworks usually have built-in mechanisms or readily available libraries for handling responsive design and media query changes. They often abstract away the details of matchMedia
, providing higher-level abstractions and convenient APIs.
CSS-in-JS Libraries: Libraries like styled-components or emotion allow you to write CSS directly within JavaScript components. They may include features that handle media queries within the styling definition, simplifying the management of responsive styling.
Other Libraries: Some dedicated libraries focus on responsive image handling or other specific aspects of adaptive design. Research libraries to find the appropriate tools.
Considerations when choosing alternatives:
Project Complexity: For simple projects, CSS media queries might suffice. For complex applications with dynamic updates, a framework’s built-in features or matchMedia
might be more suitable.
Project Size and Dependencies: Adding external libraries increases the project size and introduces dependencies. Evaluate if the benefits of an alternative library outweigh the potential overhead.
Maintainability: Consider the long-term maintainability of the solution. A well-structured approach using matchMedia
or a framework’s features can improve maintainability compared to complex CSS-only solutions.
The best approach depends on your specific project requirements and context. Consider the factors mentioned above to make an informed choice.
Media Query: A CSS rule that allows you to apply styles based on characteristics of the device displaying the content. These characteristics include screen size, resolution, orientation, and other media features.
Media Query List (MediaQueryList
): An object returned by window.matchMedia()
. It represents a specific media query and provides information about whether that query currently matches the user’s environment and allows adding listeners for changes.
matches
Property: A boolean property of a MediaQueryList
object that indicates whether the associated media query currently matches the user’s environment (true
if it matches, false
otherwise).
media
Property: A read-only string property of a MediaQueryList
object that contains the original media query string.
addListener
Method: A method of a MediaQueryList
object used to add an event listener that is triggered whenever the matches
property changes.
removeListener
Method: A method of a MediaQueryList
object used to remove an event listener that was previously added using addListener
.
Media Feature: A characteristic of the rendering device or environment that can be tested in a media query (e.g., width
, height
, orientation
, resolution
, pointer
, prefers-color-scheme
).
Viewport: The visible area of the browser window.
Device Pixel Ratio (DPR): The ratio of pixels on the physical screen to pixels in the viewport. A higher DPR indicates a higher screen resolution.
Polyfill: A piece of JavaScript code that provides functionality for older browsers that don’t support a particular feature natively.
MDN Web Docs - matchMedia()
: https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia (The official Mozilla Developer Network documentation for matchMedia()
)
Can I Use… matchMedia
Support: https://caniuse.com/?search=matchMedia (Check browser compatibility across different versions)
Examples and Tutorials (search online): Numerous blog posts and tutorials demonstrate various ways to use matchMedia
. Search for “matchMedia examples” or “matchMedia responsive design” to find relevant resources.
This appendix provides a starting point for deeper exploration and serves as a reference for key terms and resources related to matchMedia
. Remember that the web is constantly evolving, so always refer to up-to-date documentation for the most accurate and complete information.