Common usage:
Advanced usage:
Your app must include a <Provider>. It creates a React context provider with a ResizeObserver instance as its value. This allows components nested under <Provider> to be observed for size changes.
<Provider
{/* (optional) ResizeObserver constructor to be used instead of window.ResizeObserver */}
ponyfill={window.ResizeObserver}
>
{/* observable children */}
</Provider>ponyfill — optional typeof ResizeObserver
Provider instantiates a window.ResizeObserver by default. window.ResizeObserver currently has fair browser support. You may pass a ResizeObserver constructor to Provider to use instead of window.ResizeObserver. I recommend ponyfilling using @juggle/resize-observer. You can also monkey patch window.ResizeObserver and use Provider without the ponyfill prop.
import { Provider as ResizeObserverProvider } from '@envato/react-breakpoints';
import { ResizeObserver } from '@juggle/resize-observer'; // optional ponyfill
const App = () => <ResizeObserverProvider ponyfill={ResizeObserver}>...</ResizeObserverProvider>;You can observe changes of an element's boxSize by rendering it through <Observe>'s children. Your child function receives a observedElementProps argument that you spread onto the DOM element you wish to observe. If you passed the breakpoints prop, the child function also receives widthMatch and heightMatch arguments which match the values you assigned to this prop.
boxSize you observe and the boxSize you pass to your breakpoints. See Observing vs. Consuming ResizeObserverSize for more information.
<Observe
{/* (optional) set which box to observe to on the observed element */}
box='content-box'
{/* (optional) set breakpoint options */}
{/* only needed if you wish to access `widthMatch` or `heightMatch` in the child function */}
breakpoints={{
/* see `options` object of `useBreakpoints()` - this object has the exact same shape */
}}
>
{/* pass a child function */}
{
({
/* object of props to spread onto the element you wish to observe */
observedElementProps,
/* (optional) observed matching value from the `widths` option provided in the `breakpoints` prop */
widthMatch,
/* (optional) observed matching value from the `heights` option provided in the `breakpoints` prop */
heightMatch
}) => (
<>
{/* parent with access to observations via useBreakpoints() */}
<ParentComponent>
{/* observed element does not have to be a div, can be any DOM element! */}
<div {...observedElementProps}>
{/* children with access to observations via useBreakpoints() */}
<ChildComponent />
</div>
{/* sibling with access to observations via useBreakpoints() */}
<SiblingComponent />
</ParentComponent>
{/* component without useBreakpoints() can still be told about breakpoint values */}
<DumbComponent
horizontalBreakpoint={widthMatch}
verticalBreakpoint={heightMatch}
/>
</>
)
}
</Observe>box — optional ResizeObserverBoxOptions
Depending on your implementation of ResizeObserver, the internal ResizeObserverEntry can contain size information about multiple "boxes" of the observed element.
This library supports observing the following box options (but your browser may not!):
If box is left undefined or set to any value other than those listed above, <Observe> will default to using information from ResizeObserverEntry.contentRect.
boxSize you observe and the boxSize you pass to your breakpoints. See Observing vs. Consuming ResizeObserverSize for more information.
breakpoints — optional object
This prop accepts an object with a shape identical to the options object of useBreakpoints().
children — (args: RenderOptions) => ReactNode
interface RenderOptions {
observedElementProps: {
ref: React.RefCallback<ObservedElement | null>
};
widthMatch: any;
heightMatch: any;
}The children prop takes a function with three arguments.
observedElementProps—object
Using the...spread operator, you apply this object to the DOM element you want to observe.widthMatch—any
If you passed an object with awidthsproperty to thebreakpointsprop, this argument contains the currently matching width breakpoint value.heightMatch—any
If you passed an object with aheightsproperty to thebreakpointsprop, this argument contains the currently matching height breakpoint value.
Your function should at least return some JSX with {...observedElementProps} applied to a DOM element.
render — (args: RenderOptions) => ReactNode
If you prefer a render prop over children, you may use render. Note that if both provided, <Observe> will use children.
interface RenderOptions {
observedElementProps: {
ref: React.RefCallback<ObservedElement | null>
};
widthMatch: any;
heightMatch: any;
}A render prop that takes a function with three arguments.
observedElementProps—Object
Using the...spread operator, you apply this object to the DOM element you want to observe.widthMatch—any
If you passed an object with awidthsproperty to thebreakpointsprop, this argument contains the currently matching width breakpoint value.heightMatch—any
If you passed an object with aheightsproperty to thebreakpointsprop, this argument contains the currently matching height breakpoint value.
Your function should at least return some JSX with {...observedElementProps} applied to a DOM element.
import { Observe } from '@envato/react-breakpoints';
const MyObservingComponent = () => (
<Observe
box='border-box'
breakpoints={{
box: 'content-box',
widths: {
0: 'mobile',
769: 'tablet',
1025: 'desktop'
},
heights: {
0: 'SD',
720: 'HD-Ready',
1080: 'Full HD',
2160: '4K'
}
}}
>
{({ observedElementProps, widthMatch, heightMatch }) => (
<>
{/* this element is given a class based on a child sidebar's width */}
<article className={widthMatch}>
{/* this sidebar is observed */}
<aside {...observedElementProps}>
{/* this component uses `useBreakpoints()` to adapt to the sidebar's size */}
<MyResponsiveComponent />
</aside>
{/* this component receives one of the `heights` strings defined above based on the sidebar's height */}
<MyVideoComponent quality={heightMatch} />
</article>
{/* this component also uses useBreakpoints() to adapt to the sidebar's size, but from outside the sidebar */}
<MyResponsiveComponent />
</>
)}
</Observe>
);<Observe> to enable the use of its optional breakpoints prop.
Components inside an "<Observe> scope" have access to its observations. The observed element's sizes are available on a context via the useBreakpoints() hook.
The hook takes an options object as its first argument, which must include a widths or heights key (or both) with an object as its value. That object must have a shape of numbers as keys, and a value of any type. The value you set here is what will eventually be returned by useBreakpoints().
Optionally, you can include a box property, which — depending on your implementation of ResizeObserver — can target different observable "boxes" of an element. By default, the legacy contentRect property will be used by useBreakpoints(), but I recommend you use one of the spec's new ResizeObserverSize box sizes.
The hook takes an optional ResizeObserverEntry as its second argument. You probably don't need this, but know that if you pass one, useBreakpoints() will not fetch the entry from the context, so use caution!
/* returns an array of matched breakpoint values */
const {
/* matched value from `options.widths` */
widthMatch,
/* matched value from `options.heights` */
heightMatch
} = useBreakpoints(
{
/* (optional) the boxSize of the observed element to pass to the breakpoint matching logic */
box: 'border-box',
/* (optional) must be specified if `heights` is not specified */
widths: {
/* keys must be numbers and are treated like CSS's @media (min-width) */
0: 'small' /* value can be of any type */,
769: 'medium' /* keys are evaluated in order */,
1025: 'large'
},
/* (optional) must be specified if `widths` is not specified */
heights: {
/* keys must be numbers and are treated like CSS's @media (min-height) */
0: 'SD' /* value can be of any type */,
720: 'HD Ready' /* keys are evaluated in order */,
1080: 'Full HD',
2160: '4K'
},
/* (optional) the boxSize fragment index to match widths and heights on (default 0) */
fragment: 0
},
/* (optional) a ResizeObserverEntry to use instead of the one provided on context */
injectResizeObserverEntry
);options.box — optional ResizeObserverBoxOptions
Depending on your implementation of ResizeObserver, the internal ResizeObserverEntry can contain size information about multiple "boxes" of the observed element.
This library supports the following box options (but your browser may not!):
If box is left undefined or set to any value other than those listed above, useBreakpoints() will default to using information from ResizeObserverEntry.contentRect.
boxSize you observe and the boxSize you pass to your breakpoints. See Observing vs. Consuming ResizeObserverSize for more information.
options.widths — optional object
widths must be an object with numbers as its keys. The numbers represent the minimum width the observed boxSize.inlineSize must be for that key's value to be returned. The value of the highest matching width will be returned, as if using multiple CSS @media (min-width) queries.
For example, when a width of 960 is observed, using the following widths object would return 'medium':
widths: {
0: 'small',
769: 'medium',
1025: 'large'
}0 as a key for widths, you risk receiving undefined as a return value. This is intended behaviour, but makes it difficult to distinguish between receiving undefined because of a Server-Side Rendering scenario, or because the observed width is less than the lowest defined width.
For example, when a width of 360 is observed, using the following widths object would return undefined:
widths: {
769: 'medium',
1025: 'large'
}Values can be of any type, you are not restricted to return string values, and value types can be mixed for different keys.
options.heights — optional object
heights must be an object with numbers as its keys. The numbers represent the minimum height the observed boxSize.blockSize must be for that key's value to be returned. The value of the highest matching height will be returned, as if using multiple CSS @media (min-height) queries.
For example, when a height of 1440 is observed, using the following heights object would return 'Full HD':
heights: {
480: 'SD', /* returns `undefined` for heights <= 479 */
720: 'HD Ready',
1080: 'Full HD',
2160: '4K'
}0 as a key for heights, you risk receiving undefined as a return value. This is intended behaviour, but makes it difficult to distinguish between receiving undefined because of a Server-Side Rendering scenario, or because the observed height is less than the lowest defined height.
Values can be of any type, you are not restricted to return string values, and value types can be mixed for different keys.
options.fragment — optional number
The box sizes are exposed as sequences in order to support elements that have multiple fragments, which occur in multi-column scenarios. You can specify which fragment's size information to use for matching widths and heights by setting this prop. Defaults to the first fragment.
See the W3C Editor's Draft for more information about fragments.
injectResizeObserverEntry — optional ResizeObserverEntry
Allows you to force useBreakpoints() to use the ResizeObserverEntry you pass here in its calculations rather than retrieving the entry that's on the closest <Context>. Because of the Rules of Hooks, React.useContext() will still be called but its returned value is ignored.
import { useBreakpoints } from '@envato/react-breakpoints';
const MyResponsiveComponent = () => {
const options = {
box: 'border-box',
widths: {
0: 'mobile',
769: 'tablet',
1025: 'desktop'
}
};
const { widthMatch: label } = useBreakpoints(options);
return <div className={label}>This element is currently within the {label} range.</div>;
};<Observe> to:
- start and stop observing an element by passing a
refto a DOM element; - bind a standardised callback to all observations, and set the
observedEntryon a<Context>.
This hook takes an optional options object argument, which currently only supports a box option.
const [
/* ref callback to set on the element you want to observe */
ref,
/* all of the observed element's boxSizes */
observedEntry
] = useResizeObserver(
/* (optional) options object */
{
/* (optional) box of the element to observe size changes on, which trigger updates to `observedEntry` */
box: 'border-box'
}
);options.box — optional ResizeObserverBoxOptions
Depending on your implementation of ResizeObserver, you may observe one of multiple "boxes" of an element to trigger an update of observedEntry. By default this option is not set, and the size information of the observed element comes from the legacy contentRect property.
This library supports the following box options (but your browser may not!):
boxSize you observe and the boxSize you pass to your breakpoints. See Observing vs. Consuming ResizeObserverSize for more information.
import { useEffect } from 'react';
import { useResizeObserver, Context } from '@envato/react-breakpoints';
const MyObservedComponent = () => {
const options = {
box: 'border-box'
};
const [ref, observedEntry] = useResizeObserver(options);
/* for example, you can use the observedEntry information internally... */
useEffect(() => {
const firstBorderBoxFragment = observedEntry.borderBoxSize[0];
console.log(`width: ${firstBorderBoxFragment.inlineSize}`);
console.log(`height: ${firstBorderBoxFragment.blockSize}`);
}, [observedEntry]);
/* ...or put it on a Context */
return (
<Context.Provider value={observedEntry}>
<div ref={ref}>This is an observed element</div>
</Context.Provider>
);
};<Observe>.
useBreakpoints() to retrieve the ResizeObserverEntry instance set by <Observe>. It allows you to manually extract its properties, most notably .borderBoxSize, .contentBoxSize, and .devicePixelContentBoxSize.
The hook takes an optional ResizeObserverEntry as its second argument. If you pass one, useResizeObserverEntry() will not fetch it from the context, so use caution!
const resizeObserverEntry = useResizeObserverEntry(
/* (optional) a ResizeObserverEntry to use instead of the one provided on context */
injectResizeObserverEntry
);
/* retrieve width and height from legacy `contentRect` property */
const { width: contentRectWidth, height: contentRectHeight } = resizeObserverEntry.contentRect;
/* retrieve width and height from `borderBoxSize` property of first fragment */
const { inlineSize: borderBoxSizeWidth, blockSize: borderBoxSizeHeight } = resizeObserverEntry.borderBoxSize[0];
/* retrieve width and height from `contentBoxSize` property of first fragment */
const { inlineSize: contentBoxSizeWidth, blockSize: contentBoxSizeHeight } = resizeObserverEntry.contentBoxSize[0];
/* retrieve width and height from `devicePixelContentBoxSize` property of first fragment */
const {
inlineSize: devicePixelContentBoxSizeWidth,
blockSize: devicePixelContentBoxSizeHeight
} = resizeObserverEntry.devicePixelContentBoxSize[0];import { useResizeObserverEntry } from '@envato/react-breakpoints';
const MyResponsiveComponent = () => {
const resizeObserverEntry = useResizeObserverEntry();
/**
* `null` if element from Context has not been observed yet.
* This is mostly the case when doing Server-Side Rendering.
*/
if (!resizeObserverEntry) {
/* ... */
}
const { inlineSize: width, blockSize: height } = resizeObserverEntry.borderBoxSize[0];
let className;
if (width >= 1025) {
className = 'large';
} else if (width >= 769) {
className = 'medium';
} else if (width >= 0) {
className = 'small';
}
return (
<>
{/* this element's className changes based on its observed border-box width */}
{/* CAUTION - beware of creating a circular dependency by changing the observed sizes within your classnames! */}
<div className={className}>I'm being observed!</div>
</>
);
};useBreakpoints() abstracts the above implementation away for your convenience. You'll likely only need this hook if you need a property from ResizeObserverEntry which is not contentRect or one of the ResizeObserverBoxOptions.
useBreakpoints(). You may use this context with React.useContext() to access the information stored in the context provider, which is typically a ResizeObserverEntry set internally by <Observe>.
<Context.Provider value={ResizeObserverEntry}>parent.js
import { Context } from '@envato/react-breakpoints';
<Context.Provider value={myResizeObserverEntry}>
{/* children with access to `myResizeObserverEntry` */}
</Context.Provider>;child.js
import { useContext } from 'react';
import { Context } from '@envato/react-breakpoints';
const MyChildComponent = () => {
const myResizeObserverEntry = useContext(Context);
/* ... */
};child.js portion as above, you may want to use useResizeObserverEntry() instead.