React inline styles and media queries using a custom React Hook

Tim Tennant
4 min readJul 20, 2019

--

I’m a big fan of React inline styles. Co-locating styles with the component and using javascript to define them fits my programmer brain better than CSS ever has. There are some features of CSS that are not available with inline styles, one of those is media queries. But, all is not lost, we can use programming instead to write our own reusable `useMediaQuery` React Hook with a few lines of code.

Why do I like React inline styles?

React inline styles are not specified as a string. They are specified as a javascript object whose key is the camelCased version of the style name, and whose value is the style’s value, usually a string, e.g.

const myDivStyle = {
display: 'flex',
justifyContent: 'space-around'
};
export const MyComponent = () => (
<div style={myDivStyle}/>
);

I like this because:

  • styles can be colocated in the same file as the component they relate to.
  • it easier to understanding and reason about the styles being applied to the component.
  • I can leverage the abilities of javascript, so it is easier to work with more complex styling when needed.
  • there is less risk of me accidentally modifying a style that affects another component.

What are media queries?

“Media queries are useful when you want to modify your site or app depending on a device’s general type (such as print vs. screen) or specific characteristics and parameters (such as screen resolution or browser viewport width).” — mozilla.org

The cool thing about media queries is they allow us to react to things like the browser being resized, by changing the styles being used. For example we can change a colour, a column width or show/hide a <div> dependent on how wide the browser window is.

How does CSS do media queries?

Lets say we wanted to show/hide a <div> based on the width of the browser, we could do it like this with CSS:

<div id="my-content"></div>@media screen and (min-width: 0px) and (max-width: 400px) {
#my-content { display: block; } /* show it on small screens */
}

@media screen and (min-width: 401px) and (max-width: 1024px) {
#my-content { display: none; } /* hide it elsewhere */
}

How could we do a media query in React?

import React, {useEffect, useState} from 'react';

export const MyComponent = () => {
const mediaMatch = window.matchMedia('(min-width: 500px)');
const [matches, setMatches] = useState(mediaMatch.matches);

useEffect(() => {
const handler = e => setMatches(e.matches);
mediaMatch.addListener(handler);
return () => mediaMatch.removeListener(handler);
});


return (
<div style={styles.container(matches)}>
<div>First item</div>
<div>Second item</div>
</div>);
};

const styles = {
container: isRowBased => ({
display: 'flex',
flexDirection: isRowBased ? 'row' : 'column',
justifyContent: 'space-around'
})
};

In this example we vary the style ‘flexDirection’ based on the width of the browser. When the width of the browser is 500px or above we use ‘row’ layout, otherwise we use ‘column’. We vary the style by using a function to return the inline style we need,

const styles = {
container: isRowBased => ({
display: 'flex',
flexDirection: isRowBased ? 'row' : 'column',
justifyContent: 'space-around'
})
};

To detect the browser width we use a media query

const mediaMatch = window.matchMedia('(min-width: 500)');

window.matchMedia returns a MediaQueryList object, which

  • provides a boolean result instantly using the matches property,
  • or we can attach a listener to capture the value whenever it changes,

See the MDN webdocs for more details.

We then use the userEffect hook to register (and unregister a listener) on the MediaQueryList. Then use a useState hook to capture the value as it changes. The component can then React to this change, by programatically modifying our inline style based on the value.

Now let’s extract that behaviour into a reusable React Hook

Now we can generalise the media query behaviour into a custom hook that can be used by any React component and inline style.

import {useEffect, useState} from 'react';

export const useMediaQuery = (query) => {
const mediaMatch = window.matchMedia(query);
const [matches, setMatches] = useState(mediaMatch.matches);

useEffect(() => {
const handler = e => setMatches(e.matches);
mediaMatch.addListener(handler);
return () => mediaMatch.removeListener(handler);
});
return matches;
};

It’s that simple, we can now simplify our component by using this hook.

import React from 'react';
import {useMediaQuery} from './hooks';

export const MyComponent = () => {
const isRowBased = useMediaQuery('(min-width: 500px)');

return (
<div style={styles.container(isRowBased)}>
<div>First item</div>
<div>Second item</div>
</div>);
};

const styles = {
container: isRowBased => ({
display: 'flex',
flexDirection: isRowBased ? 'row' : 'column',
justifyContent: 'space-around'
})
};

Enjoy Doing Stuff Like This?

I’ve been looking for a good use case for writing a custom React Hook, and now I’ve found one! Have you written a custom hook or got a good idea for one? Let me know if you have, I’d be very interested in finding out how other people are using custom hooks to improve their code.

--

--