Skip to main content
react-md

Responsive Item

The ResponsiveItem component can be used to make images, videos, iframes, or other media elements responsive within a relative container with built-in support for forcing specific aspect ratios when the item's size is unknown.

See the objectFit class name utility function for a simpler implementation.

Simple Example

This example will show how to create responsive images by wrapping the <img> element in the ResponsiveItem. The default behavior will be to respect the <img> dimensions but scale them down into the container's width.

Try resizing the browser or updating the columns constant in the next demo to see how it works.

import { Box } from "@react-md/core/box/Box";
import { type BoxOptions } from "@react-md/core/box/styles";
import { Card } from "@react-md/core/card/Card";
import { ResponsiveItem } from "@react-md/core/responsive-item/ResponsiveItem";
import { type ReactElement } from "react";

export default function SimpleExample(): ReactElement {
  return (
    <Box align="stretch" grid fullWidth gridColumns={columns}>
      <Card align="center" justify="center">
        <ResponsiveItem>
          <img src="https://picsum.photos/200/300?image=30" alt="" />
        </ResponsiveItem>
      </Card>
      <Card align="center" justify="center">
        <ResponsiveItem>
          <img src="https://picsum.photos/300/200?image=3" alt="" />
        </ResponsiveItem>
      </Card>
      <Card align="center" justify="center">
        <ResponsiveItem>
          <img src="https://picsum.photos/300?image=1008" alt="" />
        </ResponsiveItem>
      </Card>
      <Card align="center" justify="center">
        <ResponsiveItem>
          <img src="https://picsum.photos/100/110?image=233" alt="" />
        </ResponsiveItem>
      </Card>
    </Box>
  );
}

// eslint-disable-next-line prefer-const
let columns: BoxOptions["gridColumns"] = undefined;
// columns = "fit";
// columns = "fill";
// columns = 2;

Press Enter to start editing.

Force Aspect Ratio

When dealing with unknown content, the aspect ratio can be forced using the aspectRatio prop. The default aspect ratios are: 16-9, 4-3, and 1-1.

import { Box } from "@react-md/core/box/Box";
import { Card } from "@react-md/core/card/Card";
import { ResponsiveItem } from "@react-md/core/responsive-item/ResponsiveItem";
import { type ReactElement } from "react";

export default function ForceAspectRatio(): ReactElement {
  return (
    <Box grid fullWidth>
      <Card>
        <ResponsiveItem aspectRatio="16-9">
          <img src="https://picsum.photos/400/300?image=3" alt="" />
        </ResponsiveItem>
      </Card>
      <Card>
        <ResponsiveItem aspectRatio="4-3">
          <img src="https://picsum.photos/400/300?image=3" alt="" />
        </ResponsiveItem>
      </Card>
      <Card>
        <ResponsiveItem aspectRatio="1-1">
          <img src="https://picsum.photos/300/400?image=3" alt="" />
        </ResponsiveItem>
      </Card>
    </Box>
  );
}

Press Enter to start editing.

YouTube IFrame Example

When a YouTube video should be rendered in an iframe, either the height and width need to be provided to the iframe or an aspect ratio must be set on the ResponsiveItem to work correctly.

import { Card } from "@react-md/core/card/Card";
import { ResponsiveItem } from "@react-md/core/responsive-item/ResponsiveItem";
import { type ReactElement } from "react";

export default function YouTubeIFrame(): ReactElement {
  return (
    <Card fullWidth>
      <ResponsiveItem aspectRatio="16-9">
        <iframe
          src="https://www.youtube.com/embed/kyAn3fSs8_A"
          allowFullScreen
          title="Archer - Highway To The Dangerzone"
          style={{ border: 0 }}
        />
      </ResponsiveItem>
    </Card>
  );
}

Press Enter to start editing.

Configuring Allowed Aspect Ratios

The generated aspect ratios are configured by the core.$responsive-item-aspect-ratios Sass variable which is a map of x-y: x y.

import { Box } from "@react-md/core/box/Box";
import { Card } from "@react-md/core/card/Card";
import { ResponsiveItem } from "@react-md/core/responsive-item/ResponsiveItem";
import { type ReactElement } from "react";

import styles from "./ConfiguringAllowedAspectRatios.module.scss";

export default function ConfiguringAllowedAspectRatios(): ReactElement {
  return (
    <Box className={styles.container} grid fullWidth>
      <Card>
        <ResponsiveItem aspectRatio="16-9">
          <img src="https://picsum.photos/400/300?image=3" alt="" />
        </ResponsiveItem>
      </Card>
      <Card>
        <ResponsiveItem aspectRatio="4-3">
          <img src="https://picsum.photos/400/300?image=3" alt="" />
        </ResponsiveItem>
      </Card>
      <Card>
        <ResponsiveItem aspectRatio="1-1">
          <img src="https://picsum.photos/300/400?image=3" alt="" />
        </ResponsiveItem>
      </Card>
    </Box>
  );
}

Press Enter to start editing.

@use "sass:list";
@use "@react-md/core" with (
  $responsive-item-aspect-ratios: (
    // these are the default values and can be removed
    // if they aren't needed in your app
    "16-9": 16 9,
    "4-3": 4 3,
    "1-1": 1 1,
  )
);

// this is only required for this demo and how the class names are generated
.container {
  :global {
    @each $suffix, $ratio-list in core.$responsive-item-aspect-ratios {
      .rmd-responsive-item-container--#{$suffix} {
        @include core.responsive-item-aspect-ratio(
          list.nth($ratio-list, 1),
          list.nth($ratio-list, 2)
        );
      }
    }
  }
}

Press Enter to start editing.

Responsive Item Overlay Example

It can sometimes be useful to add an overlay above the responsive item to cover it with a title, description, or other information. Use the ResponsiveItemOverlay component to add this overlayh within a ResponsiveItem.

green trees, grass, and lake at sunrise
Photo by: Michael Hull
import { Box } from "@react-md/core/box/Box";
import { box } from "@react-md/core/box/styles";
import { button } from "@react-md/core/button/styles";
import { Card } from "@react-md/core/card/Card";
import { ResponsiveItem } from "@react-md/core/responsive-item/ResponsiveItem";
import { ResponsiveItemOverlay } from "@react-md/core/responsive-item/ResponsiveItemOverlay";
import { Typography } from "@react-md/core/typography/Typography";
import InfoOutlineIcon from "@react-md/material-icons/InfoOutlineIcon";
import { type ReactElement } from "react";

export default function ResponsiveItemOverlayExample(): ReactElement {
  return (
    <Box grid fullWidth>
      <Card>
        <ResponsiveItem>
          <img
            src="https://picsum.photos/800/800?image=432"
            alt="green trees, grass, and lake at sunrise"
          />
          <ResponsiveItemOverlay
            className={box({ justify: "space-between", disablePadding: true })}
          >
            <Typography type="headline-5" margin="none">
              Photo by: Michael Hull
            </Typography>
            <a
              aria-label="Unsplash photo source"
              rel="noreferrer"
              href="https://unsplash.com/photos/photo-of-green-trees-grass-and-lake-at-sunrise-vuWB5mGu5wE"
              target="_blank"
              className={button({ buttonType: "icon" })}
            >
              <InfoOutlineIcon />
            </a>
          </ResponsiveItemOverlay>
        </ResponsiveItem>
      </Card>
    </Box>
  );
}

Press Enter to start editing.

Overlay Positions

The ResponsiveItemOverlay supports the following positions:

Top
Right
Bottom (default)
Left
Middle
Center
Absolute Center
import { Box } from "@react-md/core/box/Box";
import { Card } from "@react-md/core/card/Card";
import { ResponsiveItem } from "@react-md/core/responsive-item/ResponsiveItem";
import { ResponsiveItemOverlay } from "@react-md/core/responsive-item/ResponsiveItemOverlay";
import { Typography } from "@react-md/core/typography/Typography";
import { type ReactElement } from "react";

export default function OverlayPositions(): ReactElement {
  return (
    <Box grid fullWidth gridColumns={1}>
      <Card>
        <ResponsiveItem aspectRatio="16-9">
          <img src="https://picsum.photos/600/600?image=404" alt="" />
          <ResponsiveItemOverlay position="top">
            <Typography type="headline-5" margin="none">
              Top
            </Typography>
          </ResponsiveItemOverlay>
        </ResponsiveItem>
      </Card>
      <Card>
        <ResponsiveItem aspectRatio="16-9">
          <img src="https://picsum.photos/600/600?image=404" alt="" />
          <ResponsiveItemOverlay position="right">
            <Typography type="headline-5" margin="none">
              Right
            </Typography>
          </ResponsiveItemOverlay>
        </ResponsiveItem>
      </Card>
      <Card>
        <ResponsiveItem aspectRatio="16-9">
          <img src="https://picsum.photos/600/600?image=404" alt="" />
          <ResponsiveItemOverlay position="bottom">
            <Typography type="headline-5" margin="none">
              Bottom (default)
            </Typography>
          </ResponsiveItemOverlay>
        </ResponsiveItem>
      </Card>
      <Card>
        <ResponsiveItem aspectRatio="16-9">
          <img src="https://picsum.photos/600/600?image=404" alt="" />
          <ResponsiveItemOverlay position="left">
            <Typography type="headline-5" margin="none">
              Left
            </Typography>
          </ResponsiveItemOverlay>
        </ResponsiveItem>
      </Card>
      <Card>
        <ResponsiveItem aspectRatio="16-9">
          <img src="https://picsum.photos/600/600?image=404" alt="" />
          <ResponsiveItemOverlay position="middle">
            <Typography type="headline-5" margin="none" textAlign="center">
              Middle
            </Typography>
          </ResponsiveItemOverlay>
        </ResponsiveItem>
      </Card>
      <Card>
        <ResponsiveItem aspectRatio="16-9">
          <img src="https://picsum.photos/600/600?image=404" alt="" />
          <ResponsiveItemOverlay position="center">
            <Typography type="headline-5" margin="none" textAlign="center">
              Center
            </Typography>
          </ResponsiveItemOverlay>
        </ResponsiveItem>
      </Card>
      <Card>
        <ResponsiveItem aspectRatio="16-9">
          <img src="https://picsum.photos/600/600?image=404" alt="" />
          <ResponsiveItemOverlay position="absolute-center">
            <Typography type="headline-5" margin="none" textAlign="center">
              Absolute Center
            </Typography>
          </ResponsiveItemOverlay>
        </ResponsiveItem>
      </Card>
    </Box>
  );
}

Press Enter to start editing.

Customizing Styles

The global overlay styles can be configured by changing the following sass variables:

Otherwise, custom styles can be provided using the className prop like normal.

Overlay Text
import { Box } from "@react-md/core/box/Box";
import { Card } from "@react-md/core/card/Card";
import { ResponsiveItem } from "@react-md/core/responsive-item/ResponsiveItem";
import { ResponsiveItemOverlay } from "@react-md/core/responsive-item/ResponsiveItemOverlay";
import { Typography } from "@react-md/core/typography/Typography";
import { type ReactElement } from "react";

import styles from "./CustomizingStyles.module.scss";

export default function CustomizingStyles(): ReactElement {
  return (
    <Box grid fullWidth className={styles.container}>
      <Card>
        <ResponsiveItem>
          <img alt="" src="https://picsum.photos/800/800?image=433" />
          <ResponsiveItemOverlay position="absolute-center">
            <Typography type="headline-5" margin="none">
              Overlay Text
            </Typography>
          </ResponsiveItemOverlay>
        </ResponsiveItem>
      </Card>
    </Box>
  );
}

Press Enter to start editing.

@use "sass:color";
@use "@react-md/core/colors";
@use "@react-md/core" with (
  $responsive-item-overlay-background-color: rgba(colors.$black, 0.54),
  // the `$responsive-item-overlay-color` is really `$dark-theme-text-primary-color`, but can be used in this demo
  $responsive-item-overlay-color: color.adjust(colors.$white, $lightness: -15%),
  $responsive-item-overlay-z-index: 0,
  $responsive-item-overlay-padding: 1rem,
  // try updating the width to `auto` or another value
  $responsive-item-overlay-horizontal-width: 30%
);

// this is only required for the demo
.container {
  :global {
    @include core.responsive-item-styles;
  }
}

Press Enter to start editing.