flexycakes
v1.3.3
Published
Community fork of caplin/FlexLayout (ISC). Not affiliated.
Maintainers
Readme
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.
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 flexycakesor using yarn:
yarn add flexycakesor using pnpm:
pnpm add flexycakesBasic Setup
Import the components:
import { Layout, Model } from "flexycakes";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" />Optional: For dynamic theming, see Theme Switching
Development Setup with Symbolic Linking
If you're contributing to Flexycakes or want to develop locally:
Clone the repository:
git clone https://github.com/powerdragonfire/flexycakes.git cd flexycakesInstall dependencies:
pnpm installCreate a symbolic link:
# Build the library first pnpm build # Create global link npm link # or with pnpm pnpm link --globalLink in your project:
cd /path/to/your/project npm link flexycakes # or with pnpm pnpm link --global flexycakesStart development mode:
# In flexycakes directory pnpm devThis 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
rootOrientationVerticalis 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
borderssection - Behavior: Similar to tabsets but docked to screen edges

💡 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 themedark.css- Dark mode themegray.css- Neutral gray themeunderline.css- Minimal underline stylerounded.css- Rounded corners themecombined.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
- HTML Host Page: Copy
popout.htmlto your public directory - Tab Configuration: Add
enablePopout: trueto tab definitions - 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 playwrightBuilding
# Build for distribution
pnpm buildThe 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",
});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:
Update package:
npm uninstall flexlayout-react npm install flexycakesUpdate imports:
// Before import FlexLayout from "flexlayout-react"; // After import { Layout, Model } from "flexycakes";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
