NovaUI
Components

Resizable

Draggable resizable panel groups for building adjustable split-view layouts.

The Resizable component provides a panel group system for building split-view layouts with draggable handles. Built on react-native-gesture-handler and react-native-reanimated, it supports horizontal and vertical directions, percentage-based sizing, and an optional grip handle indicator.

Preview

Panel One
Panel Two

Installation

npx novaui-cli add resizable

Import

import {
  ResizablePanelGroup,
  ResizablePanel,
  ResizableHandle,
} from 'novaui-components'

Basic Usage

The Resizable is a compound component with these parts:

  • ResizablePanelGroup — Root container with flex-1 w-full h-full overflow-hidden and direction-based flex (flex-row or flex-col)
  • ResizablePanel — Individual panel with overflow-hidden and percentage-based width/height
  • ResizableHandle — Draggable divider with relative flex items-center justify-center z-10 bg-transparent and an optional grip icon
Panel One
Panel Two
import { View, Text } from 'react-native'
import {
  ResizablePanelGroup,
  ResizablePanel,
  ResizableHandle,
} from 'novaui-components'

export function BasicResizable() {
  return (
    <ResizablePanelGroup
      direction="horizontal"
      className="h-48 w-full rounded-lg border border-border"
    >
      <ResizablePanel defaultSize={50}>
        <View className="flex-1 items-center justify-center">
          <Text className="text-sm font-medium text-foreground">Panel One</Text>
        </View>
      </ResizablePanel>
      <ResizableHandle withHandle />
      <ResizablePanel defaultSize={50}>
        <View className="flex-1 items-center justify-center">
          <Text className="text-sm font-medium text-foreground">Panel Two</Text>
        </View>
      </ResizablePanel>
    </ResizablePanelGroup>
  )
}

Vertical Direction

Set direction="vertical" for a top-and-bottom split layout:

Top Panel
Bottom Panel
import { View, Text } from 'react-native'
import {
  ResizablePanelGroup,
  ResizablePanel,
  ResizableHandle,
} from 'novaui-components'

export function VerticalResizable() {
  return (
    <ResizablePanelGroup
      direction="vertical"
      className="h-64 w-full rounded-lg border border-border"
    >
      <ResizablePanel defaultSize={40}>
        <View className="flex-1 items-center justify-center">
          <Text className="text-sm font-medium text-foreground">Top Panel</Text>
        </View>
      </ResizablePanel>
      <ResizableHandle withHandle />
      <ResizablePanel defaultSize={60}>
        <View className="flex-1 items-center justify-center">
          <Text className="text-sm font-medium text-foreground">Bottom Panel</Text>
        </View>
      </ResizablePanel>
    </ResizablePanelGroup>
  )
}

Three-Panel Layout

Create multi-panel layouts by adding more panels and handles:

Sidebar
Content
Details
import { View, Text } from 'react-native'
import {
  ResizablePanelGroup,
  ResizablePanel,
  ResizableHandle,
} from 'novaui-components'

export function ThreePanelLayout() {
  return (
    <ResizablePanelGroup
      direction="horizontal"
      className="h-48 w-full rounded-lg border border-border"
    >
      <ResizablePanel defaultSize={25}>
        <View className="flex-1 items-center justify-center">
          <Text className="text-sm font-medium text-foreground">Sidebar</Text>
        </View>
      </ResizablePanel>
      <ResizableHandle />
      <ResizablePanel defaultSize={50}>
        <View className="flex-1 items-center justify-center">
          <Text className="text-sm font-medium text-foreground">Content</Text>
        </View>
      </ResizablePanel>
      <ResizableHandle />
      <ResizablePanel defaultSize={25}>
        <View className="flex-1 items-center justify-center">
          <Text className="text-sm font-medium text-foreground">Details</Text>
        </View>
      </ResizablePanel>
    </ResizablePanelGroup>
  )
}

Without Handle Grip

Omit the withHandle prop for a minimal divider line:

Left
Right
<ResizablePanelGroup
  direction="horizontal"
  className="h-48 w-full rounded-lg border border-border"
>
  <ResizablePanel defaultSize={50}>
    <View className="flex-1 items-center justify-center">
      <Text className="text-sm font-medium text-foreground">Left</Text>
    </View>
  </ResizablePanel>
  <ResizableHandle />
  <ResizablePanel defaultSize={50}>
    <View className="flex-1 items-center justify-center">
      <Text className="text-sm font-medium text-foreground">Right</Text>
    </View>
  </ResizablePanel>
</ResizablePanelGroup>

Props API

ResizablePanelGroup Props

Extends React.ComponentPropsWithoutRef<typeof View>:

PropTypeDefaultDescription
direction'horizontal' | 'vertical''horizontal'Layout direction of the panels (flex-row or flex-col)
classNamestring-Additional CSS classes (base: flex-1 w-full h-full overflow-hidden)

ResizablePanel Props

Extends React.ComponentPropsWithoutRef<typeof View>:

PropTypeDefaultDescription
defaultSizenumberAuto-distributedInitial panel size as a percentage (0–100). Undefined sizes split the remaining space equally
minSizenumber0Minimum panel size as a percentage
maxSizenumber100Maximum panel size as a percentage
classNamestring-Additional CSS classes (base: overflow-hidden)

ResizableHandle Props

Extends React.ComponentPropsWithoutRef<typeof View>:

PropTypeDefaultDescription
withHandlebooleanfalseShow a grip icon indicator on the handle (uses GripVertical or GripHorizontal from lucide)
classNamestring-Additional CSS classes (horizontal base: w-8 h-full -mx-4, vertical base: h-8 w-full -my-4)

Best Practices

  1. Sizes should sum to 100: Panel defaultSize values should add up to 100. If they don't, the component normalizes them proportionally.

  2. Use withHandle for discoverability: The grip icon signals to users that the divider is draggable. Omit it only in minimal or advanced layouts.

  3. Set min/max constraints: Use minSize and maxSize on panels to prevent them from collapsing to zero or taking over the entire layout.

  4. Gesture handler setup: This component requires react-native-gesture-handler and react-native-reanimated. Ensure your app is wrapped in a GestureHandlerRootView.

  5. Negative margins on handles: The handle uses -mx-4 (horizontal) or -my-4 (vertical) for a larger touch target. Keep this in mind when styling adjacent content.

  • Use ScrollArea for scrollable content within panels
  • Combine with Card to place structured content in each panel
  • Use Separator for non-interactive visual dividers

On this page