DEV Community

Cover image for Project 41 of 100 - Writing Responsive CSS in JSX
James Hubert
James Hubert

Posted on

Project 41 of 100 - Writing Responsive CSS in JSX

Hey! I'm on a mission to make 100 React.js projects ending March 8th. Please follow my dev.to profile or my twitter for updates and feel free to reach out if you have questions. Thanks for your support!

Link to today's deployed app: Link
Link to the repo: github

This was a short project riding the heels of the first draft of a client website. The website is optimized for mobile but we wanted to just put a couple of styles in so that it's comprehensible to desktop viewers. The primary challenge was the static background image element saying (paraphrased from Spanish) "More coming soon".

For mobile screens I wanted to take advantage of the background-size: cover CSS property to simply cover the space. On quite large screens though this can look ridiculous since only maybe a small part of the image will be showing.

In pure CSS this is easily solved by a media query. Simply state the screen size you are optimizing for (say- over 570px wide) and set the styles within the brackets.

In this project I am actually making use of props to pass an image URL to the component so that the component is reusable. Since I'm setting the CSS background property within the JSX, it would override some of the other styles in actual stylesheet.

The CSS is loaded in the

of the html page well before the image URL is passed to this object so this got in the way of me being able to give the image the background-size: cover style.

Instead, we can use Javascript to check for the screen size with the following built-in vanilla Javascript property of the window object:

window.innerWidth
Enter fullscreen mode Exit fullscreen mode

This property returns the inner width of the window thus allowing you to set styles for different screen sizes.

The problem? Well this property is only called once on page load. Frankly for mobile this is just fine, but if someone is looking at it on a browser and decides to resize the window and push it to the side, it won't be called again. To call the property again when the window is resized we take advantage of a callback on the window.onresize method. I am using React useState to set a windowWidth variable to the size of the screen on page load and whenever the screen is resized.

  const [windowWidth,setWindowWidth] = useState(0);

  window.onresize = () => {
    setWindowWidth(window.innerWidth);
  }
Enter fullscreen mode Exit fullscreen mode

Now that we have a variable that updates whenever the screen changes sizes we can create separate style objects to pass to the JSX element for each screen size, like here, where I use a ternary:

  const innerImageStyle = windowWidth < 570 ?
   ({
    width: '100%',
    height: '80vh',
    backgroundImage: `url(${props.imageUrl})`,
    backgroundSize: 'cover',
  }) : 
   ({
    width: '100%',
    height: '500px',
    backgroundImage: `url(${props.imageUrl})`,
    backgroundSize: 'contain',
  });
Enter fullscreen mode Exit fullscreen mode

As you can see if it's a mobile device (less than 570 pixels wide) then we want the background to cover the space available. If it's a desktop computer, we want the background to repeat (client's choice here but I like it).

I'm sure we could also do this using the React useEffect hook, but this is intended to be a simple version and I honestly feel like the built-in Javascript window methods are underestimated and under-taught for how powerful they are.

Let me know what you think and how you do conditional styling for React components in the comments...

Top comments (12)

Collapse
 
hasnaindev profile image
Muhammad Hasnain • Edited

I have to give my honest opinion. This is extremely bad. First of all you should never use objects for styling because whenever React runs the reconciliation algorithm to detect changes, it will obviously make a new copy of the virtual DOM. This means it will create a new object and compare references. It will re-render the component each time and not only when it hits the media query.

Stick to CSS/SCSS!! Or simply use a library like JSS or styled components instead (for dynamic styling). Maybe you are a beginner, which is okay, you are trying things but just keep these things in mind. Keep hustling!

Collapse
 
jameshubert_com profile image
James Hubert

No worries Muhammad I’m trying to do a react project each day so obviously going for quantity over quality. Others have complained about not having useEffect in the component which would obviously re-render the component less often.
Just curious about what you said-
“ whenever React runs the reconciliation algorithm to detect changes, it will obviously make a new copy of the virtual DOM”- does it make a whole new copy of the virtual dom or does it just re-render the component? I thought one of the main value adds of React is that it won’t re render the whole dom when it detects changes in a component.

Collapse
 
hasnaindev profile image
Muhammad Hasnain • Edited

Virtual DOM is an in memory representation of the actual DOM. React creates a second virtual DOM and both of these exist in memory at this point, after "diffing," it only updates the part of actual browser DOM that needs to be updated.

Also, about not using useEffect, each time this component is re-rendered, the code actually keep on adding more event listeners. This is a very good example of memory leak. :D

Collapse
 
developeratul profile image
Minhazur Rahman Ratul

One thing I don't understand is if I don't write a stylesheet in JSX, what if I write in Sass or Css? What is the difference between them?

Collapse
 
jameshubert_com profile image
James Hubert • Edited

Hey Ratul- You can and should have separate style sheets. You import them at the top of the page like so:

import React from “react”;
import “./myStyles.css”;

My blog post is just if you want dynamic styles and can’t use media queries :)

And even then, most of the time you just change the class. You would write the styles for two classes in your style sheet like “.dark-mode-on” and “.dark-mode-off” and then in your JS file toggle between giving the JSX element one of the two classes.

Collapse
 
johnyepthomi profile image
JohnYepthomi • Edited

Worst thing about react is that styles are effected from any CSS files that matches the elements even if it's not from the CSS intented for that component. Like style leak. And we have to resort to styled components and crap. Is there a way to tie a CSS to a component without leaks?

Thread Thread
 
betoshiver profile image
Alberto Shiver

Yes, there is an easy way, you can do CSS modules, (styles.module.css) that way it'll only take the styles from the specified module even if another module had the same class name.

When you declare a className on a jsx component it goes like this example:

className ={module1.btn}

It'll only apply the styles for btn that are declared on the module1 module

Collapse
 
developeratul profile image
Minhazur Rahman Ratul

oh got it. Thanks a lot for explaining brother :)

Collapse
 
palashgdev profile image
Palash Gupta

window.onresize gonna fire every time if you can incorporate debouncing that would be cool

Collapse
 
jameshubert_com profile image
James Hubert

You mean on every resize or every component load? I think either one is ok. I’ll check into this though- maybe you’re right.

Collapse
 
palashgdev profile image
Palash Gupta

yup it impact the website performance also

but there is another cool thing Resize observer

developer.mozilla.org/en-US/docs/W...

check this one out

Collapse
 
morganjay profile image
James Morgan

Would love to see the same or similar thing done with useEffect hook 😁