> ## Documentation Index
> Fetch the complete documentation index at: https://docs.twenty.com/llms.txt
> Use this file to discover all available pages before exploring further.

# ResizeHandle

> Resize a panel with pointer dragging or keyboard controls.

export const StoryEmbed = ({storyId, title, height = 240}) => <>
    <Tabs>
      <Tab title="Light">
        <iframe title={`${title} (light)`} src={`https://storybook.twenty.com/iframe.html?id=${storyId}&viewMode=story&globals=colorScheme:light`} width="100%" height={height} loading="lazy" style={{
  border: 0
}} />
      </Tab>
      <Tab title="Dark">
        <iframe title={`${title} (dark)`} src={`https://storybook.twenty.com/iframe.html?id=${storyId}&viewMode=story&globals=colorScheme:dark`} width="100%" height={height} loading="lazy" style={{
  border: 0
}} />
      </Tab>
    </Tabs>
    <a href={`https://storybook.twenty.com/?path=/story/${storyId}`}>
      Open in Storybook
    </a>
  </>;

`ResizeHandle` manages a numeric size and exposes a focusable separator. The application applies that size to the panel; the handle does not resize another element automatically.

<StoryEmbed storyId="ui-layout-resizehandle--resizable" title="ResizeHandle example" />

## Usage

Use `axis="y"` for height or `axis="x"` for width. Give the handle a specific accessible name and use `aria-controls` to identify the panel.

## Uncontrolled state

```tsx theme={null}
import { useId, useState } from 'react';
import { ResizeHandle } from 'twenty-ui/primitives/layout';
import { Text } from 'twenty-ui/primitives/typography';

export const ResizableNotes = () => {
  const panelId = useId();
  const [height, setHeight] = useState(150);

  return (
    <>
      <Text id={panelId} style={{ height, overflow: 'auto' }}>
        Record notes
      </Text>
      <ResizeHandle
        axis="y"
        defaultValue={150}
        onValueChange={setHeight}
        min={50}
        max={500}
        aria-label="Resize notes height"
        aria-controls={panelId}
      />
    </>
  );
};
```

The handle owns its value. `onValueChange` copies the latest size to the panel layout without controlling the handle. `defaultValue` only sets the initial size.

## Controlled state

```tsx theme={null}
import { useId, useState } from 'react';
import { ResizeHandle } from 'twenty-ui/primitives/layout';
import { Text } from 'twenty-ui/primitives/typography';

export const ResizableNotes = () => {
  const panelId = useId();
  const [height, setHeight] = useState(150);

  return (
    <>
      <Text id={panelId} style={{ height, overflow: 'auto' }}>
        Record notes
      </Text>
      <ResizeHandle
        axis="y"
        value={height}
        onValueChange={setHeight}
        min={50}
        max={500}
        aria-label="Resize notes height"
        aria-controls={panelId}
      />
    </>
  );
};
```

The application owns the value and can update it from other controls. Keep `value` and `defaultValue` separate and retain the same ownership mode throughout the component's lifetime.

## Keyboard and bounds

For `axis="y"`, Down increases the size and Up decreases it. For `axis="x"`, Right increases and Left decreases in left-to-right content; the directions reverse under [TextDirectionProvider](/ui/primitives/layout/text-direction-provider) with `direction="rtl"`.

Arrow keys use `step`, which defaults to `10`. Home moves to `min`, and End moves to `max`. Pointer dragging uses the pixel movement and is not snapped to `step`. Both interactions stay within the bounds. `disabled` prevents resizing and removes the handle from the tab order.

The separator orientation describes the dividing edge: horizontal for height resizing and vertical for width resizing.

## Custom handle

`children` replaces the visible grip; it does not replace the separator behavior. `render` can replace the root element. Custom wrappers must [forward the supplied props and ref](/ui/primitives/overview#custom-components) so focus, pointer capture, keyboard handlers, and accessibility attributes reach the same element.

## Props

<ParamField body="axis" type="&#x22;x&#x22; | &#x22;y&#x22;" default="y">
  Pointer movement axis: `y` for height or `x` for width.
</ParamField>

<ParamField body="defaultValue" type="number | (readonly string[] & number)" default="150">
  Initial size when the handle owns its state.
</ParamField>

<ParamField body="disabled" type="boolean" default="false">
  Disables resizing and removes the handle from the tab order.
</ParamField>

<ParamField body="max" type="number" default="500">
  Maximum allowed size.
</ParamField>

<ParamField body="min" type="number" default="50">
  Minimum allowed size.
</ParamField>

<ParamField body="onValueChange" type="((value: number) => void)">
  Called with the bounded size after pointer or keyboard interaction.
</ParamField>

<ParamField body="render" type="ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>">
  Allows you to replace the component's HTML element
  with a different tag, or compose it with another component.

  Accepts a `ReactElement` or a function that returns the element to render.
</ParamField>

<ParamField body="step" type="number" default="10">
  Keyboard arrow increment. Pointer movement uses the pixel delta.
</ParamField>

<ParamField body="value" type="number">
  Controlled size. Apply the value to the element being resized.
</ParamField>
