Learn How to Build Responsive Websites in React Without CSS

William Le

Believe it or not, you don’t need CSS @media queries. In this article, learn a few lines of JavaScript that will allow you to mimic the features of @media queries and build fully responsive websites.

The biggest reason developers don’t opt for inline styles in their React projects is because it lacks some notable CSS features:

  • :hover, :first-child and other pseudo-selectors
  • @keyframe animations
  • @media queries

The usual solution is to use CSS-in-JS libraries like styled-components or Radium to get these missing features. It works, however, I think the broader question is, “Are these really valid critiques of inline styles when the argument relies on CSS jargon?”

While JavaScript can’t have :hover it does have onMouseOver. Or for @keyframe… it has the Greensock animation library, or Popmotion. JavaScript can’t have some CSS features, but it’s always had a robust browser-native API.

🐊 Alligator.io recommends

Recommended React and GraphQL course

If Ya Can’t Beat Em, JavaScript Em

You can even build @media query-like behavior by just using vanilla JavaScript. It’s incredibly simple.

class Card extends Component {

  constructor() {

    super();

    this.mediaQuery = {
      desktop: 1200,
      tablet: 768,
      phone: 576,
    };

    this.state = {
      windowWidth: null
    };
  }

  componentDidMount() {
    window.addEventListener('resize', () => {
      this.setState({windowWidth: document.body.clientWidth})
    });
  }

  render() {
    return (
      <div style={{
        width: this.state.windowWidth > this.mediaQuery.phone
          ? '50%'
          : '100%',
        //more styling :)
      }}>
        <!-- <Card> contents -->
      </div>
    );
  }
}

Check out the live demo!

The important part is document.body.clientWidth which gives you the width of the browser. Just add a listener for the window “resize” event to update windowWidth and voilà! There you have it… a fully responsive website!

Advanced 1990s Technology

document.body.clientWidth has been around for a very long time, yet it’s still useful today for building modern, responsive websites. And you can actually get more mileage from this simple solution.

In this next demo, a low-res image is used for smaller devices to save bandwidth, and desktop devices (larger than 768px) uses a high-res image. This is incredibly simple with JavaScript, whereas it would be quite difficult to tackle with @media queries.

class ImageGallery extends Component {

  constructor() {

    super();

    this.mediaQuery = {
      desktop: 1200,
      tablet: 768,
      phone: 576,
    };

    this.state = {
      windowWidth: null
    };
  }

  componentDidMount() {
    window.addEventListener('resize', () => {
      this.setState({windowWidth: document.body.clientWidth})
    });
  }

  render () {
    const isTablet = this.state.windowWidth < this.mediaQuery.tablet
    const imgUrl = isTablet
      ? 'https://picsum.photos/300/300'
      : 'https://picsum.photos/1200/1200'  // 👈  this ternary right here

    return (
      <div>
        <h1>Mildly Inspirational Photos</h1>
        <div><img src={imgUrl}/></div>
      </div>
    )
  }
}

This is just one example that shows how versatile a JavaScript-driven solution can be. In fact, you can do a lot of other stuff that @media queries aren’t able to, like:

  • Show React components that are crafted specifically for smaller screens
  • Show a modal to download the native iOS app
  • Block specific routes that don’t work on a targeted device

We're using the Bootstrap breakpoints here, but you can tailor this for your project's needs.

Conclusion

Try out this JavaScript-driven solution! Inline styles are surprisingly flexible, and in some cases provide more expressive power than CSS.

🌠 See the demo for a more robust implementation that uses the Context API to store the browser width, as well as applying a throttle to the "resize" listener.

  Tweet It

🕵 Search Results

🔎 Searching...

Sponsored by #native_company# — Learn More
#native_title# #native_desc#
#native_cta#