Let's create a simple React hook to detect browsers and their capabilities

Dima Vyshniakov - Sep 30 - - Dev Community

User agent sniffing is the most popular approach for browser detection. Unfortunately, it's not very accessible for a front end development because of multiple reasons. Browser vendors constantly trying to make sniffing not possible. Thus, each browser has their own user agent string format, which is very complex to parse.

There is a much more simple way of achieving the same using browser CSS API, which I'm going to show you. So let's create browser capabilities detection React hook.

We are going to use CSS.supports() static method. It returns a boolean value indicating if the browser supports a given CSS feature, or not. This is javascript analog of @supports at-rule. It works similar to media queries, but with CSS capabilities as a subject.

Hook to detect supported features

The most naive approach of calling CSS.supports() during component render cycle will create problems in Server Side Rendering environments, such as Next.js. Because the server side renderer has no access to browser APIs, it just produces a string of code.

import type {FC} from 'react';

const Component: FC = () => {
    // 🚫 Don't do this!
    const hasFeature = CSS.supports('your-css-declaration');
    // ...
}
Enter fullscreen mode Exit fullscreen mode

We will use this simple hook instead. The hook receives a string containing support condition, a CSS rule we are going to validate, e.g. display: flex.

import {useState, useEffect} from 'react';

export const useSupports = (supportCondition: string) => {
    // Create a state to store declaration check result
    const [checkResult, setCheckResult] = useState<boolean | undefined>();

    useEffect(() => {
        // Run check as a side effect, on user side only
        setCheckResult(CSS.supports(supportCondition));
    }, [supportCondition]);


    return checkResult;
};
Enter fullscreen mode Exit fullscreen mode

Now we can check for different CSS features support from inside React component.

import type {FC} from 'react';

const Component: FC = () => {

    // Check for native `transform-style: preserve` support
    const hasNativeTransformSupport = useSupports('
        (transform-style: preserve)
    ');

    // Check for vendor prefixed `transform-style: preserve` support
    const hasNativeTransformSupport = useSupports('
        (-moz-transform-style: preserve) or (-webkit-transform-style: preserve)
    ');
    // ...
}
Enter fullscreen mode Exit fullscreen mode

Detect user browser using CSS support conditions

In order to detect user browser, we have to do a little hacking.

Browser hack has nothing to do with law violations. It's just a special CSS declaration or selector which works differently in one of available browsers.

Here is the reference page with various browser hacks. After thorough experimentation on my machine, I've chosen these:

const hacksMapping = {
    // anything -moz will work, I assume
    firefox: '-moz-appearance:none',
    safari: '-webkit-hyphens:none',
    // tough one because Webkit and Blink are relatives
    chrome: '
        not (-webkit-hyphens:none)) and (not (-moz-appearance:none)) and (list-style-type:"*"'
}
Enter fullscreen mode Exit fullscreen mode

And here is our final hook look like:

export const useDetectBrowser = () => {
    const isFirefox = useSupports(hacksMapping.firefox);
    const isChrome = useSupports(hacksMapping.chrome);
    const isSafari = useSupports(hacksMapping.safari);

    return [
        {browser: 'firefox', condition: isFirefox},
        {browser: 'chromium based', condition: isChrome},
        {browser: 'safari', condition: isSafari},
    ].find(({condition}) => condition)?.browser as 
        'firefox' | 'chromium based' | 'safari' | undefined;
};
Enter fullscreen mode Exit fullscreen mode

Full demo

Here is a full working demo of the hook.

Final thoughts

While I cannot assert that this method is entirely foolproof or stable, it is important to note that browsers frequently undergo updates, and vendor-specific properties are often deprecated or replaced by standardized ones. This issue is equally applicable to user agent sniffing, as both approaches encounter similar challenges.

However, the CSS.supports() method offers a more maintainable and granular solution. It encourages developers to adopt strategies such as graceful degradation or progressive enhancement, allowing for more precise and adaptable patch applications.

Happy coding.

. . . . .
Terabox Video Player