npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-measured-dom

v1.1.1

Published

Width & height providers for React

Downloads

56

Readme

react-measured-dom

Build Status

Zero-dependency React bounding box providers and hooks for DOM elements.

Why react-measured-dom?

Sometimes you need access to size and/or position of a component DOM element, typically:

  • You are using a library such as react-window, react-virtualized or react-vtree and need numeric width and height in order to render your components
  • You are rendering responsive charts (or WebGL)
  • You are visually relating components that are unrelated in DOM
  • You are implementing a component that changes its behavior depending on its size and/or position
  • ...

In these cases you usually end up using some sort of stateful component logic based on the resize event of the window object. The problem with this solution is that your layout might change even though window dimensions haven't.

That is where react-measured-dom comes in!

In this guide

  • Installation
  • API Docs
    • Measured
      • Measured for DOM nodes
      • Measured as HOC
      • Measured with styled-components
      • Measured element props
    • useBoundingBox
      • useBoundingBox basic API
      • useBoundingBox transforms
    • BoudingBox

Quick example

import React from 'react';
import { Measured } from 'react-measured-dom';

const MyComponent: React.FC = () => (
  <Measured.div className='full-size'>
    {box => (
      {/* Let's say you are rendering a Chart */}
      <Chart width={box.width} height={box.height} />
    )}
  </Measured.div>
);

Check out the API docs to get started!

Installation

react-measured-dom is available as an NPM module (along with its TypeScript definitions):

# For npm users
npm install --save react-measured-dom

# For yarn users
yarn add react-measured-dom

API Documentation

Measured

import { Measured } from 'react-measured-dom';

Using predefined DOM elements

Probably the most interesting export that gives you instant access to all pre-wrapped HTML elements:

import React from 'react';
import { Measured } from 'react-measured-dom';

const MyComponent: React.FC = () => (
  <>
    <Measured.div />
    <Measured.span />
    <Measured.em />
    <Measured.aside />
    {/* See below for all supported elements */}
  </>
);

Measured exposes the following HTML elements:

a, abbr, address, article, aside, b, bdi, bdo, big, blockquote, body, button, canvas, caption, cite, code, dd, del, details, dfn, dialog, div, dl, dt, em, embed, fieldset, figcaption, figure, footer, form, h1, h2, h3, h4, h5, h6, head, header, hgroup, hr, i, iframe, img, input, ins, kbd, label, legend, li, main, map, mark, meter, nav, object, ol, output, p, picture, pre, progress, q, rp, rt, ruby, s, samp, section, select, small, span, strong, sub, summary, sup, table, tbody, td, textarea, tfoot, th, thead, time, tr, u, ul, var, video,

Using Measured as HOC

If these are not enough (for example you want to wrap an existing component for measurements) you can go the HOC way:

import React from 'react';
import Label from '../your/label/component';
import { Measured } from 'react-measured-dom';

const MeasuredLabel = Measured(Label);

const MyComponent: React.FC = () => (
  // MeasuredLabel component accepts all the original props from Label
  <MeasuredLabel someLabelProp="value">
    {box => {
      // Use the bounding box here
    }}
  </MeasuredLabel>
);

IMPORTANT The ref of the wrapped element must be a ref to a HTMLElement!

Using Measured with styled-components

Since react-measured-dom's only requirement is for the wrapped element to expose a ref containing an HTMLElement, it is possible to use it in conjunction with styled-components as well:

import React from 'react';
import styled from 'styled-components';
import { Measured } from 'react-measured-dom';

const StyledComponent = styled.div`
  ...
`;

const MeasuredStyledComponent = Measured(StyledComponent);

const MyComponent: React.FC = () => (
  <MeasuredStyledComponent>
    {box => {
      // Use the bounding box here
    }}
  </MeasuredStyledComponent>
);

Besides the props of the wrapped element, Measured accepts the following props:

children

children?: ReactNode | (box: BoundingBox) => ReactNode

Although Measured.div can accept ReactNode as children, the default use case will most probably look like:

const MyComponent = () => (
  <Measured.div>
    {boundingBox => (
      <Chart width={boundingBox.width} height={boundingBox.height}>
    )}
  </Measured.div>
)

In this case the children prop of Measured.div is a render function that receives a single parameter, boundingBox (see BoundingBox below).

onBoundingBoxChange

onBoundingBoxChange?: (props: BoundingBox | undefined) => void

In some cases you want to make the bounding box available to a parent component, then most probably store it in a component state somewhere. For this you can use the onBoundingBoxChange prop:

const MyComponent = () => {
  const [boundingBox, setBoundingBox] = useState<BoundingBox>();
  const className = boundingBox && boundingBox.top < 0 ? "highlighted" : null

  <div>
    <Measured.div onBoundingBoxChange={setBoundingBox}>
      ... Some children here, can also be a callback ...
    </Measured.div>

    <div className={className}>
      And the bounding box gets used somewhere else
    </div>
  </div>
}

This prop gets called everytime the bounding box changes. When the component unmounts it gets called with undefined.

positionOnly

positionOnly?: boolean

If you are not interested in the element dimensions and only want to rerender when the position changes you can pass truthy positionOnly:

const MyComponent = () => (
  <Measured.div positionOnly>
    {boundingBox => {
      // boundingBox will now have width & height set to NaN
    }}
  </Measured.div>
)

In this case the width and height of the boundingBox (in both children and onBoundingBoxChange callback) will be set to NaN.

sizeOnly

sizeOnly?: boolean

If you are not interested in the element position and only want to rerender when the position changes you can pass truthy sizeOnly:

const MyComponent = () => (
  <Measured.div sizeOnly>
    {boundingBox => {
      // boundingBox will now have top, right, bottom and left set to NaN
    }}
  </Measured.div>
)

In this case the top, right, bottom and left of the boundingBox (in both children and onBoundingBoxChange callback) will be set to NaN.

IMPORTANT For most of the use cases you want to turn sizeOnly on since you don't want to be rerendering your charts or lists whenever e.g. the user scrolls the page.

useBoundingBox

import { useBoundingBox } from 'react-measured-dom';

Sometimes you just want a more fine grained access to react-measured-dom capabilities. In those cases you can skip the HOC approach and measure a ref to an HTMLElement using useBoundingBox:

const MyComponent = () => {
  const ref = useRef<HTMLElement>();
  const boundingBox: BoundingBox | undefined = useBoundingBox(ref);

  // You can either use the boudingBox here or pass it around
  // just remember, on the first render it is going to be `undefined`

  return <div ref={ref}>
    {/* ... */}
  </div>
}

Basic API

useBoundingBox: (
  ref: React.RefObject<HTMLElement>,
  onChange?: (box: BoundingBox | undefined) => void,
)

Using transforms

Sometimes you might want to tweak the measured size/position values depending on external factors (e.g. to discard size or position in order to optimise rendering). For that you can pass an array of transforms applied to the original measured BoundingBox.

// For ease of use falsy values are also allowed
type CheckerTransform = (box: BoundingBox) => BoundingBox;

useBoundingBox: (
  ref: React.RefObject<HTMLElement>,
  onChange?: (box: BoundingBox | undefined) => void,
  transform?: CheckerTransform
) => BoundingBox

These will be applied left to right, the return value of one passed to the next.

BoundingBox

A data container for information about a bounding box. Looks like:

interface BoundingBox {
  readonly top: number;
  readonly right: number;
  readonly bottom: number;
  readonly left: number;
  readonly width: number;
  readonly height: number;
}

IMPORTANT top, right, bottom and left are all measured from the left top corner of the reference element, they do not act like e.g. CSS bottom or right values that are measured relative to the bottom edge of the container.

Issues

Please use the github issue tracker to submit any bugs or feature requests.