3 Ways To Implement Responsive Design In Your React App

React — The Game Changer
React has truly been a game changer in how we now approach developing front-end applications for the web. Components, right from their introduction, went against the “separation of concerns” rule of thumb, choosing to leverage JSX and bringing together HTML and JS in the same file. UI elements are now being rendered via their own Components with all the intent and purpose of reusability.
Responsive Done Many Ways
Responsive design was always a requirement and now that we’re building apps using the “mobile first” mindset, is even more important than ever.
Using the most basic approach, responsive design has been implemented using Media Queries, where a more experienced developer might opt to add event listeners that respond to the “resize” event and add/remove styles via JS. Even still, both novice and experienced developers alike might work with a front-end framework like Bootstrap or Materialize with predefined breakpoints and custom Components for making those changes.
React And Implementing Responsive Design
So let’s take a look at 3 different approaches to implementing responsive design in your next React app.
- Media Queries
- Inline styles
- Higher Order Components — (React-Socks)
The Process
As we review the different ways to implement responsive design, I’ll focus on the following points which must be accomplished regardless of which approach you take.
- Tracking changes to the window size (width/height)
- Add/Remove/Update the necessary DOM elements to reflect those changes
The Demo
Note: The above demo is using the Inline-Style approach
Although this site has many responsive features, for the purpose of this demo I will be focusing solely on the navigation menu and making the following changes:
Mobile: width < 1024 — Removes the top navigation and shows the hamburger icon
Desktop: width > 1023 — Shows the navigation elements and hides the hamburger icon
So let’s begin!
Media Queries
The power of CSS never ceases to amaze and inspire. I’ve seen countless CSS works of art on CodePen and on almost any given day I learn something new about CSS. So it goes without saying that the first approach to implementing responsive design is using Media Queries.
CodeSandbox: https://codesandbox.io/s/mars-media-queries-0wqnv
Note: Media Queries were added to a separate responsive.css file
Tracking Changes To The Window Size
Changes to the window size are tracked using the CSS @media rule and setting the min-width to 1024px. This essentially states that the rules defined here apply only when the window width is at a minumum of 1024ppx.
@media only screen and (min-width: 1024px) {}
Updating DOM Elements
Within the media query rule, additional CSS rules are added that target specific DOM elements.
@media only screen and (min-width: 1024px) {
.container header .header-nav-area #nav_container {
display:flex;
}
}Pros/Cons
Pros
- Relatively simple to apply
- Anyone with basic CSS skills can create and implement Media Queries
Cons
- React will render extra DOM elements, hidden by CSS, that are never used
- CSS is external to the Component
- An additional import needed for the CSS file
Inline Styles
Anytime I teach intro Front-End Web Dev and the topic of inline styles I always say, emphatically, “Don’t do this”. I then continue to reinforce that styles should be defined via CSS. It’s not until we get to learn JS and/or jQuery that adding styles via .style or .css() do we break the rule.
CodeSandbox: https://codesandbox.io/s/mars-inline-styles-wpixk
Note: Nav elements have been split off into their own Components and placed into a TopNav folder
Tracking The Window Size
Tracking changes to the window size requires that we add an event listener to the window object that listens for “resize” events. Once a “resize” event occurs, a callback function is called that retrieves the current width from the window object.
window.addEventListener(“resize”, updateDimensions)const updateDimensions = () => {
const width = window.innerWidth
}
Since Hooks are all the rage, let’s import and use useState to create a width variable and its supporting setWindowWidth() method, which will be used to update the width in the updateDimensions() function.
import React, { useState } from 'react'function App() {
const [width, setWindowWidth] = useState(0); const updateDimensions = () => {
const width = window.innerWidth
setWindowWidth(width)
} //…rest of App.js below…
}
Then we will create a supporting function that will be responsible for grabbing the window.innerWidth value and updating state:
Adding The Event Listener
The useEffect Hook must now be imported and used to do the following:
- Call updateDimensions to set the initial width to the current window size
- Add the event listener when the Component mounts
- Remove the event listener when the Component unmounts
useEffect essentially replaces the class-based componentDidMount, componentDidUppdate, and componentWillUnmount lifecycle methods.
import React, { useState, useEffect } from 'react'function App() {
const [width, setWindowWidth] = useState(0) useEffect(() => {
updateDimensions();
window.addEventListener(“resize”, updateDimensions); return () =>
window.removeEventListener(“resize”,updateDimensions); }, []) const updateDimensions = () => {
const width = window.innerWidth
setWindowWidth(width)
} //…rest of App.js below…
}
Updating The Elements
I’ve opted to create an object that uses conditional logic to determine if any changes to width require showing/hiding the top navigation. This is then passed down as props to the Header.
const responsive = {
showTopNavMenu: width > 1023
}return (
<>
<Header showTopNavMenu={responsive.showTopNavMenu}/>
<Main />
</>
)
The Header then works out its own logic to determine the exact CSS that will be used to show/hide specific DOM elements. In this case, the elements will either have their display property set to “flex” or “none”.
const showNav= {
display: showTopNavMenu ? 'flex' : 'none'
}const showMenuIcon = {
display: showTopNavMenu ? 'none' : 'flex',
}return (
<>
<div id="nav_container" style={showNav}>
<nav id="navigation">{navMenuItems}</nav>
</div>
<span className="menu-icon" style={showMenuIcon}/>
</>
)
Here’s what those DOM elements look like in developer tools:
Desktop:

Tablet:

Pros/Cons
Pros
- Responsive design no longer depends on an external CSS file
- The Component is more self contained and reusable
- CSS is scoped to this Component with less chance of conflicting CSS
Cons
- Considerably more work to implement than Media Queries
- Media Queries are more easily and intuitively read
Higher Order Components
There are quite a few 3rd party libraries to choose from and for this article I went with React-Socks. Another one that I debated using was react-use-media, however, I was looking for a more Higher Order Component approach which is why I went with React-Socks.
CodeSandbox: https://codesandbox.io/s/mars-react-socks-xg8z2
Using the library requires installing it via npm or yarn. I’ve used CodeSandbox for these demos and imported it via “Add Dependencies”.
Once installed we then import the Breakpoint and BreakpointProvider Components and wrap the top level Component with BreakpointProvider.
import { Breakpoint, BreakpointProvider } from ‘react-socks’;function App() {//...rest of App.js above....return (
<BreakpointProvider>
<Header />
<Main />
</BreakpointProvider>}
This Higher Order Component now provides access to the Breakpoint Component in all child Components within the heirarchy.
The library also provides a few configurations for listening for common breakpoints. The easiest is using small, medium, and large keywords to target breakpoints and then assigning up or down as to which direction they should apply.
The nav_container will only show on large desktops and higher (>1023) and the menu-icon will only show on medium and below (<767).
<Breakpoint large up>
<div id=”nav_container” ></div>
</Breakpoint><Breakpoint medium down>
<div className=”menu-icon”></div>
</Breakpoint>
Here’s what those settings look like in developer tools when applied. Take note that the class name syntax uses the BEM approach to assigning class names.
Desktop:
Tablet:
Pros/Cons
Pros
- Easy to implement and configure
- Class names in Dev Tools are semantic and reflect the changes
Cons
- Noticed a small delay in removing/adding the nav elements
- Didn’t work on inline elements, such as spans, but that might be just a limitation for this library
- Required changing inline elements to block or using divs instead of spans
Final Thoughts
As React continues to evolve and push the boundaries of Front-End Dev so does our need to rethink old paradigms. If you’re comfortable with Media Queries then by all means keep using them, but as more and more developers lean towards a CSS-in-JS approach you’ll find yourself hard pressed not to move in that direction.
Do keep in mind that although the cons of differnt approaches are small in this example, in a real world production application, these cons could exacerbate due to higher complexity and lead to performance issues.
Special shout out to Thomas Nickels for his additional feedback and comments that helped add some finishing touches to the article.
