Skip to main content

Overview

The BoxRenderable is a fundamental building block for creating layouts in OpenTUI. It provides a container with customizable borders, backgrounds, and full flexbox layout support through Yoga layout engine.

Basic Usage

import { BoxRenderable } from '@opentui/core'

const box = new BoxRenderable(renderer, {
  id: 'my-box',
  width: 'auto',
  height: 20,
  backgroundColor: '#1e293b',
  border: true,
  borderStyle: 'single',
  borderColor: '#3b82f6',
})

renderer.root.add(box)

Props

Layout Props

width
number | 'auto' | `${number}%`
default:"'auto'"
The width of the box. Can be a fixed pixel value, percentage, or ‘auto’ to size based on content.
height
number | 'auto' | `${number}%`
default:"'auto'"
The height of the box. Can be a fixed pixel value, percentage, or ‘auto’ to size based on content.
flexDirection
'row' | 'column' | 'row-reverse' | 'column-reverse'
default:"'column'"
Sets the main axis direction for child elements.
flexGrow
number
default:"0"
How much the box should grow relative to siblings when there’s extra space.
flexShrink
number
default:"1"
How much the box should shrink relative to siblings when space is constrained.
alignItems
'flex-start' | 'flex-end' | 'center' | 'stretch'
default:"'flex-start'"
Alignment of children along the cross axis.
justifyContent
'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around'
default:"'flex-start'"
Alignment of children along the main axis.
gap
number | `${number}%`
Spacing between all children (both row and column gaps).
rowGap
number | `${number}%`
Spacing between rows of children.
columnGap
number | `${number}%`
Spacing between columns of children.

Spacing Props

padding
number | `${number}%`
Padding on all sides.
paddingX
number | `${number}%`
Horizontal padding (left and right).
paddingY
number | `${number}%`
Vertical padding (top and bottom).
paddingTop
number | `${number}%`
Padding on the top side.
paddingRight
number | `${number}%`
Padding on the right side.
paddingBottom
number | `${number}%`
Padding on the bottom side.
paddingLeft
number | `${number}%`
Padding on the left side.
margin
number | `${number}%`
Margin on all sides.
marginX
number | `${number}%`
Horizontal margin (left and right).
marginY
number | `${number}%`
Vertical margin (top and bottom).

Visual Props

backgroundColor
string | RGBA
default:"'transparent'"
Background color as a hex string (e.g., ‘#1e293b’) or RGBA object.
border
boolean | BorderSides[]
default:"false"
Enable borders on all sides (true) or specify individual sides as an array: ['top', 'right', 'bottom', 'left'].
borderStyle
'single' | 'double' | 'rounded' | 'bold' | 'singleDouble' | 'doubleSingle' | 'classic'
default:"'single'"
The style of the border characters.
borderColor
string | RGBA
default:"'#FFFFFF'"
Color of the border.
focusedBorderColor
string | RGBA
default:"'#00AAFF'"
Border color when the box is focused (requires focusable: true).
customBorderChars
BorderCharacters
Custom characters for border rendering. Object with keys: topLeft, top, topRight, right, bottomRight, bottom, bottomLeft, left.
shouldFill
boolean
default:"true"
Whether to fill the box with the background color.
title
string
Optional title text to display in the top border.
titleAlignment
'left' | 'center' | 'right'
default:"'left'"
Alignment of the title text in the border.

Behavior Props

focusable
boolean
default:"false"
Whether the box can receive keyboard focus.
visible
boolean
default:"true"
Whether the box is visible.
zIndex
number
default:"0"
Stacking order of the box. Higher values appear on top.
position
'relative' | 'absolute'
default:"'relative'"
Positioning mode. Use ‘absolute’ with left, right, top, bottom props.

Examples

Three-Column Layout

const container = new BoxRenderable(renderer, {
  id: 'container',
  flexDirection: 'row',
  alignItems: 'stretch',
  width: 'auto',
  height: 'auto',
  flexGrow: 1,
})

const leftSidebar = new BoxRenderable(renderer, {
  id: 'left-sidebar',
  width: 20,
  backgroundColor: '#64748b',
  border: true,
  padding: 1,
})

const mainContent = new BoxRenderable(renderer, {
  id: 'main-content',
  flexGrow: 1,
  backgroundColor: '#eab308',
  border: true,
  padding: 2,
})

const rightSidebar = new BoxRenderable(renderer, {
  id: 'right-sidebar',
  width: 20,
  backgroundColor: '#7c3aed',
  border: true,
  padding: 1,
})

container.add(leftSidebar)
container.add(mainContent)
container.add(rightSidebar)

renderer.root.add(container)

Box with Title and Custom Border

const dialog = new BoxRenderable(renderer, {
  id: 'dialog',
  width: 50,
  height: 15,
  backgroundColor: '#1e293b',
  border: true,
  borderStyle: 'double',
  borderColor: '#3b82f6',
  title: 'Settings',
  titleAlignment: 'center',
  padding: 2,
})

renderer.root.add(dialog)

Absolute Positioned Box

const overlay = new BoxRenderable(renderer, {
  id: 'overlay',
  position: 'absolute',
  right: 1,
  bottom: 1,
  width: 20,
  height: 3,
  backgroundColor: '#22c55e',
  border: true,
  borderColor: '#16a34a',
})

renderer.root.add(overlay)

Centered Content

const centered = new BoxRenderable(renderer, {
  id: 'centered',
  flexDirection: 'row',
  alignItems: 'center',
  justifyContent: 'center',
  width: 'auto',
  height: 'auto',
  flexGrow: 1,
  backgroundColor: '#0f172a',
})

const content = new BoxRenderable(renderer, {
  id: 'content',
  width: 40,
  height: 10,
  backgroundColor: '#7c3aed',
  border: true,
})

centered.add(content)
renderer.root.add(centered)

Focusable Box with Highlight

const focusBox = new BoxRenderable(renderer, {
  id: 'focus-box',
  width: 30,
  height: 10,
  backgroundColor: '#1e293b',
  border: true,
  borderColor: '#64748b',
  focusedBorderColor: '#3b82f6',
  focusable: true,
  padding: 1,
})

renderer.root.add(focusBox)
focusBox.focus()

Gap and Spacing

const gridContainer = new BoxRenderable(renderer, {
  id: 'grid',
  flexDirection: 'column',
  gap: 2,
  padding: 2,
  backgroundColor: '#0f172a',
  border: true,
})

for (let i = 0; i < 5; i++) {
  const row = new BoxRenderable(renderer, {
    id: `row-${i}`,
    flexDirection: 'row',
    columnGap: 2,
    height: 3,
  })

  for (let j = 0; j < 3; j++) {
    const cell = new BoxRenderable(renderer, {
      id: `cell-${i}-${j}`,
      flexGrow: 1,
      backgroundColor: '#3b82f6',
    })
    row.add(cell)
  }

  gridContainer.add(row)
}

renderer.root.add(gridContainer)

Methods

add(child: Renderable, index?: number): number

Adds a child renderable to the box.
const text = new TextRenderable(renderer, { content: 'Hello' })
box.add(text)

remove(id: string): void

Removes a child by its ID.
box.remove('child-id')

focus(): void

Sets focus to the box (requires focusable: true).
box.focus()

requestRender(): void

Manually requests a re-render of the box.
box.backgroundColor = '#ff0000'
box.requestRender()

Notes

  • Box uses the Yoga layout engine, which provides flexbox-like layout capabilities
  • Borders take up 1 cell on each enabled side, affecting the inner content area
  • When using percentage-based sizing, the value is relative to the parent container
  • The shouldFill prop controls whether the background color fills the entire box area
  • Absolute positioning takes the box out of the normal layout flow
For scrollable content areas, use ScrollBox which wraps Box with scrolling functionality.

Build docs developers (and LLMs) love