Resizable

Previous

API reference for @platejs/resizable.

@platejs/resizable provides the headless resize wrapper, resize handle primitive, shared resize stores, and length utilities used by Plate media components. It owns behavior; registry components own styling.

Installation

pnpm add @platejs/resizable
pnpm add @platejs/resizable

Ownership

SurfaceOwnerUse
Resizable@platejs/resizableWraps a Plate element, tracks width, clamps resize values, and writes the final width to the node.
ResizeHandle@platejs/resizablePrimitive handle that starts mouse/touch resizing and emits ResizeEvent values.
ResizableProvider@platejs/resizableStores the active width for the current resizable subtree.
ResizeHandleProvider@platejs/resizableShares the wrapper onResize callback with nested handles.
Resize hooks@platejs/resizableSplit state from DOM props for custom wrappers and handles.
Length utilities@platejs/resizableConvert and clamp static pixel widths and relative percent widths.

Media Pattern

Wrap each resizable element with ResizableProvider. Media components use the provider width for captions and use Resizable plus left/right handles around the media body.

Resizable media element
import type { TImageElement } from 'platejs';
import type { PlateElementProps } from 'platejs/react';
 
import {
  Resizable,
  ResizableProvider,
  ResizeHandle,
  useResizableValue,
} from '@platejs/resizable';
import { PlateElement, withHOC } from 'platejs/react';
 
export const ImageElement = withHOC(
  ResizableProvider,
  function ImageElement(props: PlateElementProps<TImageElement>) {
    const width = useResizableValue('width');
 
    return (
      <PlateElement {...props}>
        <figure contentEditable={false}>
          <Resizable
            options={{
              align: 'center',
              maxWidth: '100%',
              minWidth: 120,
            }}
          >
            <ResizeHandle options={{ direction: 'left' }} />
            <img alt="" src={props.element.url as string} />
            <ResizeHandle options={{ direction: 'right' }} />
          </Resizable>
 
          <figcaption style={{ width }}>Caption</figcaption>
        </figure>
 
        {props.children}
      </PlateElement>
    );
  }
);
Resizable media element
import type { TImageElement } from 'platejs';
import type { PlateElementProps } from 'platejs/react';
 
import {
  Resizable,
  ResizableProvider,
  ResizeHandle,
  useResizableValue,
} from '@platejs/resizable';
import { PlateElement, withHOC } from 'platejs/react';
 
export const ImageElement = withHOC(
  ResizableProvider,
  function ImageElement(props: PlateElementProps<TImageElement>) {
    const width = useResizableValue('width');
 
    return (
      <PlateElement {...props}>
        <figure contentEditable={false}>
          <Resizable
            options={{
              align: 'center',
              maxWidth: '100%',
              minWidth: 120,
            }}
          >
            <ResizeHandle options={{ direction: 'left' }} />
            <img alt="" src={props.element.url as string} />
            <ResizeHandle options={{ direction: 'right' }} />
          </Resizable>
 
          <figcaption style={{ width }}>Caption</figcaption>
        </figure>
 
        {props.children}
      </PlateElement>
    );
  }
);

The registry resize-handle component imports these primitives and adds the absolute positioning, hover affordance, and alignment classes.

Resizable Wrapper

Resizable composes useResizableState and useResizable. It renders a relative outer wrapper and a relative inner wrapper, then provides its resize callback to descendants through ResizeHandleProvider.

Options{ options: ResizableOptions } & React.HTMLAttributes<HTMLDivElement>

    Width constraints and alignment used by the resize calculation.

    Media body, resize handles, or custom controls.

OptionsResizableOptions

    Alignment used to calculate resize delta. Defaults to center.

    Maximum width. Defaults to 100%.

    Minimum width. Defaults to 92.

    Reserved in the options type. Resizable itself does not read this value.

ReturnsReturnType<typeof useResizableState>

    Alignment used by resize math.

    Maximum width passed to the wrapper style and clamp utility.

    Minimum width passed to the wrapper style and clamp utility.

    Writes the finished width to the current TResizableElement. If the width is unchanged, it selects the node.

    Updates the transient provider width while dragging.

    Current provider width. It is synced from element.width ?? '100%'.

Parameters

    State returned by useResizableState.

Returnsobject

    Converts a handle delta into a new width, clamps it, stores it while dragging, and writes it to the node when finished is true.

    Inner wrapper props with position: 'relative', width, minWidth, and maxWidth.

    Outer wrapper props with position: 'relative'.

    Measures the static wrapper width for percent-to-pixel conversion.

For centered elements, left and right handles double the delta so the element grows from both sides. Left handles invert the delta before clamping.

Resize Handles

ResizeHandle is a primitive div created with createPrimitiveComponent. It returns null in read-only mode because useResizeHandle sets hidden from useReadOnly().

OptionsResizeHandleProps

    Direction, initial size, and lifecycle callbacks for the handle.

    Precomputed state when composing your own handle pipeline.

OptionsResizeHandleOptions

    Resize edge. Defaults to left.

    Starting width or height. If omitted, the handle reads its parent element on pointer start.

    Called on mouse over and touch move.

    Called after hover ends or resizing finishes.

    Called after the hook records the starting pointer position and size.

    Resize callback. Defaults to the nearest ResizeHandleProvider value.

    Called after the hook records the starting touch position and size.

ReturnsReturnType<typeof useResizeHandleState>

    Direction, pointer state, horizontal/vertical mode, read-only state, setters, and callbacks consumed by useResizeHandle.

Parameters

    State returned by useResizeHandleState.

Returnsobject

    true while the editor is read-only.

    Mouse and touch handlers for starting resize, tracking hover, and finishing resize.

Resize handles listen on window while dragging. Mouse and touch move events emit finished: false; mouse up and touch end emit finished: true.

Stores

APIStateUse
ResizableProvider{ width: React.CSSProperties['width'] }Wrap a resizable node and expose the active width to captions or overlays.
useResizableValue('width')React.CSSProperties['width']Read the current width.
useResizableSet('width')setterSet the current width.
useResizableStore / resizableStoreatom storeAdvanced access to the resizable store.
ResizeHandleProvider{ onResize: (event: ResizeEvent) => void }Provides the wrapper resize callback to nested handles.
useResizeHandleValue('onResize')callbackRead the current resize callback.
useResizeHandleSet('onResize')setterReplace the current resize callback.
useResizeHandleStoreatom storeAdvanced access to the handle store.

Types

TypeValue
ResizeDirection'bottom' | 'left' | 'right' | 'top'
ResizeLengthnumber | string
ResizeLengthStaticnumber
ResizeLengthRelativestring
ResizeEvent{ delta: number; direction: ResizeDirection; finished: boolean; initialSize: number }

Length Utilities

Methods

    Clamps a pixel length to pixel min/max values.

    Converts length and constraints to pixels, clamps the value, then returns the same length kind as the input.

    Converts a pixel length to a percent string. Percent strings pass through unchanged.

    Converts a percent string to pixels. Numbers pass through unchanged.

    Narrows pointer events by checking for touches.

resizeLengthToStatic parses strings as percentages. Use numeric pixel lengths when the source value is not a percentage.

  • Media covers the image, video, audio, and embed elements that consume the resizable primitives.
  • Resize Handle covers the styled registry wrapper.