Skip to content

API Reference ​

Composable API ​

The composable API is the recommended way to use Flexily. It provides a high-level engine with pluggable text measurement, built on top of the low-level Node API.

createFlexily(options?) ​

Create a batteries-included engine with monospace text measurement.

typescript
import { createFlexily } from "flexily"

const flex = createFlexily()
const node = flex.createNode()
node.setTextContent("Hello world")
flex.calculateLayout(node, 80, 24)

Options:

OptionTypeDefaultDescription
charWidthnumber1Width of each character cell
charHeightnumber1Height of each character cell
defaults"yoga" | "css""yoga"Style-defaults preset for created nodes — see Yoga divergences

For terminal UIs, the default charWidth=1, charHeight=1 maps directly to terminal cells.

createBareFlexily() ​

Create a minimal engine with no text measurement plugin. Use pipe() to add plugins. Accepts the same optional defaults preset as createFlexily().

typescript
import { createBareFlexily, pipe, withTestMeasurer } from "flexily"

const flex = pipe(createBareFlexily(), withTestMeasurer())

Calling setTextContent() on a bare engine without a text plugin throws an error.

pipe(engine, ...plugins) ​

Apply plugins to an engine, left to right. Returns the modified engine.

typescript
import { createBareFlexily, pipe, withMonospace, withTestMeasurer } from "flexily"

const flex = pipe(createBareFlexily(), withMonospace(1, 1))

FlexilyEngine ​

The composable engine interface.

typescript
interface FlexilyEngine {
  createNode(): FlexilyNode
  calculateLayout(root: FlexilyNode, width?: number, height?: number, direction?: number): void
  textLayout?: TextLayoutService
}
MethodDescription
createNode()Create a new FlexilyNode (Node with text content mixin)
calculateLayoutCalculate layout for a tree (direction defaults to LTR)
textLayoutThe active text measurement backend (if any)

FlexilyNode ​

Extends Node with text content methods. Created via engine.createNode().

typescript
interface FlexilyNode extends Node {
  setTextContent(text: string, style?: Partial<ResolvedTextStyle>): void
  getTextContent(): string | null
}
MethodDescription
setTextContent()Set text content and install an automatic measure function
getTextContent()Get the current text content (or null if none)

Calling setMeasureFunc() on a FlexilyNode clears its text content. Calling setTextContent() replaces any existing measure function.

FlexilyPlugin ​

A function that extends or configures the engine.

typescript
type FlexilyPlugin = (engine: FlexilyEngine) => FlexilyEngine

TextLayoutService ​

The pluggable text measurement backend interface.

typescript
interface TextLayoutService {
  prepare(input: TextPrepareInput): PreparedText
}

prepare() takes text and style, returning a PreparedText object that can compute intrinsic sizes and layout at any width.

Text Measurement Plugins ​

withMonospace(charWidth?, charHeight?) ​

Monospace text measurement. Each grapheme cluster = charWidth units wide, always 1 line (no wrapping). Default for createFlexily().

typescript
import { createBareFlexily, pipe, withMonospace } from "flexily"

const flex = pipe(createBareFlexily(), withMonospace(1, 1))
ParameterTypeDefaultDescription
charWidthnumber1Width per character cell
charHeightnumber1Height per character cell

withTestMeasurer() ​

Deterministic text measurement for tests and CI. Fixed grapheme width table: Latin 0.8, CJK 1.0, emoji 1.8 (relative to fontSize). Supports word wrapping.

typescript
import { createBareFlexily, pipe, withTestMeasurer } from "flexily"

const flex = pipe(createBareFlexily(), withTestMeasurer())

Cross-platform deterministic -- same results on every OS and runtime.

withPretext(pretext) ​

Proportional font measurement via @chenglou/pretext (peer dependency). For Canvas-based text rendering.

typescript
import { createBareFlexily, pipe, withPretext } from "flexily"
import pretext from "@chenglou/pretext"

const flex = pipe(createBareFlexily(), withPretext(pretext))
ParameterTypeDescription
pretextPretextAPIThe pretext module (install separately)

Node ​

The low-level Yoga-compatible API. All composable API nodes extend Node.

Creating Nodes ​

typescript
import { Node } from "flexily"

const node = Node.create()

Nodes form a tree. Each node represents a flexbox container or leaf.

Tree Operations ​

typescript
node.insertChild(child, index) // Insert child at index
node.removeChild(child) // Remove child
node.getChildCount() // Number of children
node.getChild(index) // Get child by index
node.getParent() // Get parent node (or null)

Dimension Setters ​

typescript
node.setWidth(100) // Fixed width
node.setHeight(50) // Fixed height
node.setWidthAuto() // Auto width
node.setHeightAuto() // Auto height
node.setWidthPercent(50) // 50% of parent
node.setHeightPercent(50) // 50% of parent
node.setMinWidth(10)
node.setMinHeight(10)
node.setMaxWidth(200)
node.setMaxHeight(200)

Flex Properties ​

typescript
import {
  FLEX_DIRECTION_ROW,
  FLEX_DIRECTION_COLUMN,
  FLEX_DIRECTION_ROW_REVERSE,
  FLEX_DIRECTION_COLUMN_REVERSE,
  WRAP_WRAP,
  WRAP_NO_WRAP,
  JUSTIFY_CENTER,
  JUSTIFY_FLEX_START,
  JUSTIFY_FLEX_END,
  JUSTIFY_SPACE_BETWEEN,
  JUSTIFY_SPACE_AROUND,
  JUSTIFY_SPACE_EVENLY,
  ALIGN_CENTER,
  ALIGN_FLEX_START,
  ALIGN_FLEX_END,
  ALIGN_STRETCH,
  ALIGN_BASELINE,
} from "flexily"

node.setFlexDirection(FLEX_DIRECTION_ROW)
node.setFlexWrap(WRAP_WRAP)
node.setFlexGrow(1)
node.setFlexShrink(0)
node.setFlexBasis(100)
node.setJustifyContent(JUSTIFY_CENTER)
node.setAlignItems(ALIGN_CENTER)
node.setAlignSelf(ALIGN_FLEX_END)
node.setAlignContent(ALIGN_CENTER)

Spacing ​

typescript
import {
  EDGE_TOP,
  EDGE_RIGHT,
  EDGE_BOTTOM,
  EDGE_LEFT,
  EDGE_ALL,
  EDGE_START,
  EDGE_END,
  GUTTER_ALL,
  GUTTER_ROW,
  GUTTER_COLUMN,
} from "flexily"

// Padding
node.setPadding(EDGE_ALL, 10)
node.setPadding(EDGE_TOP, 5)

// Margin
node.setMargin(EDGE_ALL, 10)
node.setMarginAuto(EDGE_LEFT)

// Border
node.setBorder(EDGE_ALL, 1)

// Gap
node.setGap(GUTTER_ALL, 10)
node.setGap(GUTTER_ROW, 5)
node.setGap(GUTTER_COLUMN, 5)

The production engine accepts number | Value in all four spacing setters. Numbers remain point lengths. Use parseLength with your adapter's ch and lh scale to construct a Value; the existing resolver evaluates math expressions.

typescript
import { parseLength } from "flexily"

const scale = { ch: 1, lh: 1 } // Terminal cells; choose your adapter's scale.
node.setPadding(EDGE_LEFT, parseLength("2ch", scale))
node.setMargin(EDGE_START, parseLength("10cqi", scale))
node.setBorder(EDGE_LEFT, parseLength("calc(1ch + 2cqi)", scale))
node.setGap(GUTTER_ROW, parseLength("1lh", scale))

cqi uses the nearest ancestor query container, skipping the node itself. A query container's own padding, margin, border and gap therefore use its ancestor context; its children use its frozen inline size. The layout root uses the width passed to calculateLayout for its own CQ spacing. Resize resolves these values again during layout and intrinsic measurement.

Inline edges (left, right, start and end) and column gaps refuse lh. Block edges (top and bottom) and row gaps refuse ch and cqi. EDGE_ALL and GUTTER_ALL check both axes before changing any value. Percentage padding and margins retain their existing containing-block width basis; border widths refuse percentages, including percentages inside math expressions.

getPadding and getMargin return stored Value objects. getBorder and getGap retain numeric point results and throw LengthError for a stored non-point length; read node.style.border or node.style.gap to inspect that Value. After layout, getComputedPadding, getComputedMargin and getComputedBorder resolve edge lengths in the layout context. There is no computed gap getter. Unset logical borders still return NaN from getBorder.

The classic engine retains its numeric setters and percentage/auto behavior. It refuses CQ, adapter units and math expressions in shared spacing storage with a typed property error saying production engine only.

Positioning ​

typescript
import { POSITION_TYPE_ABSOLUTE, POSITION_TYPE_RELATIVE } from "flexily"

node.setPositionType(POSITION_TYPE_ABSOLUTE)
node.setPosition(EDGE_TOP, 10)
node.setPosition(EDGE_LEFT, 20)

Aspect Ratio ​

typescript
node.setAspectRatio(16 / 9)

Measure Functions ​

For leaf nodes that measure their own content (e.g., text):

typescript
node.setMeasureFunc((width, widthMode, height, heightMode) => {
  // widthMode: MEASURE_MODE_EXACTLY | MEASURE_MODE_AT_MOST | MEASURE_MODE_UNDEFINED
  return { width: computedWidth, height: computedHeight }
})

Baseline Functions ​

typescript
node.setBaselineFunc((width, height) => {
  return baselineOffset // number
})

Layout Calculation ​

typescript
import { DIRECTION_LTR, DIRECTION_RTL } from "flexily"

node.calculateLayout(availableWidth, availableHeight, DIRECTION_LTR)

Reading Computed Values ​

After calculateLayout():

typescript
node.getComputedLeft()
node.getComputedTop()
node.getComputedRight()
node.getComputedBottom()
node.getComputedWidth()
node.getComputedHeight()
node.getComputedPadding(EDGE_TOP)
node.getComputedMargin(EDGE_LEFT)
node.getComputedBorder(EDGE_ALL)

Dirty Tracking ​

typescript
node.isDirty() // Has this node been modified since last layout?
node.markDirty() // Force recalculation

Disposal ​

typescript
node.free() // Free this node and clean up references

Constants ​

All constants are Yoga-compatible. Import from flexily:

CategoryConstants
DirectionDIRECTION_LTR, DIRECTION_RTL, DIRECTION_INHERIT
Flex DirectionFLEX_DIRECTION_ROW, FLEX_DIRECTION_COLUMN, FLEX_DIRECTION_ROW_REVERSE, FLEX_DIRECTION_COLUMN_REVERSE
JustifyJUSTIFY_FLEX_START, JUSTIFY_CENTER, JUSTIFY_FLEX_END, JUSTIFY_SPACE_BETWEEN, JUSTIFY_SPACE_AROUND, JUSTIFY_SPACE_EVENLY
AlignALIGN_FLEX_START, ALIGN_CENTER, ALIGN_FLEX_END, ALIGN_STRETCH, ALIGN_BASELINE, ALIGN_AUTO, ALIGN_SPACE_BETWEEN, ALIGN_SPACE_AROUND, ALIGN_SPACE_EVENLY
WrapWRAP_NO_WRAP, WRAP_WRAP, WRAP_WRAP_REVERSE
PositionPOSITION_TYPE_STATIC, POSITION_TYPE_RELATIVE, POSITION_TYPE_ABSOLUTE
EdgeEDGE_LEFT, EDGE_TOP, EDGE_RIGHT, EDGE_BOTTOM, EDGE_START, EDGE_END, EDGE_HORIZONTAL, EDGE_VERTICAL, EDGE_ALL
GapGUTTER_COLUMN, GUTTER_ROW, GUTTER_ALL
Measure ModeMEASURE_MODE_EXACTLY, MEASURE_MODE_AT_MOST, MEASURE_MODE_UNDEFINED
OverflowOVERFLOW_VISIBLE, OVERFLOW_HIDDEN, OVERFLOW_SCROLL
DisplayDISPLAY_FLEX, DISPLAY_NONE

Testing Utilities ​

Flexily exports diagnostic helpers for downstream consumers:

typescript
import {
  getLayout,
  formatLayout,
  diffLayouts,
  textMeasure,
  assertLayoutSanity,
  expectRelayoutMatchesFresh,
  expectIdempotent,
  expectResizeRoundTrip,
} from "flexily/testing"
ExportDescription
getLayout(node)Recursively extract computed layout as plain objects
formatLayout(layout)Pretty-print a layout tree for debugging
diffLayouts(a, b)Node-by-node diff with NaN-safe comparison
textMeasure(width)Factory for wrapping text measure functions
assertLayoutSanity(node)Validate dimensions are finite and non-negative
expectRelayoutMatchesFresh(buildTree, w, h)Differential oracle: incremental must match fresh
expectIdempotent(buildTree, w, h)Two identical layouts must produce same result
expectResizeRoundTrip(buildTree, widths)Resize sequence must match fresh at final width

Last updated: