npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

flexily

v0.3.0

Published

Pure JavaScript flexbox layout engine — Yoga-compatible API, faster initial layout, smaller bundle, no WASM

Readme

Flexily

Pure JavaScript flexbox layout engine with Yoga-compatible API.

npm version License: MIT

import { Node, FLEX_DIRECTION_ROW, DIRECTION_LTR } from "flexily"

const root = Node.create()
root.setWidth(100)
root.setFlexDirection(FLEX_DIRECTION_ROW)

const child = Node.create()
child.setFlexGrow(1)
root.insertChild(child, 0)

root.calculateLayout(100, 100, DIRECTION_LTR)
console.log(child.getComputedWidth()) // 100

Why Flexily?

Yoga is the industry standard flexbox engine, used by React Native, Ink, and thousands of apps. It's mature and battle-tested. But it's C++ compiled to WASM, and that creates real problems for JavaScript applications:

Async initialization. Yoga requires await Yoga.init() before creating any nodes. No synchronous startup, no use at module load time, no use in config files or build scripts. For CLIs that should start instantly, this adds latency and complexity.

WASM boundary crossing. Every method call (setWidth, setFlexGrow, etc.) crosses the JS-to-WASM boundary. Node creation is ~8x more expensive than a JS object. For TUIs that rebuild layout trees per render, this dominates.

Memory growth. WASM linear memory grows but never shrinks. Yoga's yoga-wasm-web has a known memory growth bug where each node allocation permanently grows the WASM heap. In long-running apps, this caused 120GB RAM usage in Claude Code.

Debugging opacity. You can't step into WASM in a JS debugger. When layout is wrong, you get a computed number with no way to inspect the algorithm's intermediate state. Flexily is readable JS — set a breakpoint in layout-zero.ts.

No tree-shaking. The WASM binary is monolithic. You get the entire engine even if you use a fraction of the features.

Facebook's original pure-JS flexbox engine (css-layout) was abandoned when they moved to C++. flexbox.js exists but is unmaintained and missing features. Flexily fills the gap: comprehensive CSS flexbox support, Yoga-compatible API, pure JS, zero WASM.

Who Should Use Flexily

Most developers should use a framework built on Flexily, not Flexily directly. Flexily is for:

  • Framework authors building a TUI or layout framework that needs a JS layout engine
  • Canvas/game developers who need flexbox for non-DOM rendering
  • Specialized tools where you need direct control over layout computation
  • Anyone replacing Yoga who wants a drop-in pure-JS alternative

Building a terminal UI? Use silvery, which uses Flexily by default. You get React components, hooks, and layout feedback without touching the low-level API.

Status

1495 tests, including 44 Yoga compatibility tests and 1200+ incremental re-layout fuzz tests. Used by silvery as its default layout engine.

| Feature | Status | | --------------------------------------------- | -------- | | Core flexbox (direction, grow, shrink, basis) | Complete | | Alignment (justify-content, align-items) | Complete | | Spacing (gap, padding, margin, border) | Complete | | Constraints (min/max width/height) | Complete | | Measure functions (text sizing) | Complete | | Absolute positioning | Complete | | Aspect ratio | Complete | | Flex-wrap (multi-line layouts) | Complete | | Logical edges (EDGE_START/END) | Complete | | RTL support | Complete | | Baseline alignment | Complete |

Installation

npm install flexily

Performance

Flexily and Yoga each win in different scenarios:

| Scenario | Winner | Margin | Tree Size | | ----------------------- | ----------- | ---------- | ---------------- | | Initial layout | Flexily | 1.5-2.5x | 64-969 nodes | | No-change re-layout | Flexily | 5.5x | 406-969 nodes | | Single dirty leaf | Yoga | 2.8-3.4x | 406-969 nodes | | Deep nesting (15+) | Yoga | increasing | 1 node per level |

Benchmarks use TUI-realistic trees: columns × bordered cards with measure functions (e.g., 5 columns × 20 cards = ~406 nodes, 8×30 = ~969 nodes). Typical depth is 3-5 levels (column → card → content → text). See docs/performance.md for full methodology.

Where Yoga wins — and why it matters less in practice. Yoga is 2.8-3.4x faster in the single-dirty-leaf scenario: one node changes in a ~400-1000 node tree. WASM's per-node layout computation is genuinely faster than JS. But in interactive TUIs, most renders are no-change frames (cursor moved, selection changed) where Flexily is 5.5x faster. Initial layout (new screen, tab switch) also favors Flexily at 1.5-2.5x. The single-dirty-leaf case is a minority of frames in practice.

Typical interactive TUI operation mix:

| Operation | Frequency | Winner | Why | | --------------------- | ------------- | -------------- | ------------------------------------- | | Cursor/selection move | Very frequent | Flexily 5.5x | No layout change → fingerprint cache | | Content edit | Frequent | Yoga 3x | Single dirty leaf in existing tree | | Initial render | Once | Flexily 1.5-2x | JS node creation avoids WASM boundary | | Window resize | Occasional | Yoga 2.7x | Full re-layout of existing tree |

Flexily's fingerprint cache makes no-change re-layout essentially free (27ns regardless of tree size). Initial layout wins come from JS node creation avoiding WASM boundary crossings (~8x cheaper per node). Most TUI apps have shallow nesting (3-5 levels) — well below the 15-level crossover where Yoga overtakes Flexily.

Use Yoga instead when your primary workload is frequent incremental re-layout of large pre-existing trees, you have deep nesting (15+ levels), or you're in the React Native ecosystem.

See docs/performance.md for detailed benchmarks including TUI-realistic trees with measure functions.

Algorithm

Flexily provides two layout implementations that produce identical output and pass identical tests:

Zero-allocation (default, flexily): Mutates FlexInfo structs on nodes instead of allocating temporary objects. Faster for flat/wide trees typical of TUI layouts. Re-entrant via save/restore of scratch arrays (supports nested calculateLayout() calls from measure/baseline functions).

Classic (flexily/classic): Allocates temporary objects during layout. Easier to read and debug. Use this when stepping through the algorithm or comparing behavior.

import { Node } from "flexily" // zero-allocation (default)
import { Node } from "flexily/classic" // allocating (debugging)

Both implement CSS Flexbox spec Section 9.7 with iterative freeze for min/max constraints, Yoga-compatible edge-based rounding, weighted flex-shrink, auto margin absorption, and full RTL support.

Correctness

Incremental re-layout (caching unchanged subtrees) is essential for performance but introduces subtle bugs — Chrome's Blink team experienced a "chain of ~10 bugs over a year" in their flexbox implementation. Flexily addresses this with layered testing:

| Layer | Tests | What it verifies | | ------------------ | --------- | -------------------------------------------------------------- | | Yoga compatibility | 44 | Identical output to Yoga for every feature | | Feature tests | ~110 | Each flexbox feature in isolation | | Re-layout fuzz | 1200+ | Incremental re-layout matches fresh layout across random trees |

The fuzz tests use a differential oracle: build a random tree, layout, mark nodes dirty, re-layout, then compare against a fresh layout of the identical tree. This has caught 3 distinct caching bugs that all 524 single-pass tests missed.

See docs/testing.md for methodology and docs/incremental-layout-bugs.md for the bug taxonomy.

Bundle Size

| | Yoga | Flexily | Savings | | -------- | ------ | ----------------- | -------------------- | | Minified | 117 KB | 47 KB (35 KB[^1]) | 2.5-3.4x smaller | | Gzipped | 39 KB | 16 KB (11 KB[^1]) | 2.5-3.6x smaller |

[^1]: 11 KB when bundlers tree-shake the optional debug dependency.

API Compatibility

Yoga-compatible API (44 comparison tests passing). Near drop-in replacement for common use cases:

// Yoga
import Yoga from "yoga-wasm-web"
const yoga = await Yoga.init() // Async!
const root = yoga.Node.create()

// Flexily
import { Node } from "flexily"
const root = Node.create() // Sync!

Same constants, same method names, same behavior.

Documentation

| Document | Description | | ---------------------------------------------------------- | ----------------------------------- | | Getting Started | Quick guide to building layouts | | API Reference | Complete API documentation | | Algorithm | How the layout algorithm works | | Performance | Benchmarks and methodology | | Yoga Comparison | Feature comparison with Yoga | | Testing | Test infrastructure and methodology | | Incremental Layout Bugs | Bug taxonomy and debugging guide |

Related Projects

| Project | Language | Description | | ------------------------------------------------------- | ---------- | -------------------------------------------------------------------------------------------------- | | Yoga | C++/WASM | Facebook's flexbox engine. Industry standard, used by React Native, Ink, Litho. | | Taffy | Rust | High-performance layout library supporting Flexbox and CSS Grid. Used by Dioxus and Bevy. | | flexbox.js | JavaScript | Pure JS flexbox engine by Planning-nl. Reference implementation that inspired Flexily's algorithm. | | css-layout | JavaScript | Facebook's original pure-JS flexbox, predecessor to Yoga. Deprecated. | | silvery | TypeScript | React for CLIs with layout feedback. Uses Flexily by default. |

Code Structure

src/
├── index.ts        # Main export
├── node-zero.ts    # Node class with FlexInfo
├── layout-zero.ts  # Layout algorithm (~2000 lines)
├── constants.ts    # Flexbox constants (Yoga-compatible)
├── types.ts        # TypeScript interfaces
├── utils.ts        # Shared utilities
└── classic/        # Allocating algorithm (for debugging)
    ├── node.ts
    └── layout.ts

License

MIT