DEV Community

Cover image for Simple Element Queries With React Hooks
i. welch canavan
i. welch canavan

Posted on • Originally published at welchcanavan.com

Simple Element Queries With React Hooks

Originally published at welchcanavan.com

During the interminable wait for element queries it is difficult to not run ahead and experiment with their immense potential. Though there are a few options for using them in your CSS today, the two primary options (EQCSS and CSS Element Queries) have differing APIs with no clear winner. I try to avoid committing to fluctuating syntaxes in my projects, as I prefer solutions that are more explicit. I've written a few iterations of custom solutions in a few frameworks, but haven't been very happy with them so far. While working on a side project recently I found that React Hooks provide a concise and satisfying solution.

The Component

I'm going to presume a general familiarity with React Hooks for this article, but if you would like to familiarize yourself with them you would be hard pressed to do better than Dan Abromov's introduction. While you could write a custom hook relying on ResizeObserver directly or element-resize-detector (if you need broader browser support), I'm relying on the useMeasure hook from react-use. If you haven't come across react-use it is a collection of battle-tested and crowd-sourced React Hooks that address common use cases.

This example also relies on the classnames package. You could absolutely write a version of this that uses CSS-in-JS, but I've got this crazy hangup about legible code, so my example is written with classnames and Sass.

Here is a contrived example of what a React functional component conditionally styled by width could look like:

import classNames from 'classnames';
import { useMeasure } from 'react-use';

function MyComponent() {
    const [ref, { width }] = useMeasure();
    const containerClasses = classNames({
        container: true,
        'container--md': width >= 800,
    });

    return (
        <div className={containerClasses} ref={ref}>
            {/* All of your cool component stuff */}
        </div>
    );
}
Enter fullscreen mode Exit fullscreen mode

The styles

The accompanying styles can be written like so, working up from small to large. Again, the following code is contrived, but the sky is the limit in terms of potential.

.container {
    background-color: red;

    &--md {
        background-color: blue;
    }
}
Enter fullscreen mode Exit fullscreen mode

I've found that element queries can be very powerful with CSS Grid and grid-template-areas, allowing you to drop a component in to any layout and have its contents arranged logically.

.container {
    &--md {
        display: grid;
        grid-template-areas:
            "A A"
            "B C"
        ;
        grid-template-columns: 1fr 1fr;
    }

    &--lg {
        grid-template-areas: "A B C";
        grid-template-columns: 1fr 2fr 1fr;
    }
}
Enter fullscreen mode Exit fullscreen mode

Keeping it DRY

To provide your app with some consistency you could write a utility function like so:

const generateWidthClasses = (containerClassName, width, sizes) => Object
    .keys(sizes)
    .reduce((sizesObj, size) => {
        sizesObj[`${containerClassName}--${size}`] = width >= sizes[size];

        return sizesObj;
    }, {});
Enter fullscreen mode Exit fullscreen mode

You could then use that utility function across many components:

const containerSizes = {
    sm: 600,
    md: 800,
    lg: 1000,
};

function MyComponent() {
    const [ref, { width }] = useMeasure();
    const containerClasses = classNames({
        container: true,
        ...generateWidthClasses("container", width, containerSizes)
    });
    // ...
}
Enter fullscreen mode Exit fullscreen mode

All Together Now

If you'd like to explore this idea further, here's a working example:

The web development community has only scratched the surface of element queries' potential, I can only imagine what people will come up with once element queries are easier and more common. If anyone builds on this idea, I'd love to hear from you!

Top comments (1)

Collapse
 
jessicagarson profile image
Jessica Garson

Awesome!