Media query syntax for Reactjs
Asked Answered
B

16

47

How do I do the following CSS media query in Reactjs?

.heading {
  text-align: right;
  /* media queries */
  @media (max-width: 767px) {
    text-align: center;
  }
  @media (max-width: 400px) {
    text-align: left;
  }
}

I tried the following but it throws a syntax error and fails to compile.

heading: {
  textAlign: 'right',
  @media (maxWidth: '767px') {
    textAlign: 'center';
  }
  @media (maxWidth: '400px') {
    textAlign: 'left';
  }
}
Burghley answered 2/2, 2019 at 9:19 Comment(0)
I
22

If you have a special cases, when you need to get media query result inside you react app (for example, you want to show some component at mobile version), you can use helpers like react-responsive or react-media-hook.

Ileac answered 2/2, 2019 at 11:17 Comment(1)
The problem with this answer, apart from not containing an example, is that it adds listeners for each component needing a media query. If you use it inside a list item or a cell table, you can easily amass absurd amounts of event listeners, which is completely unnecessary. They should only be added once. I've provided an answer showing how this can be done with minimal impact on runtime performance, using a context provider.Nonjoinder
G
50

You can make media queries inside React:

import React, { Component } from 'react';

class App extends Component {
  constructor(props) {
    super(props)
    this.state = { matches: window.matchMedia("(min-width: 768px)").matches };
  }

  componentDidMount() {
    const handler = e => this.setState({matches: e.matches});
    window.matchMedia("(min-width: 768px)").addEventListener('change', handler);
  }
  render() {
    return (
      <div >
      {this.state.matches && (<h1>Big Screen</h1>)}
      {!this.state.matches && (<h3>Small Screen</h3>)}
      </div>
    );
  }
}

export default App;

https://stackblitz.com/edit/react-cu8xqj?file=src/App.js

Glomerate answered 19/11, 2019 at 14:23 Comment(11)
Hello @ferit, am I right to say .. we have to create different components to handle Big Screen and small screen? So every screens I have will need to create for both big and small?Stairhead
@TommyLeong No. It only re-renders when screen size changes.Glomerate
Yes i understand. My ques is, to tackle diff screen size, am I suppose to create different view and render based on screen size?Stairhead
@TommyLeong Exactly.Glomerate
I see.. wouldn't it be too much work say if I have 50 screens? Can we use make use of media query to tackle this?Stairhead
@TommyLeong That's responsive design. You can't have 50 screens, but maybe at most 5 different layouts you can need. Regardless of React and this answer you have to define each of them separately to obtain a responsive design.Glomerate
While interesting, I would recommend against this approach as implemented. Responsive design does not mean you should write multiple versions of every component, and in fact this is what media queries allow you to avoid. Using the listener to conditionally apply styles might be better, but even then it seems like it may get complex fast. Most people would be better off with the answer @josh-pittman gave or using a library as others have suggested.Overwrought
@Streeper The effect is the same with using a css file. Also, I did not say you should write multiple instances of components but you should write multiple instances of rulesets to obtain a responsiveness, which is universal, meaning that you have to do it no matter what approach you take.Glomerate
You can trim this function a bit more by eliminating the handler, the result would be this: window.matchMedia('(min-width: 768px)').addEventListener('change', (e) => this.setState({ matches: e.matches }))Tetherball
Yeah, you are right, using an anonymous handler. But I wanted it to be explicitly defined to make it easier to understand for visitors.Glomerate
If by any chance somebody stumbles over an issue trying to get @ferit answer to work, check your query string! For me, I forgot to put the parenthesis around the query string. So window.matchMedia('max-width: 720px').matches did not work, but window.matchMedia('(max-width: 720px)').matches did work. Thank you, @ferit!Foreandafter
T
34

The answer given by Ferit turned out to be quite useful, however, it was only the example in class components, but it is usually difficult or cumbersome to apply that to functional components, and since sometimes it is problematic to transform a functional component to a class one, here I leave the example using Hooks

import React, { useState, useEffect } from 'react';

const App = () => {
  const [matches, setMatches] = useState(
    window.matchMedia("(min-width: 768px)").matches
  )

  useEffect(() => {
    window
    .matchMedia("(min-width: 768px)")
    .addEventListener('change', e => setMatches( e.matches ));
  }, []);

  return (
    <div >
      {matches && (<h1>Big Screen</h1>)}
      {!matches && (<h3>Small Screen</h3>)}
    </div>
  );
}

export default App;
Tetherball answered 1/8, 2021 at 0:56 Comment(2)
Yes, just forget to write that in the exampleTetherball
FWIW you can also use the resize event type.Lyophilize
I
22

If you have a special cases, when you need to get media query result inside you react app (for example, you want to show some component at mobile version), you can use helpers like react-responsive or react-media-hook.

Ileac answered 2/2, 2019 at 11:17 Comment(1)
The problem with this answer, apart from not containing an example, is that it adds listeners for each component needing a media query. If you use it inside a list item or a cell table, you can easily amass absurd amounts of event listeners, which is completely unnecessary. They should only be added once. I've provided an answer showing how this can be done with minimal impact on runtime performance, using a context provider.Nonjoinder
N
20

You cannot set media queries inline. You will need to create a separate CSS stylesheet and then import the stylesheet.

So the following code would go in a new styles.css file for example.

.heading {
  text-align: right;
  /* media queries */
  @media (max-width: 767px) {
    text-align: center;
  }
  @media (max-width: 400px) {
    text-align: left;
  }
}

Then you can import your new CSS styles file into your react file. For example, you could add import './styles.css' to the top of your App.jsx file (assuming they are both at the root level), or you could import it directly into a specific react component file.

Nankeen answered 2/2, 2019 at 9:24 Comment(7)
Hello, it's not true, see my answer.Glomerate
Neat. Nice update. There was no need to vote my answer down though. This is via a web api, I meant that you can't use media queries inline. This is still true.Nankeen
You are right, my solution is not inline either, so no way to do inline queries. But you are also wrong because your answer implies there is no way to make media queries inside React. That's why I downvoted.Glomerate
I did not imply there is no way to use media queries inside react, my answer specifically outlines how to use media queries in react.Nankeen
You consider external css files as "inside"?Glomerate
Who said anything about inside? I'm not sure what you mean by inside. The question doesn't specify anything about 'inside'. The approach above is how I use media queries in my react projects. This is starting to get petty now. I think your comment is a great addition to the answer, but marking down other correct ways of doing the same thing is counterproductive.Nankeen
Maybe I assumed it should be inside. OK. Please do a trivial change to your answer so I can revert my downvote.Glomerate
R
12

Another way of writing media query in reactjs project:

style.js file:

root: {
 background: "white",

 "@media (max-width: 1920px)": {
   background: "red",
 }
}

style.css file:

.root: {
 background: "white";

 @media (max-width: 1920px) {
   background: "red";
 }
}

Thank you

Rhumb answered 28/6, 2021 at 17:37 Comment(1)
In case of using material UI try this: material-ui.com/customization/breakpointsRhumb
S
9

You can use styled-components if you're used to using css. It would look something like this.

import React from 'react';
import Styled from "styled-components";

function YourComponent() {

    const heading = Styled.h1`
        Text-align:right;

            @media (max-width: 767px) {
                text-align: center;
            }
            @media (max-width: 400px) {
                text-align: left;
            }
    `;

   return(
        <>
           <heading>This is my heading</heading>
        </>
    )
}

If you have a lot of styling you need to do you can do your styles in another js file and import each style as needed. If you do this don't forget to export your style.

Systemic answered 24/3, 2021 at 23:17 Comment(0)
D
3

Drop in no dependencies hook solution. Works with server side rendered Nextjs.

const useQuery = (query: string) => {
  const [matches, setMatches] = useState(false)

  const handleChange = (e) => setMatches( e.matches )

  useEffect(() => {
    const m = window.matchMedia(query)

    setMatches(m.matches)
    
    m.addEventListener('change', handleChange);

    return () => {
      m.removeEventListener('change', handleChange);
    }
  }, []);

  return !matches;
}

const Menu= () => {
  const isMobile = useQuery("(min-width: 768px)");
  ...
}

Drugget answered 14/7, 2022 at 8:29 Comment(0)
G
2

in my case i was using a custom hook to generate some breakpoint values for me:

import { useMediaQuery } from 'react-responsive';

export const useBreakpoints = () => {
  const isMobileSmall = useMediaQuery({ query: '(max-width: 325px)' });
  const isMobileMid = useMediaQuery({ query: '(max-width: 375px)' });
  const isMobileFloor = useMediaQuery({ query: '(max-width: 425px)' });

  const isTabletFloor = useMediaQuery({ query: '(max-width: 426px)' });
  const isTabletMid = useMediaQuery({ query: '(max-width: 768px)' });
  const isTabletCeil = useMediaQuery({ query: '(max-width: 1024px)' });

  const isLaptopFloor = useMediaQuery({ query: '(max-width: 1025px)' });
  const isLaptopCeil = useMediaQuery({ query: '(max-width: 1440px)' });

  const isXHDFloor = useMediaQuery({ query: '(max-width: 1441px)' });
  const isXHDCeil = useMediaQuery({ query: '(max-width: 4096px)' });

  return {
    isMobileSmall,
    isMobileMid,
    isMobileFloor,
    isTabletFloor,
    isTabletMid,
    isTabletCeil,
    isLaptopFloor,
    isLaptopCeil,
    isXHDFloor,
    isXHDCeil,
  };
};

and i was calling it in my component inside of useMemo, which is incorrect :)

so i put it outside of useMemo and it worked as charm!

basically what i'm trying to point you at is avoid using nested hook call!

Gilgai answered 19/12, 2020 at 21:58 Comment(0)
G
2

you can just make your own custom hook like this working snippet of code

hooks/useStyleMediaQuery.js

import { useState, useEffect } from 'react'

export const useStyleMediaQuery = ({ mixOrMax, widthOrHeight, value }) => {

  if (!mixOrMax) mixOrMax = 'min';
  if (!widthOrHeight) widthOrHeight = 'width';

  const [matches, setMatches] = useState(
    window.matchMedia(`(${mixOrMax}-${widthOrHeight}: ${value}px)`).matches
  )

  useEffect(() => {
    window
      .matchMedia(`(${mixOrMax}-${widthOrHeight}: ${value}px)`)
      .addEventListener('change', e => setMatches(e.matches));
  }, [mixOrMax, widthOrHeight, value]);

  return { matches }

}

App.js

import { useStyleMediaQuery } from 'hooks/useStyleMediaQuery'

import ComponentIwantToShowOnlyOnMobile from 'components/ComponentIwantToShowOnlyOnMobile'
import ComponentIwantToShowOnlyOnDesktop from 'components/ComponentIwantToShowOnlyOnDesktop'

function App() {

    const { matches: isMobile } = useStyleMediaQuery({ mixOrMax: 'max', widthOrHeight: 'width', value: 767 });
    const { matches: imSmall } = useStyleMediaQuery({ mixOrMax: 'max', widthOrHeight: 'width', value: 400 });

    return (
        <>
            {isMobile && <ComponentIwantToShowOnlyOnMobile />}
            {!isMobile && <ComponentIwantToShowOnlyOnDesktop />}
            {imSmall && <h1>I'm very small.. 400px width or less</h1>}
        </>
    );
}

export default App;

that's it :)

Guadalajara answered 24/8, 2022 at 20:12 Comment(0)
K
1

there is also a way to include a media query by using boolean values for ex.

<div style={{window.innerWidth > 768 ? '800px' : '400px'}}/>

and this serves the problem well

Koloski answered 30/6, 2020 at 8:55 Comment(1)
That's would not make it responsive. Only after refreshing.Gramercy
S
1

aphrodite can help.

here is an example:

import React from "react";
import { StyleSheet, css } from "aphrodite";

import "./style.css";

const styles = StyleSheet.create({
  heading: {
    textAlign: "right",
    backgroundColor: "red",
    "@media (max-width: 767px)": {
      textAlign: "center",
      backgroundColor: "green"
    },
    "@media (max-width: 767px)": {
      textAlign: "center",
      backgroundColor: "blue"
    }
  }
});

export default function App() {
  return (
    <div className={css(styles.heading)}>
      <h1>Hello aphrodite!</h1>
    </div>
  );
}
Sullage answered 11/4, 2021 at 10:44 Comment(0)
U
1

JSX or reactJS indicatorContainerProps:{ position: 'fixed', right: '0px', bottom: '0%', marginBottom: '40%', display:'flex', justifyContent:'center', flexDirection:'row', alignItems:'center', "@media (orientation:landscape)": { marginBottom: '0px' }

Upstage answered 4/8, 2022 at 17:28 Comment(0)
N
1
import { useResponsiveness } from "react-responsiveness";

const { isMax } = useResponsiveness()
// also available: { isMin, isOnly, currentInterval, matches } 
// ...

  heading: {
    textAlign: isMax('sm') 
      ? 'left'
      : isMax('md')
      ? 'center'
      : 'right'
  }
// provider in App.(js|ts):

import { ResponsivenessProvider } from "react-responsiveness";

const breakpoints: {
  sm: 0,
  md: 401,
  lg: 768
}

function App() {
    // ...
}

const ResponsiveApp = () => (
  <ResponsivenessProvider {...{ breakpoints }}>
    <App />
  </ResponsivenessProvider>
);

export default ResponsiveApp;

Documentation and more examples.

Note: I wrote this package myself, because others were adding a set of listeners for each component needing a media query, instead of adding them only once per app, which is best practice (I'm obsessed with runtime performance and user experience).

Nonjoinder answered 29/8, 2023 at 11:41 Comment(0)
T
0
const styles = (window.innerWidth > window.innerHeight) ? {
    header: {
        display: 'flex',
        justifyContent: 'center',
    },
    body: {
        boxShadow: '0px 0px 5px black',
        display: 'flex',
        justifyContent: 'center',
        flexDirection: 'column'
    }
} : {
    // other styles
}

You can use this syntax for your styles.

Trough answered 9/2, 2022 at 15:37 Comment(0)
I
0

If you don't want many complications put the class itself inside the media query and only change whatever should change at that particular query:

.heading {
   text-align: right;
   width: 360px;
   height: fit-content;
   padding: 10px 0px;
   box-shadow: 1px 3px 2px 0px rgb(206, 117, 1);
   display: flex;


/* media queries */

@media (max-width: 767px) {
.heading{
   text-align: center;
}
}

@media (max-width: 400px) {    
.heading{
   text-align: left;
}
}
Isobel answered 14/1, 2023 at 4:13 Comment(0)
H
0

I ended up with the following solution, similar to Muhammed Moussa's one but with no external libraries:

export default function App() {
  const yourCss = `
    .heading {
       text-align: right;
       /* media queries */
       @media (max-width: 767px) {
         text-align: center;
       }
       @media (max-width: 400px) {
         text-align: left;
       }
    }`;

  return (
    <>
      <style>{yourCss}</style>
      <h1 className="heading">
        Styled heading
      </h1>
    </>
  );
}
Healion answered 19/7 at 19:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.