10

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:

  • Polling the container size on an interval basis
  • Polling the container size on window resize events
  • Setting up the theme breakpoints based on the container and window sizes at startup

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

Marten Jacobs
  • 199
  • 1
  • 9

2 Answers2

1

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.

Lucas Basquerotto
  • 7,260
  • 2
  • 47
  • 61
0

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/

Andrei
  • 1,723
  • 1
  • 16
  • 27
  • 4
    Thanks, but that's not really what I mean. The problem is that the breakpoints (where the grid item will change from 24 to 12 columns in your example) refer to the actual window size of the browser, and not to the container size for my widget. – Marten Jacobs Mar 08 '19 at 14:12
  • @MartenJacobs did you find a solution that works for you? I have the same need – Xavier Feb 09 '20 at 23:27
  • Any progress on this topic? Thanks for info guys. – Ivan Mjartan Feb 09 '22 at 09:26