CSS resize: let users resize a box

Two CSS lines put a drag handle in the corner of any box: resize to choose the direction, and overflow so the property takes effect. No JavaScript is needed.

The CSS resize property lets the user resize a box by dragging a handle in its corner. Set resize: both (or horizontal or vertical) and give the element an overflow value other than visible. That is the whole recipe.

.box {
  resize: both;
  overflow: auto;
}

Try it. Drag the bottom-right corner of the blue box with a mouse.

Live exampletry it here, then copy the code
Share it as a link
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>CSS resize: both</title>
<style>
  body { margin: 0; padding: 20px; font-family: system-ui, sans-serif; background: #f4f5f7; }
  .box {
    resize: both;     /* show a handle in the corner, drag in both directions */
    overflow: auto;   /* required: resize does nothing while overflow is visible */
    width: 240px; height: 140px;
    max-width: 100%;  /* never wider than the page */
    min-width: 120px; min-height: 60px;
    box-sizing: border-box;
    padding: 14px 16px; border-radius: 10px;
    background: #fff; border: 2px solid #2563eb;
  }
  code { font: 13px ui-monospace, Consolas, monospace; }
  #out { margin-top: 12px; color: #374151; font-size: 14px; }
</style>
</head>
<body>
<div class="box" id="box"><b>Drag my bottom-right corner.</b><br>The browser draws the handle. No JavaScript moves this box.</div>
<p id="out">style="<code id="attr"></code>"</p>

<script>
  // Only to show what the browser writes: the resize itself needs no script.
  const box = document.getElementById('box');
  const attr = document.getElementById('attr');
  new ResizeObserver(() => { attr.textContent = box.getAttribute('style') || ''; }).observe(box);
</script>
</body>
</html>
resize: both on a div. The line under the box shows what the browser writes into its style attribute as you drag.

The script in that example only prints the style attribute. The browser draws the handle and does the resizing on its own.

The overflow rule

resize does nothing on a plain <div>. The spec says the property applies to elements that are scroll containers. A div starts with overflow: visible, so it is not one.

The same resize line, with and without an overflow value that makes a scroll container.
The same resize line, with and without an overflow value that makes a scroll container.

MDN lists the cases where resize does not apply:

  • inline elements, such as a <span>
  • block elements whose overflow is visible or clip

overflow: auto is usually the right pick. It shows scrollbars only when the content no longer fits. hidden also works, but content that no longer fits is cut off with no scrollbar.

The six values

The property takes one keyword. The initial value is none, and the property is not inherited, so it has to be set on each box you want to resize.

Value What the user can change
none Nothing. No handle is shown
both Width and height
horizontal Width only
vertical Height only
block Size along the block axis
inline Size along the inline axis

block and inline follow the text direction. In horizontal text, inline means width and block means height. In vertical writing modes they swap.

Live exampletry it here, then copy the code
Share it as a link
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>CSS resize values</title>
<style>
  body { margin: 0; padding: 16px; font-family: system-ui, sans-serif; background: #f4f5f7; }
  .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 12px; }
  .box {
    width: 150px; height: 80px; max-width: 100%;
    box-sizing: border-box; padding: 10px; border-radius: 8px;
    background: #fff; border: 2px solid #2563eb;
    font: 13px/1.4 ui-monospace, Consolas, monospace;
  }
  .scroll { overflow: auto; }            /* the four boxes that can resize */
  .none { resize: none; }
  .horizontal { resize: horizontal; }
  .vertical { resize: vertical; }
  .both { resize: both; }
  .visible { resize: both; overflow: visible; border-color: #c2410c; }  /* no handle */
  .inline { resize: inline; overflow: auto; }  /* width, in left-to-right text */
</style>
</head>
<body>
<div class="grid">
  <div class="box scroll none">resize: none</div>
  <div class="box scroll horizontal">resize: horizontal</div>
  <div class="box scroll vertical">resize: vertical</div>
  <div class="box scroll both">resize: both</div>
  <div class="box inline">resize: inline</div>
  <div class="box visible">resize: both<br>overflow: visible</div>
</div>
</body>
</html>
Drag each corner. The orange box has resize: both but overflow: visible, so it has no handle.

MDN's compatibility data lists block and inline in Chrome, Edge, Firefox and desktop Safari. In the Chromium I tested with, inline changed only the width, as expected for horizontal text.

What the browser writes when you drag

A resize is not stored in some hidden place. The spec says the browser sets width and height in the element's own style attribute, in px. If the user resizes in one direction only, only that one property is set.

The stylesheet size is the starting point. After a drag, px values in the style attribute take over.
The stylesheet size is the starting point. After a drag, px values in the style attribute take over.

Three things follow from this:

  • A width in your stylesheet no longer applies after a drag. The style attribute replaces it, unless the stylesheet rule uses !important.
  • Changing resize back to none later does not undo the size. The spec says so directly.
  • To reset the box, clear the inline values in script.
box.style.width = '';
box.style.height = '';

The size is also not saved anywhere. Reload the page and the box is back to its stylesheet size.

Setting limits with min and max

Without limits, a user can drag a box past the edge of its card, or down to a sliver. The spec says the browser must let the user resize with no other constraints than min-width, max-width, min-height and max-height.

The pointer can keep going. The box stops at max-width.
The pointer can keep going. The box stops at max-width.

So those four properties are the controls. A few pairings that work well:

  • max-width: 100% keeps a box inside its parent.
  • A min-height stops the box collapsing to nothing.
  • A percentage max-width on a sidebar leaves room for the main column.

In my test, the boxes in the examples shrank below their starting size, down to their min-width and min-height, and never past them. css min-width explains how those limits interact with width.

A finished example: a resizable sidebar and notes panel

The same two lines build a small app layout. The sidebar uses resize: horizontal, and the notes panel uses resize: vertical.

Live exampletry it here, then copy the code
Share it as a link
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Resizable sidebar and notes panel</title>
<style>
  body { margin: 0; padding: 12px; font-family: system-ui, sans-serif; background: #f4f5f7; }
  * { box-sizing: border-box; }  /* sizes include padding */
  .app { display: flex; gap: 10px; min-height: 300px; }
  .side {
    resize: horizontal;   /* drag the corner left or right */
    overflow: auto;
    width: 160px;
    min-width: 90px;      /* the user cannot drag past these */
    max-width: 60%;
    flex-shrink: 0;       /* keep the width the user chose */
    background: #1e293b; color: #e2e8f0; border-radius: 10px; padding: 12px;
  }
  .side a { display: block; color: inherit; text-decoration: none; padding: 6px 0; }
  .main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 10px; }
  .card { background: #fff; border-radius: 10px; padding: 12px; }
  .notes {
    resize: vertical;     /* only the height changes */
    overflow: auto;
    height: 90px; min-height: 50px; max-height: 200px;
    flex-shrink: 0;       /* keep the height the user chose */
    border: 1px solid #cbd5e1;
  }
  button { font: inherit; padding: 6px 12px; border-radius: 8px; border: 1px solid #94a3b8; background: #fff; cursor: pointer; align-self: flex-start; }
</style>
</head>
<body>
<div class="app">
  <nav class="side" id="side">
    <b>Pages</b>
    <a href="#">Overview</a><a href="#">Reports</a><a href="#">Settings</a>
  </nav>
  <div class="main">
    <div class="card">Drag the sidebar's bottom-right corner to change its width.</div>
    <div class="card notes" id="notes">Notes panel. Drag its bottom-right corner to make it taller, up to 200px.</div>
    <button id="reset">Reset sizes</button>
  </div>
</div>

<script>
  // Resizing writes width/height into the style attribute.
  // Clearing them returns each panel to its stylesheet size.
  document.getElementById('reset').addEventListener('click', () => {
    document.getElementById('side').style.width = '';
    document.getElementById('notes').style.height = '';
  });
</script>
</body>
</html>
Drag the sidebar corner to change its width, and the notes corner to change its height. Reset clears the sizes.

Three details make it behave:

  1. flex-shrink: 0 on both panels. Without it, flexbox can squeeze a panel smaller than the size the user chose.
  2. box-sizing: border-box, so min-width and max-width include the padding and match what you see.
  3. A reset button that clears style.width and style.height, because that is where the user's sizes live.

If other code needs to react when the user resizes, such as redrawing a chart, use a ResizeObserver. The first example uses one to print the style attribute.

Textareas are already resizable

MDN notes that in many browsers, a <textarea> is resizable by default. In the Chromium I tested with, a textarea's computed resize value is both. That is why the corner handle appears without any CSS.

textarea { resize: vertical; }  /* taller only */
textarea { resize: none; }      /* no handle */

The HTML textarea guide covers rows, cols and auto height. For a textarea that grows as you type, field-sizing is another approach.

Browser support

MDN's compatibility data lists resize in Chrome, Edge, Firefox and Safari on desktop, and in Chrome on Android. For Safari on iOS, it says the property is recognized but has no effect.

So on an iPhone the box stays at its stylesheet size. If people on phones must be able to change the size, give them another control too, such as buttons.

When it does not work

What you see Cause Fix
No handle on a div overflow is visible overflow: auto
No handle, overflow is set overflow: clip Use auto or hidden
No handle on a span Inline elements are not resizable display: block or inline-block, plus overflow
Box can be dragged off the page No upper limit max-width: 100%
Box shrinks to a sliver No lower limit min-width and min-height
Panel ignores the chosen size in a flex row Flexbox shrinks it flex-shrink: 0
Drag works, but the size snaps back An !important width or height in CSS Remove !important
No handle on an iPhone Safari on iOS has no effect for resize Offer buttons as well

A resizable layout is easier to show than to describe. A screenshot cannot be dragged, and the person you send it to wants to try the handle themselves.

Paste the page into a NOS document and choose Create share link. HTML to link walks through it.

The page renders as written and its scripts run, so anyone with the link can drag the corners. If you change the code later, the same link shows the new version.

Questions people ask

Why does resize: both do nothing on my div?

The resize property only applies to scroll containers. A div has overflow: visible by default, so it is not one. Add overflow: auto (or hidden or scroll) and the handle appears.

How do I stop a textarea from being resized?

Use textarea { resize: none; }. To keep the handle but allow only a taller box, use resize: vertical instead. The textarea guide covers the other textarea settings.

Can I limit how big or small the user can make the box?

Yes. Set min-width, max-width, min-height and max-height. The spec says these are the only limits the browser applies to a user resize, so they are the tool for the job.

Does the resize property work on phones?

MDN's compatibility data lists support in Chrome on Android. For Safari on iOS it says the property is recognized but has no effect, so iPhone users cannot resize the box by dragging.

How do I undo a resize the user made?

The browser stores the new size in the element's style attribute. Clear it with el.style.width = '' and el.style.height = '', and the stylesheet size comes back.

Keep reading