Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[joy-ui] Add useMediaQuery hook #35037

Open
2 tasks done
scherroman opened this issue Nov 7, 2022 · 14 comments
Open
2 tasks done

[joy-ui] Add useMediaQuery hook #35037

scherroman opened this issue Nov 7, 2022 · 14 comments
Assignees
Labels
docs Improvements or additions to the documentation hook: useMediaQuery package: joy-ui Specific to @mui/joy ready to take Help wanted. Guidance available. There is a high chance the change will be accepted

Comments

@scherroman
Copy link

scherroman commented Nov 7, 2022

Duplicates

  • I have searched the existing issues

Latest version

  • I have tested the latest version

Summary 💡

I'd like to be able to import and use useMediaQuery with Joy UI just like Material UI supports to adapt my layout for different screen sizes

Examples 🌈

import { Button, IconButton, useMediaQuery } from '@mui/joy'
import SettingsIcon from '@mui/icons-material/Settings'

const NavigationBar = () => {
    let isScreenMobile = useMediaQuery((theme) =>
        theme.breakpoints.down('mobile')
    )

    return (
        <div>
            {isScreenMobile ? (
                <Button startDecorator={<SettingsIcon />}>
                    Settings
                </Button>
            ) : (
                <IconButton>
                    <SettingsIcon />
                </IconButton>
            )}
        </div>
    )
}

Motivation 🔦

I'm trying to adapt my interface to have my navigation bar show just an IconButton when on a tablet or larger sized screen, but a larger regular Button with an icon when on a mobile sized screen. At the moment I appear to be unable to do so without something like useMediaQuery which has access to my theme's media breakpoints, which makes the app less friendly and functional on mobile. While the sx prop is very useful for adapting individual components, it's common to want to render entirely different components at different breakpoints.

For reference, my breakpoints are set to:

mobile: 0,
tablet: 640,
laptop: 1024,
desktop: 1200

Many well-designed web apps like Twitter have elements like side bars that collapse, disappear completely (like the "What's happening" bar on the righthand side) or are replaced by new layouts (like the navigation bar on the lefthand side, which collapses at smaller sizes and is wholly replaced by a bottom navigation bar on mobile). This kind of adaptability is essential for designing a responsive/mobile-friendly app.

If there's a workaround in the meantime I'd be happy to learn it! I love the aesthetic of Joy UI much more than Material UI 2, so am eager and making an effort to use it although it's still in alpha.

@scherroman scherroman added the status: waiting for maintainer These issues haven't been looked at yet by a maintainer label Nov 7, 2022
@scherroman
Copy link
Author

scherroman commented Nov 7, 2022

Dug around some more and found the Display page of MUI System which recommends using the sx breakpoints to hide and show elements at different screen sizes using the display property. Makes sense and works well! Also would work better than a hook if using server-side rendering. Though if you use display: block to show buttons it messes up their styling. Tried to just omit the breakpoints where the component is visible as it's redundant, but that causes the button not to show, so had to look in the browser console to find that normally display: inline-flex is used and explicitly use that. Would be nice to have a way to just use the default or set display to 'whatever the value should be/would have been if I wasn't setting it'

<IconButton
    sx={{
        display: { mobile: 'none', tablet: 'inline-flex' }
    }}
>
    <SettingsIcon />
</IconButton>
<Button
    startDecorator={<SettingsIcon />}
    sx={{
        display: { mobile: 'inline-flex', tablet: 'none' }
    }}
>
    Settings
</Button>

@scherroman
Copy link
Author

Reopening this as I've run into a situation where I want to adapt a modal from layout='fullscreen' when on mobile to layout='center' when on tablet or larger, which is not covered by the use of breakpoints on the sx property. Passing in an object with breakpoints like <ModalDialog layout={{ mobile: 'fullscreen', tablet: 'center' }}> doesn't appear to work either.

It seems the useMediaQuery hook is needed to support this. In the meantime, I've gotten around this by using the useMedia hook from react-use:

import { useMedia } from 'react-use'

import {
    Modal,
    ModalDialog,
    useTheme
} from '@mui/joy'

const EditModal = () => {
    let theme = useTheme()
    let isMobile = useMedia(`(max-width: ${theme.breakpoints.values.tablet}px)`)

    return (
        <Modal open onClose={onClose}>
            <ModalDialog layout={isMobile ? 'fullscreen' : 'center'}>
            </ModalDialog>
        </Modal>
    )
}

@siriwatknp
Copy link
Member

Honestly, I don't recommend using JS to switch between layouts. Always use CSS if you can.

This is better for sure:

<Modal open onClose={onClose}>
    <ModalDialog layout="center" sx={theme => ({
      [theme.breakpoints.only('xs')]: {
        top: 0,
        left: 0,
        right: 0,
        bottom: 0,
        border: 0,
        borderRadius: 0,
        transform: 'initial',
      }
    })}>
    </ModalDialog>
</Modal>

I am changing this issue to a question instead.

@siriwatknp siriwatknp removed the status: waiting for maintainer These issues haven't been looked at yet by a maintainer label Nov 28, 2022
@siriwatknp siriwatknp changed the title Support useMediaQuery for Joy UI [ModalDialog][Joy] Switch between layouts with media query Nov 28, 2022
@siriwatknp siriwatknp changed the title [ModalDialog][Joy] Switch between layouts with media query [Joy] Media query usage Nov 28, 2022
@siriwatknp
Copy link
Member

After reading the original issue, it is not about the Modal. I will try to add some docs about the media query to Joy UI.

@siriwatknp siriwatknp reopened this Nov 28, 2022
@siriwatknp siriwatknp added the docs Improvements or additions to the documentation label Nov 28, 2022
@scherroman
Copy link
Author

Honestly, I don't recommend using JS to switch between layouts. Always use CSS if you can.

This is better for sure:

<Modal open onClose={onClose}>
    <ModalDialog layout="center" sx={theme => ({
      [theme.breakpoints.only('xs')]: {
        top: 0,
        left: 0,
        right: 0,
        bottom: 0,
        border: 0,
        borderRadius: 0,
        transform: 'initial',
      }
    })}>
    </ModalDialog>
</Modal>

Thanks for showing that! Though are there any reasons to prefer using css over js aside from css better supporting server-side rendering? It does seem like a lost opportunity to have to directly override all the styles already applied by a property like layout to replicate a different layout at certain breakpoints, instead of being able to simply change the value of the property itself with something like layout={{ mobile: 'fullscreen', tablet: 'center' }} or some similar way.

@siriwatknp
Copy link
Member

Though are there any reasons to prefer using css over js aside from css better supporting server-side rendering?

I think CSS is simpler, and framework agnostic. This is just one small example but in a production app, the more JS you have the more complexity you have to handle as well.

layout={{ mobile: 'fullscreen', tablet: 'center' }}

I have been thinking about providing style utilities in Joy UI to make it easier for developers in situations like this:

import ModalDialog, { getLayoutFullscreenStyle } from '@mui/joy/ModalDialog';

<ModalDialog
  layout="center"
  sx={theme => ({
    [theme.breakpoints.only('xs')]: getLayoutFullscreenStyle(),
  })}
/>

I think this will benefit the developer's productivity and allows us to create tailor-made styles that won't impact production bundle size if you don't use them.

What do you think?

@scherroman
Copy link
Author

I have been thinking about providing style utilities in Joy UI to make it easier for developers in situations like this:

import ModalDialog, { getLayoutFullscreenStyle } from '@mui/joy/ModalDialog';

<ModalDialog
  layout="center"
  sx={theme => ({
    [theme.breakpoints.only('xs')]: getLayoutFullscreenStyle(),
  })}
/>

I think this will benefit the developer's productivity and allows us to create tailor-made styles that won't impact production bundle size if you don't use them.

That would be a big improvement over having to manually specify the styles as it's simpler, clearer, and less mental overhead/error-prone in determining which styles to override.

Personally however I love the simplicity, conciseness, and explicitness of being able to optionally pass in an object with breakpoints to properties the most, similar to how the Stack and Grid components from MUI System allow for some properties:

<Stack direction={{ xs: 'column', sm: 'row' }} spacing={{ xs: 1, sm: 2, md: 4 }}>

<Grid container spacing={{ xs: 2, md: 3 }} columns={{ xs: 4, sm: 8, md: 12 }}>

Not sure if this is more difficult to achieve for higher-level properties, or about its impact on bundle size and the best way to optimize there, but my biggest consideration would be for the fluidity and expressiveness of the API. Perhaps the two methods are not mutually exclusive

@justintoman
Copy link
Contributor

I'm trying to render a table with fewer columns on smaller screens and I can't set colSpan dynamically with css/sx, so I need useMediaQuery or to not use a table. Joy supports <Table> with native <td> / <th>, so I think it could make sense to add this feature as well.

@Studio384
Copy link
Contributor

Is there any progress on this? It would be great if hooks like these aren't specific to Material (or Joy for that matter). So far all use cases mention here have been related to width/height/dimensions, but I personally also have various use-cases for this hook that depend on capabilities of hover, the pointer type, whether or not its a screen, etc.

@danilo-leal danilo-leal changed the title [Joy] Media query usage [joy-ui] Add media query usage Sep 25, 2023
@danilo-leal danilo-leal added the ready to take Help wanted. Guidance available. There is a high chance the change will be accepted label Sep 25, 2023
@crusaider
Copy link

crusaider commented Oct 10, 2023

Another use case where at least I can not make use of the sx prop is the Drawer component. I have a need to be able to anchor the drawer to the left side of the screen on desktop but to the right side on mobile. I can not see a easy way of doing that without giving the Drawer component different values of the the anchor prop.

@siriwatknp siriwatknp changed the title [joy-ui] Add media query usage [joy-ui] Add useMediaQuery hook Oct 11, 2023
@siriwatknp
Copy link
Member

The useMediaQuery can be moved to @mui/system to be shared across Material UI and Joy UI.

Does anyone want to take this?

@justintoman
Copy link
Contributor

@siriwatknp I can try!

I'm thinking move the hook into@mui/system then re-export it from @mui/material to prevent breaking changes.

@Sen-442b
Copy link

Sen-442b commented Dec 2, 2023

Can we not use the useMediaQuery hook from @mui/material using experimental_extendTheme?

@AlanGreyjoy
Copy link

Create your own hook

import { useEffect, useState } from 'react'

const useMediaQuery = query => {
  const [matches, setMatches] = useState(false)

  useEffect(() => {
    const mediaQuery = window.matchMedia(query)
    setMatches(mediaQuery.matches)

    const handler = event => setMatches(event.matches)
    mediaQuery.addEventListener('change', handler)

    return () => mediaQuery.removeEventListener('change', handler)
  }, [query])

  return matches
}

export default useMediaQuery

In a file

import useMediaQuery from 'somewhere/in/your/project/useMediaQuery';

const isMdOrSmaller = useMediaQuery('(max-width: 900px)')
console.log('isMdOrSmaller', isMdOrSmaller)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs Improvements or additions to the documentation hook: useMediaQuery package: joy-ui Specific to @mui/joy ready to take Help wanted. Guidance available. There is a high chance the change will be accepted
Projects
None yet
Development

No branches or pull requests

9 participants