Marten Jacobs
Marten Jacobs

Reputation: 307

Material UI responsive based on element size

I'm using React and MaterialUI to build a system that will display widgets inside another (not React-based) web site. I'd like to make the widgets responsive, but they need to respond to their own container width rather than the window width, as I won't know how much of the page the widget will take up.

Options I've considered:

These all seem rather ugly solutions to me. Is there an elegant way to do what I want?

Upvotes: 11

Views: 6284

Answers (2)

Lucas Basquerotto
Lucas Basquerotto

Reputation: 8143

Instead of breakpoints you can listen to changes to the component size. You can use react-use hook useMeasure to achieve that (it relies on ResizeObserver, which is supported by all major browsers), like in the following example:

/** @jsx jsx */
import { css, jsx } from '@emotion/core';
import { faAddressBook, faCoffee } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { useMeasure } from 'react-use';

const useMyComponentStyle = (params) => {
    const { color } = params;
    const [ref, { width }] = useMeasure();

    const borderColor = width < 150 ? 'red' : width < 400 ? 'yellow' : 'green';
    const icon = width < 250 ? faCoffee : faAddressBook;

    const style = css`
        color: ${color};
        padding: 10px;
        border: 1px solid ${borderColor};
    `;

    return {
        ref,
        style,
        icon,
        width,
    };
};

export const MyComponent = (props) => {
    const { ref, style, icon, width } = useMyComponentStyle(props);

    return (
        <div ref={ref} css={style}>
            <FontAwesomeIcon icon={icon} />
            {props.children} [[{parseInt('' + width)}px]]
        </div>
    );
};

const containerStyle = css`
    padding: 100px 200px;
    border: 1px solid blue;
`;

export const MyContainer = () => (
    <div css={containerStyle}>
        <MyComponent color='blue'></MyComponent>
    </div>
);

ReactDOM.render(<MyContainer />, document.getElementById('root'));

The example above uses emotion for the css styles, but the style could be defined using another library, like jss or styled components, or even plain react inline style.

The component MyComponent is included inside the container component MyContainer that has left and right padding with value 200px, and you can see as you resize your browser view that the border color of MyComponent is based on the size of the component itself (red if the width of the component is less than 150px, yellow if it's less than 400px, otherwise it's green), not based on the size of the window.

Upvotes: 1

Andrei
Andrei

Reputation: 1873

To make various @material-ui/core elements take up only as much space as the container you place them in, I'd use the Grid component.

I've used the following:

<Grid container spacing={24}>
  <Grid item>
    Your content here
  </Grid>
</Grid>

If you further want your grid item to be responsive to things like screen size, you may do:

const grid = {
    xs: 24,
    sm: 24,
    md: 24,
    lg: 12,
    xl: 12,
};

and

<Grid item {...grid}>

Documentation: https://material-ui.com/layout/grid/

Upvotes: 0

Related Questions