Using Box

The Box module is the core of effect-boxes. A Box represents a rectangular block of text with explicit dimensions that can be composed, aligned, and transformed through functional composition.

Creating Boxes

The most common way to create a box is from a string. Multi-line strings are handled automatically.

import * as Box from "effect-boxes/Box";
 
const hello = Box.text("Hello\nWorld");
// 2 rows, 5 columns
Hello
World

Other constructors include Box.emptyBox(rows, cols) for blank space, Box.char(c) for a single character, and Box.line(s) for a single-line box:

// An empty box with specific dimensions
const empty = Box.emptyBox(2, 10);
 
// A single character
const star = Box.char("*");
 
// A single-line box (strips newlines)
const oneLine = Box.line("Hello World");

Text Flow

For longer text, para wraps content to a specified width, and columns creates newspaper-style columnar layouts.

const paragraph = Box.para(
  "The Box module is the core of the effect-boxes library, providing the fundamental data structures and operations for creating and composing rectangular text layouts. Boxes can be combined horizontally and vertically, aligned within larger spaces, and annotated with styling information for rich terminal output.",
  Box.left,
  50,
);
The Box module is the core of the effect-boxes
library, providing the fundamental data structures
and operations for creating and composing
rectangular text layouts. Boxes can be combined
horizontally and vertically, aligned within larger
spaces, and annotated with styling information for
rich terminal output.
const cols = Box.columns(
  "The Box module is the core of the effect-boxes library...",
  Box.left, // alignment
  25, // column width
  6, // column height
);
const layout = Box.hsep(cols, 2, Box.top);
The Box module is the      composing rectangular      information for rich
core of the effect-boxes   text layouts. Boxes can    terminal output.
library, providing the     be combined horizontally
fundamental data           and vertically, aligned
structures and operations  within larger spaces, and
for creating and           annotated with styling

Composing Boxes

Boxes can be combined horizontally and vertically to build complex layouts.

Horizontal Composition

const row = Box.hcat(
  [Box.text("Left"), Box.text(" | "), Box.text("Right")],
  Box.top,
);
Left | Right

Use punctuateH to insert a separator between boxes, or hsep for spacing:

const table = Box.punctuateH(
  [Box.text("Name"), Box.text("Age"), Box.text("City")],
  Box.top,
  Box.text(" | "),
);
Name | Age | City
const spaced = Box.hsep(
  [Box.text("A"), Box.text("B"), Box.text("C")],
  3, // spaces between boxes
  Box.top,
);
A   B   C

Vertical Composition

const column = Box.vcat(
  [Box.text("Top"), Box.text("Middle"), Box.text("Bottom")],
  Box.left,
);
Top
Middle
Bottom
const sections = Box.punctuateV(
  [Box.text("Header"), Box.text("Content"), Box.text("Footer")],
  Box.left,
  Box.text("---"),
);
Header
---
Content
---
Footer

Appending Individual Boxes

For combining two boxes, use hAppend and vAppend:

const combined = pipe(Box.text("Hello "), Box.hAppend(Box.text("World")));
Hello World

Alignment and Positioning

Alignment

Control how a box is positioned within a larger space:

const centered = Box.alignHoriz(Box.text("Hello"), Box.center1, 20);
────────────────────
       Hello        
────────────────────
// Align both horizontally and vertically
const centered2d = Box.align(
  Box.text("X"),
  Box.center1, // horizontal
  Box.center1, // vertical
  3, // height
  7, // width
);
───────
       
   X   
       
───────

Moving Boxes

Shift a box by adding empty space around it:

const positioned = pipe(Box.text("Hello"), Box.moveRight(5), Box.moveDown(2));
──────────
          
          
     Hello
──────────

Available movement functions: moveRight, moveLeft, moveDown, moveUp.

Truncation

Truncate each line to a maximum width, inserting an ellipsis where content was removed. The position parameter controls which part of the text is kept.

const long = Box.text("This is a very long piece of text");
 
// Keep the beginning
pipe(long, Box.truncate(15, Box.left));
 
// Keep the end
pipe(long, Box.truncate(15, Box.right));
 
// Keep both ends
pipe(long, Box.truncate(15, Box.center1));
left
This is a very…
pipe(long, Box.truncate(15, Box.right));
right
… piece of text
pipe(long, Box.truncate(15, Box.center1));
center
This is…of text

If the box is already within the target width, it is returned unchanged.

Annotations

Boxes are parameterized by an annotation type: Box<A>. Annotations are metadata attached to a box's content that renderers interpret differently.

Use Box.annotate to attach an annotation:

import * as Ansi from "effect-boxes/Ansi";
 
const error = pipe(Box.text("Error!"), Box.annotate(Ansi.red));
Error!

The annotation type determines what styling is applied at render time:

  • Ansi — Terminal colors and text effects (bold, underline, etc.)
  • Cmd — Terminal control sequences (cursor movement, screen clearing)
  • Html — HTML elements and attributes for web rendering

Each annotation type has a corresponding renderer that interprets it. See Using Ansi for terminal styling and the Rendering guide for how renderers process annotations.

Rendering

To get a string from a box, use the sync convenience functions:

const box = pipe(Box.text("Hello"), Box.annotate(Ansi.red))
 
// Strips all annotations
Box.renderPlainSync(box)
renderPlainSync
Hello
// Preserves ANSI styling
Box.renderPrettySync(box)
renderPrettySync
Hello

For Effect-based rendering and the full renderer layer system, see the Rendering guide.