AspectRatio
A layout primitive that constrains its content to a fixed width-to-height ratio.
Overview
An AspectRatio reserves a box at a fixed width-to-height ratio using the native CSS aspect-ratio
property — the classic use is holding responsive media (an image, a video, a map embed) so the
layout doesn’t reflow while the asset loads. It fills the available width and derives its height
from the ratio.
Anatomy
A single <div> set to width: 100%, the given aspect-ratio, and overflow: hidden. Size a
single child to width: 100%; height: 100% (e.g. an <img> with object-fit: cover) to fill the
box. There is no sketch layer — this primitive is purely structural.
Variants & States
AspectRatio is a static layout box — it has no interactive states. The only variation is the
ratio value itself.
| State | Status | Notes |
|---|---|---|
| Hover | Not implemented | Not interactive. |
| Focus | Not implemented | Not focusable. |
| Disabled | Not implemented | — |
| Loading | Not implemented | — |
| Error | Not implemented | — |
Usage
Do
- Place a single child sized to width: 100%; height: 100% (an <img> with object-fit: cover is the common case).
- Pass the ratio as a number expression like 16 / 9 or 4 / 3 so intent stays readable.
- Constrain the outer width (e.g. a max-width wrapper) — the box derives its height from that width.
Don't
- Don't set an explicit height on the box — the ratio derives it from the width.
- Don't use it purely to add spacing; it's for reserving media proportions, not general layout gaps.
Accessibility
- Renders a plain
<div>with no role or label — it is a layout wrapper, so the accessible content is whatever child you place inside (e.g. an<img>needs its ownalt). - Because it reserves space before media loads, it prevents layout shift — a real accessibility and usability win for users on slow connections.
- See the Accessibility Guide for shared conventions.
Content
N/A — AspectRatio has no fixed content model. Content is whatever single element you place inside, sized to fill the box.
Props API
React@ghds/react
| Prop | Type | Default | Description |
|---|---|---|---|
ratio | number | 1 | Desired width-to-height ratio, e.g. 16 / 9 for widescreen or 1 for a square. |
...rest | HTMLAttributes<HTMLDivElement> | — | Native <div> attributes (className, style, etc.) pass through. |
Web Components@ghds/web-components
| Prop | Type | Default | Description |
|---|---|---|---|
ratio | number | 1 | Reflected attribute; sets the host aspect-ratio. Slot a single child to fill the box. |
React Native@ghds/react-native
| Prop | Type | Default | Description |
|---|---|---|---|
ratio | number | 1 | Width-to-height ratio applied via the native aspectRatio style. |
children | ReactNode | — | Content constrained to the ratio box. |
style | StyleProp<ViewStyle> | — | Extra style merged onto the ratio box. |
accessibilityLabel | string | — | Accessible label for the region. |
testID | string | — | Test handle for queries. |
Code examples
// React — @ghds/react
import { AspectRatio } from '@ghds/react/aspect-ratio';
<AspectRatio ratio={16 / 9}>
<img src="https://example.com/cover.jpg" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
</AspectRatio>;
<!-- Web Components — @ghds/web-components -->
<script type="module">
import '@ghds/web-components/aspect-ratio';
</script>
<gh-aspect-ratio ratio="1.7778">
<img src="https://example.com/cover.jpg" style="width: 100%; height: 100%; object-fit: cover;" />
</gh-aspect-ratio>
// React Native — @ghds/react-native
import { AspectRatio } from '@ghds/react-native/aspect-ratio';
import { Image } from 'react-native';
<AspectRatio ratio={16 / 9}>
<Image
source={{ uri: 'https://example.com/cover.jpg' }}
style={{ width: '100%', height: '100%' }}
resizeMode="cover"
/>
</AspectRatio>;
Live Demo
React and Web Components render live below, driven by the same tokens — toggle dark mode in the
header to see both respond identically. React Native can’t run in a browser without additional
react-native-web wiring (not set up in this site), so its usage is shown as a code sample instead.