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

flexycakes

v1.3.3

Published

Community fork of caplin/FlexLayout (ISC). Not affiliated.

Readme

npm downloads npm Discord

This is Flexycakes, a fork of FlexLayout with enhanced features including pin/unpin functionality for panels. Flexycakes extends the original FlexLayout capabilities with additional user interface improvements and workflow optimizations.

📍 Pin Board

💰 Paid Bounties Program

🚀 Exciting News! We're launching a paid bounty system to support young developers in the open source community.

Join Discord

What we're working on:

  • 🔍 Researching the best bounty platforms
  • 📋 Setting up contract logistics
  • 🎯 Defining contribution opportunities
  • 💡 Creating developer-friendly workflows

Stay tuned: Join our Discord community for real-time updates and early access to bounty opportunities!

🚀 Quick Start

Installation

Install Flexycakes from npm:

npm install flexycakes

or using yarn:

yarn add flexycakes

or using pnpm:

pnpm add flexycakes

Basic Setup

  1. Import the components:

    import { Layout, Model } from "flexycakes";
  2. Include a theme:

    Option A: Import in your JavaScript code:

    import "flexycakes/style/light.css";

    Option B: Link in your HTML:

    <link rel="stylesheet" href="/style/light.css" />
  3. Optional: For dynamic theming, see Theme Switching

Development Setup with Symbolic Linking

If you're contributing to Flexycakes or want to develop locally:

  1. Clone the repository:

    git clone https://github.com/powerdragonfire/flexycakes.git
    cd flexycakes
  2. Install dependencies:

    pnpm install
  3. Create a symbolic link:

    # Build the library first
    pnpm build
    
    # Create global link
    npm link
    # or with pnpm
    pnpm link --global
  4. Link in your project:

    cd /path/to/your/project
    npm link flexycakes
    # or with pnpm
    pnpm link --global flexycakes
  5. Start development mode:

    # In flexycakes directory
    pnpm dev

    This will watch for changes and rebuild automatically.


📖 Usage Guide

Basic Layout Setup

The <Layout> component renders the tabsets and splitters. Here's what you need:

Required Props

| Prop | Type | Description | | --------- | -------- | ----------------------------------------- | | model | Model | The layout model containing the structure | | factory | Function | Creates React components for each tab |

💡 Tip: See Optional Layout Props for additional configuration options.

Core Concepts

The model is a tree of Node objects that define your layout structure:

  • Created using Model.fromJson(jsonObject)
  • Saved using model.toJson()

The factory function takes a Node object and returns the React component to render in that tab.

Simple Example

// Define your layout structure
const json = {
    global: {},
    borders: [],
    layout: {
        type: "row",
        weight: 100,
        children: [
            {
                type: "tabset",
                weight: 50,
                children: [
                    {
                        type: "tab",
                        name: "Dashboard",
                        component: "dashboard",
                    },
                ],
            },
            {
                type: "tabset",
                weight: 50,
                children: [
                    {
                        type: "tab",
                        name: "Settings",
                        component: "settings",
                    },
                ],
            },
        ],
    },
};

// Create your app
const model = Model.fromJson(json);

function App() {
    const factory = (node) => {
        const component = node.getComponent();

        switch (component) {
            case "dashboard":
                return <DashboardComponent />;
            case "settings":
                return <SettingsComponent />;
            default:
                return <div>{node.getName()}</div>;
        }
    };

    return <Layout model={model} factory={factory} />;
}

🎮 Try it live: CodeSandbox Demo | TypeScript Example


🏗️ Layout Structure

JSON Model Components

The model JSON has 4 main sections:

| Section | Required | Description | | --------- | -------- | ---------------------------------------------- | | global | ❌ | Global layout options | | layout | ✅ | Main layout hierarchy | | borders | ❌ | Edge panels ("top", "bottom", "left", "right") | | popouts | ❌ | External window definitions |

Node Types

🔹 Row Nodes

  • Purpose: Container for tabsets and child rows
  • Orientation: Top-level rows are horizontal (unless rootOrientationVertical is set)
  • Children: Tabsets and other rows

🔹 TabSet Nodes

  • Purpose: Container for tabs
  • Properties: List of tabs + selected tab index
  • Behavior: Auto-created when tabs move, deleted when empty (unless enableDeleteWhenEmpty: false)

🔹 Tab Nodes

  • Purpose: Individual panel content
  • Properties: Component name (for factory) + display text
  • Content: Loaded via the factory function

🔹 Border Nodes

  • Purpose: Edge-docked panels
  • Location: Can only exist in the borders section
  • Behavior: Similar to tabsets but docked to screen edges

FlexLayout Demo Showing Layout

💡 Pro Tip: Use the demo app to visually create layouts, then export the JSON via 'Show Layout JSON in console'.

Weight System

Node weights determine relative sizing:

  • Example: Weights of 30,70 = same as 3,7
  • Usage: Only relative values matter
  • Application: Applies to rows and tabsets within their parent

🎨 Customization

Theme Switching

Available Themes

  • light.css - Clean light theme
  • dark.css - Dark mode theme
  • gray.css - Neutral gray theme
  • underline.css - Minimal underline style
  • rounded.css - Rounded corners theme
  • combined.css - All themes in one file

Dynamic Theme Switching

Use the combined.css theme for runtime switching:

// Setup with theme container
<div ref={containerRef} className="flexlayout__theme_light">
    <Layout model={model} factory={factory} />
</div>;

// Change theme programmatically
containerRef.current.className = "flexlayout__theme_dark";

Custom Tab Rendering

Customize individual tabs with onRenderTab:

const onRenderTab = (node, renderValues) => {
    // renderValues.leading = <Icon />; (red area)
    // renderValues.content += " *"; (green area)
    renderValues.buttons.push(<MenuIcon key="menu" />); // yellow area
};

<Layout model={model} factory={factory} onRenderTab={onRenderTab} />;

Custom TabSet Rendering

Customize tabset headers with onRenderTabSet:

const onRenderTabSet = (node, renderValues) => {
    // Add persistent buttons (red area)
    renderValues.stickyButtons.push(
        <button key="add" onClick={() => addNewTab(node)}>
            +
        </button>,
    );

    // Add contextual buttons (green area)
    renderValues.buttons.push(<SettingsIcon key="settings" />);
};

⚡ Actions & Events

Model Actions

All layout changes happen through actions via Model.doAction():

// Add a new tab
model.doAction(
    Actions.addNode(
        { type: "tab", component: "grid", name: "New Grid" },
        "targetTabsetId",
        DockLocation.CENTER,
        0, // position (use -1 for end)
    ),
);

// Update global settings
model.doAction(
    Actions.updateModelAttributes({
        splitterSize: 40,
    }),
);

📚 Reference: Full Actions API

Tab Events

Handle tab lifecycle events:

function MyComponent({ node }) {
    useEffect(() => {
        // Listen for resize events
        node.setEventListener("resize", ({ rect }) => {
            console.log("Tab resized:", rect);
        });

        // Save data before serialization
        node.setEventListener("save", () => {
            node.getConfig().myData = getCurrentData();
        });

        // Handle visibility changes
        node.setEventListener("visibility", ({ visible }) => {
            if (visible) startUpdates();
            else stopUpdates();
        });
    }, [node]);
}

Available Events

| Event | Parameters | Description | | ------------ | ----------- | ---------------------------- | | resize | {rect} | Tab resized during layout | | close | none | Tab is being closed | | visibility | {visible} | Tab visibility changed | | save | none | Before serialization to JSON |


🪟 Popout Windows

Overview

Render tabs in external browser windows for multi-monitor setups.

Setup Requirements

  1. HTML Host Page: Copy popout.html to your public directory
  2. Tab Configuration: Add enablePopout: true to tab definitions
  3. Styling: Styles are automatically copied from main window

Code Considerations

Popout windows use a different document and window. Access them via:

function PopoutComponent() {
    const selfRef = useRef();

    const handleEvent = () => {
        // ❌ Wrong - uses main window
        document.addEventListener("click", handler);

        // ✅ Correct - uses popout window
        const popoutDoc = selfRef.current.ownerDocument;
        const popoutWindow = popoutDoc.defaultView;
        popoutDoc.addEventListener("click", handler);
    };

    return <div ref={selfRef}>Content</div>;
}

Limitations

  • ⚠️ Context: Code runs in main window JS context
  • ⚠️ Events: Must use popout window/document for listeners
  • ⚠️ Throttling: Timers throttle when main window is hidden
  • ⚠️ Libraries: Third-party controls may not work without modification
  • ⚠️ Zoom: Incorrect sizing when browser is zoomed
  • ⚠️ State: Windows can't reload in maximized/minimized states

📖 Deep Dive: React Portals in Popups


🛠️ Development

Running Locally

# Install dependencies
pnpm install

# Start development server
pnpm dev

# Run tests (in separate terminal)
pnpm playwright

Building

# Build for distribution
pnpm build

The built files will be in the dist/ directory.


📚 Appendix

Layout Properties

Complete list of optional props for the <Layout> component:

JSON Model Schemas

TypeScript interfaces for all configuration objects:

Core Model

Node Types

Layout Component Methods

Programmatically add tabs using Layout component methods:

// Get reference to Layout component
const layoutRef = useRef();

// Add tab to specific tabset
layoutRef.current.addTabToTabSet("NAVIGATION", {
    type: "tab",
    component: "grid",
    name: "New Grid",
});

Complete Layout Methods API

Multi-Splitter Mode

Remove tabs entirely for a pure splitter interface:

const json = {
    global: {
        tabSetEnableTabStrip: false,
    },
    // ... rest of layout
};

Node ID Management

// Auto-generated IDs are UUIDs
const autoId = "#0c459064-8dee-444e-8636-eb9ab910fb27";

// Get node ID
const nodeId = node.getId();

// Use in actions
model.doAction(Actions.selectTab(nodeId));

Comparing Flexycakes with other React layout managers:

| Library | Repository | Key Features | | -------------- | ----------------------------------------------------------------------------- | ---------------------------------- | | Flexycakes | powerdragonfire/flexycakes | Enhanced FlexLayout with pin/unpin | | rc-dock | ticlo/rc-dock | Dock-style layouts | | Dockview | dockview.dev | Modern docking solution | | Lumino | jupyterlab/lumino | JupyterLab's layout system | | Golden Layout | golden-layout/golden-layout | Multi-window layouts | | React Mosaic | nomcopter/react-mosaic | Tiling window manager |

From FlexLayout to Flexycakes

Flexycakes is a drop-in replacement for FlexLayout with additional features:

  1. Update package:

    npm uninstall flexlayout-react
    npm install flexycakes
  2. Update imports:

    // Before
    import FlexLayout from "flexlayout-react";
    
    // After
    import { Layout, Model } from "flexycakes";
  3. Update CSS imports:

    // Before
    import "flexlayout-react/style/light.css";
    
    // After
    import "flexycakes/style/light.css";

New Features in Flexycakes

  • 📌 Pin/Unpin functionality for panels
  • 🎨 Enhanced UI components
  • ⚡ Improved workflow optimizations
  • 🔧 Additional customization options