# How to resize HTML elements: with few lines of CSS, using a React hook, or with pure JavaScript

How to resize DOM elements super easily with pure CSS, or in a fancier way using
React lib.

You know how textareas can be resized easily by dragging from the corner? This
same behavior can be activated for any type of element.

For example, the below demo is showing
[the classic Holy Grail layout](<https://en.wikipedia.org/wiki/Holy_grail_(web_design)>).
When rendering it, I think it’s important to see the fluidity of the layout,
which makes a good use-case for resizable content:

[Interactive example](/resize-any-element)

Here’s how that component looks like (nothing too surprising there):

```jsx
const HolyGrail = () => (
  <Wrap>
    <Header>Resizable wrapper</Header>
    <SidebarLeft>Sidebar content</SidebarLeft>
    <Content>
      <p>Choose the resizing behavior:</p>
    </Content>
    <SidebarRight>Sidebar content</SidebarRight>
    <Footer>Holy Grail</Footer>
  </Wrap>
)
```

When using it, wrap it to a `ResizableContainer` component to make it resizable:

```jsx
<ResizableContainer>
  <HolyGrail />
</ResizableContainer>
```

That’s pretty simple, check the CSS below.

## The code: a few lines of CSS

Here’s the CSS for the resizable container:

```css
.resizable-container {
  height: 400px;
  overflow: auto;
  resize: both;
}
```

The `height` is not necessarily needed, I just like it since it’s a wrapper,
then I blast the inner component 100% height. **The `overflow` should be
anything else than `visible`**.

Possible values for `resize` are:

`none`

Resizing is disabled.

`both`

Both axis, Y and X, are subjected to resizing.

`horizontal`

Size can only be changed horizontally (X axis ↔︎).

`vertical`

Only vertical sizing is possible (Y axis ↕︎).

`block` experimental

The element displays a mechanism for allowing the user to resize it in the
    block direction (either horizontally or vertically, depending on the
    writing-mode and direction value).

`inline` experimental

The element displays a mechanism for allowing the user to resize it in the
    inline direction (either horizontally or vertically, depending on the
    writing-mode and direction value).

### About the block and inline values

If I understand right, the `block` and `inline` values are just a different ways
of saying horizontal and vertical, because some languages (like Japanese) are
written from top to bottom. If my computer would be in Japanese, setting
`resize` to `block` would actually be the same as horizontal, whereas in my
English computer it means vertical.

### Disabling textarea resizing

By setting `resize: none` you can easily disable textarea sizing:

Which I think is the only HTML element that has resizing on by default.

### Browser support for resize

This is an old feature, I’ve just learned about it recently heh. The `block` and
`inline` modes only work in FireFox for now (2021).

## Resizable Iframe

Iframes can be made resizable like any other element. This comes in handy when
displaying demos and such.

[iframe](https://clubmate.fi)

I wrapped the Iframe to a `div`, and set the resizing to the outer element, then
set the Iframe width and height to 100%:

```html
<div style="resize: both; overflow: auto; height: 300px;">
  <iframe
    src="https://clubmate.fi"
    style="width: 100%; height: 100%; border: 1px solid #aaa"
  ></iframe>
</div>
```

## Resizable React component

The only benefit of making something resizable with JavaScript: have more
control.

Below is a component made using a simple resize hook I wrote. Compared to the
The CSS implementation has a few advantages:

- It gives you the size of the box.
- You can define a sizing step, in the pink example below it’s 40px.
- The look of the handle can be controlled. I’m using a
  [CSS triangle](/different-trapezoid-shapes-with-css).
- The transition between the steps can be animated.

[Interactive example](/resize-any-element)

There’s **3 parts** to the code that renders that box.

### Usage of the component

I build it as a render prop component, so the size is returned from the
function. The usage of it looks like this:

```jsx
<ResizableComponent options={{ step: 40 }}>
  {size => (
    <span>
      I’m resizable, my size is {size.width} x {size.height}
    </span>
  )}
</ResizableComponent>
```

### The component itself

At the heart of it, is the `useResize` hook (below), which doesn’t render
anything on its own, but merely provides the logic. You feed the hook a `ref` to
the element you want to resize, and the optional options. Building a hook,
rather than a component makes it super flexible, you’re not bound to the
limitations of the component, but can make your own.

The resizing hook gives back 3 things: `initResize`, `size`, and `cursor` (more
on those [below](#the-resize-hook)):

```jsx
import React from 'react'
import PropTypes from 'prop-types'
import { useResize } from './useResize.ts'
import { Handle, Resizable } from './styles.tsx'

const ResizableComponent = props => {
  const ref = React.useRef()
  const { initResize, size, cursor } = useResize(ref, props.options)

  return (
    <Resizable ref={ref}>
      {props.children(size)}
      <Handle cursor={cursor} onMouseDown={initResize} />
    </Resizable>
  )
}
```

### The resize hook

This hook is pretty basic. You could easily add more features if needed, like
keyboard support, Max/min dimensions and so on. But there’s already many React
helpers for resizing components, building this hook was more of an exercise.

See a demo in CodeSandbox:

[![Edit vibrant-lamarr-uv54l](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/vibrant-lamarr-uv54l?fontsize=14&hidenavigation=1&theme=dark)

#### Input params

ref

A ref to the resized component. Use `React.useRef()`

options.step
  A px value, i.e.: 40. The box will be resized with this interval.
  options.axis

`both|horizontal|vertical`. Both is the default.

#### Return value

An the resize hook will return these three things:

initResize

A function, which you should call with an `onMouseDown` in your
    resizing handle.

size
  This is the live size of the resized box.
  cursor

This depends on the sizing axis passed in `options.axis`. Then
    you can pass this along into the your resizing handle.

```jsx
import React from 'react'

const cursor = {
  both: 'nwse-resize',
  vertical: 'ns-resize',
  horizontal: 'ew-resize',
}

export const useResize = (ref, options) => {
  ref = ref || {}
  const { step = 1, axis = 'both' } = options || {}
  const [coords, setCoords] = React.useState({ x: Infinity, y: Infinity })
  const [dims, setDims] = React.useState({ width: Infinity, height: Infinity })
  const [size, setSize] = React.useState({ width: Infinity, height: Infinity })

  const initResize = event => {
    if (!ref.current) return
    setCoords({ x: event.clientX, y: event.clientY })
    const { width, height } = window.getComputedStyle(ref.current)
    setDims({ width: parseInt(width, 10), height: parseInt(height, 10) })
  }

  React.useEffect(() => {
    // Round the size based to `props.step`.
    const getValue = input => Math.ceil(input / step) * step

    const doDrag = event => {
      if (!ref.current) return

      // Calculate the box size.
      const width = getValue(dims.width + event.clientX - coords.x)
      const height = getValue(dims.height + event.clientY - coords.y)

      // Set the box size.
      if (axis === 'both') {
        ref.current.style.width = width + 'px'
        ref.current.style.height = height + 'px'
      }
      if (axis === 'horizontal') ref.current.style.width = width + 'px'
      if (axis === 'vertical') ref.current.style.height = width + 'px'
      setSize({ width, height })
    }

    const stopDrag = () => {
      document.removeEventListener('mousemove', doDrag, false)
      document.removeEventListener('mouseup', stopDrag, false)
    }

    document.addEventListener('mousemove', doDrag, false)
    document.addEventListener('mouseup', stopDrag, false)
  }, [dims, coords, step, ref, axis])

  return { initResize, size, cursor: cursor[axis] }
}
```

## Make element resizable with vanilla JavaScript

Here’s pure JavaScript solution,
[based heavily to this StackOverflow answer](https://stackoverflow.com/a/8960307/976972).

See a demo:

[![Edit resizable-box](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/resizable-box-0md86?fontsize=14&hidenavigation=1&theme=dark)

Usage:

```html
<div id="box" class="box">Click the element to make it resizable.</div>
```

The script:

```js
const el = document.getElementById('box')

el.addEventListener('click', init, false)

const coords = { x: Infinity, y: Infinity }
let startWidth, startHeight

function init() {
  el.removeEventListener('click', init, false)
  el.classList.add('resizable')
  makeTheHandle()
}

function makeTheHandle() {
  const handle = document.createElement('div')
  handle.classList.add('handle')
  el.appendChild(handle)
  handle.addEventListener('mousedown', initDrag, false)
}

function initDrag(event) {
  coords.x = event.clientX
  coords.y = event.clientY
  startWidth = parseInt(window.getComputedStyle(el).width, 10)
  startHeight = parseInt(window.getComputedStyle(el).height, 10)
  document.addEventListener('mousemove', doDrag, false)
  document.addEventListener('mouseup', stopDrag, false)
}

function doDrag(event) {
  el.style.width = startWidth + event.clientX - coords.x + 'px'
  el.style.height = startHeight + event.clientY - coords.y + 'px'
}

function stopDrag() {
  document.removeEventListener('mousemove', doDrag, false)
  document.removeEventListener('mouseup', stopDrag, false)
}
```

And the styles:

```css
.box {
  background-color: Pink;
  padding: 20px;
  position: relative;
}

.handle {
  background-color: Black;
  bottom: 0;
  cursor: se-resize;
  height: 10px;
  position: absolute;
  right: 0;
  width: 10px;
}
```

## Conclusions

I would use the native CSS solution whenever possible, and then fallback to a JS
solution if I have design or accessibility needs that are not met by the native
`resize`.

There are also resizing sensors that detect resizing and return the dimensions.
Here are the first few I found with a quick search:

- https://www.npmjs.com/package/react-element-resize
- https://www.npmjs.com/package/react-resize-detector

Thanks for reading. Hope this was helpful.
