Resizable

free

Resizable lets users drag a surface to adjust working space without leaving the page.

Sidebar
Content
import Resizable, {
  type ResizeHandleConfig,
} from '@/components/ui/resizable';

Installation

Add this component with the NateUI CLI.

npx nateui@latest add Resizable

Examples

Default Handles

import Resizable from '@/components/ui/resizable';

const contentClassName = [
  'grid h-full place-items-center rounded-control border',

Handle Set

Only the configured edge and corner handles are rendered.
import Resizable, {
  type ResizeHandleConfig,
} from '@/components/ui/resizable';

Custom Handles

Custom resize points

Custom content replaces the visible square while the hitbox remains.

import Resizable, {
  type ResizeHandleConfig,
} from '@/components/ui/resizable';

Grouped

Sidebar
Editor
Content
import Resizable, {
  type ResizeHandleConfig,
} from '@/components/ui/resizable';

Controlled Size

Controlled panel
310px x 176px
import { useState } from 'react';
import Button from '@/components/ui/button';
import Resizable, {
  type ResizeCallback,

Grid

Grid snapped

Size changes land on a 24px rhythm.

import Resizable from '@/components/ui/resizable';

const contentClassName = [
  'flex h-full flex-col justify-between rounded-control border',

Bounds And Ratio

Aspect ratio stays locked inside the parent bounds.
import Resizable from '@/components/ui/resizable';

const parentClassName = [
  'relative h-72 w-full max-w-3xl overflow-hidden',

API

Resizable renders a positioned element with draggable handles. It can manage its own size from defaultSize, or accept a controlled size object and report changes through resize callbacks.

PropDescriptionTypeDefault
asElementElement or component used for the resizable root.ElementType'div'
boundsLimits resizing to the parent, window, or a specific element.'parent' | 'window' | HTMLElement-
boundsByDirectionApplies directional bounds from the side being dragged.booleanfalse
childrenContent rendered inside the resizable surface.ReactNode-
classNameClass names applied to the root element.string-
classNamesSlot class names for the root, wrapper, and each handle.ResizableClassNames-
defaultSizeInitial uncontrolled size.Size{ width: 'auto', height: 'auto' }
disabledPrevents handles from starting a resize interaction.booleanfalse
handlesCanonical handle configuration. Omit it for all default handles, pass false for no handles, or pass an explicit position list.false | ResizeHandleConfig[]all directions
enableLegacy direction map used when handles is not provided.Enable | falseall directions
gridSnaps resize movement to horizontal and vertical steps.[number, number][1, 1]
gridGapGap offset used when snapping to grid.[number, number][0, 0]
handleClassesLegacy alias for per-handle class names.HandleClassNames-
handleClassNamesClass names applied to individual handle hit areas.HandleClassNames-
handleComponentLegacy alias for per-handle custom content.HandleComponents-
handleComponentsLegacy custom content map used when handles is not provided.HandleComponents-
handleStylesInline styles applied to individual handle hit areas.HandleStyles-
handleWrapperClassLegacy alias for the handle wrapper class name.string-
handleWrapperClassNameClass names applied to the handle wrapper.string-
handleWrapperStyleInline styles applied to the handle wrapper.CSSProperties-
lockAspectRatioPreserves the current ratio, or uses a provided ratio number.boolean | numberfalse
lockAspectRatioExtraHeightExtra height included while preserving ratio.number0
lockAspectRatioExtraWidthExtra width included while preserving ratio.number0
maxHeightMaximum height as pixels or a CSS size.SizeValue-
maxWidthMaximum width as pixels or a CSS size.SizeValue-
minHeightMinimum height as pixels or a CSS size.SizeValue-
minWidthMinimum width as pixels or a CSS size.SizeValue-
onResizeCalled while a handle is dragged.ResizeCallback-
onResizeStartCalled before resizing starts; return false to cancel.ResizeStartCallback-
onResizeStopCalled when pointer interaction ends.ResizeCallback-
refRef forwarded to the root element.Ref<HTMLElement>-
resizeRatioMultiplier for pointer movement on each axis.number | [number, number]1
scalePointer scale compensation for transformed parents.number1
sizeControlled size.Size-
snapSnap points for width and height.{ x?: number[]; y?: number[] }-
snapGapMaximum distance from a snap point before snapping applies.number0
styleInline styles applied to the root element.CSSProperties-
...nativePropsNative props for the rendered root element.ComponentPropsWithoutRef<E>-

Types

type ResizeDirection =
  | 'top'
  | 'right'
  | 'bottom'
  | 'left'
  | 'topRight'
  | 'bottomRight'
  | 'bottomLeft'
  | 'topLeft';
 
type ResizeHandleConfig = {
  position: ResizeDirection;
  handler?: false | ReactNode;
};
 
type Enable = Partial<Record<ResizeDirection, boolean>>;
 
type SizeValue = string | number;
 
type Size = {
  width?: SizeValue;
  height?: SizeValue;
};
 
type NumberSize = {
  width: number;
  height: number;
};
 
type ResizeCallback = (
  event: PointerEvent,
  direction: ResizeDirection,
  elementRef: HTMLElement,
  delta: NumberSize,
) => void;
 
type ResizeStartCallback = (
  event: ReactPointerEvent<HTMLElement>,
  direction: ResizeDirection,
  elementRef: HTMLElement,
) => void | boolean;
 
type HandleClassNames = Partial<Record<ResizeDirection, string>>;
type HandleComponents = Partial<Record<ResizeDirection, ReactNode>>;
type HandleStyles = Partial<Record<ResizeDirection, CSSProperties>>;
 
type ResizableClassNames = Partial<
  Record<'root' | 'handleWrapper' | ResizeDirection, string>
>;