react-mosaic-ui
v1.0.1
Published
A modern React tiling window manager with FSD architecture, Rollup bundling, and Tailwind CSS v4
Maintainers
Readme
React Mosaic UI
A modern React tiling window manager built with FSD architecture, TypeScript, and Tailwind CSS v4.
Inspired by react-mosaic
📚 Documentation
Core Guides
- Claude Guide: Main guide for working with Claude Code
- FSD Architecture: Feature-Sliced Design architecture details
- Naming Convention: File, variable, and function naming rules
- Clean Code Guide: Clean code principles and patterns
- TypeScript Guide: Type definitions and TypeScript best practices
- Testing Guide: Test writing and
.test.tsfile rules
🏗️ Project Structure
src/
├── shared/ # Reusable types, utilities, UI
│ ├── types/ # Common type definitions
│ ├── lib/ # Utility functions
│ └── ui/ # Basic UI components
├── entities/ # Business entities
│ ├── mosaic/ # Mosaic main component
│ └── window/ # MosaicWindow component
├── features/ # Business features
│ ├── drag-drop/ # Drag and drop
│ ├── resize/ # Resizing
│ └── window-controls/ # Window controls
└── widgets/ # Complex UI blocks🎯 Core Principles
1. File Naming
- Kebab-case:
user-profile.tsx,add-to-cart.ts - Test files:
my-component.test.ts - Type definitions:
my-component.types.ts
2. FSD Layers
shared → entities → features → widgets3. Tailwind CSS v4
- Prefix:
rm-(react-mosaic) - Scoped: Only applied within
.react-mosaicclass - CSS Variables: User customizable
🚀 Getting Started
Installation
bun installDevelopment
# Build library
bun run build
# Run example (recommended)
cd example && bun install && bun run dev
# Run tests
bun run test
# Type checking
bun run typecheckExample App
The example app runs in a separate directory:
cd example
bun install
bun run devOpen http://localhost:5173 in your browser.
📦 Usage
Basic Usage
import { Mosaic, MosaicWindow, type MosaicNode } from 'react-mosaic-ui';
import 'react-mosaic-ui/styles.css';
type ViewId = 'a' | 'b' | 'c';
function App() {
const [tree, setTree] = useState<MosaicNode<ViewId>>({
direction: 'row',
first: 'a',
second: {
direction: 'column',
first: 'b',
second: 'c',
},
});
return (
<Mosaic<ViewId>
renderTile={(id, path) => (
<MosaicWindow path={path} title={`Window ${id}`}>
<div>Content for {id}</div>
</MosaicWindow>
)}
value={tree}
onChange={setTree}
/>
);
}Advanced Usage
import {
Mosaic,
MosaicWindow,
createBalancedTreeFromLeaves,
getLeaves,
} from 'react-mosaic-ui';
function App() {
const [tree, setTree] = useState<MosaicNode<string> | null>(null);
const createNode = () => `window-${Date.now()}`;
const autoArrange = () => {
if (!tree) return;
const leaves = getLeaves(tree);
const balanced = createBalancedTreeFromLeaves(leaves);
setTree(balanced);
};
return (
<Mosaic
renderTile={(id, path) => (
<MosaicWindow
path={path}
title={id}
createNode={createNode}
onDragStart={() => console.log('Drag started')}
onDragEnd={(type) => console.log('Drag ended:', type)}
additionalControls={
<div>
<button onClick={() => alert('Custom action')}>
Custom Action
</button>
</div>
}
>
<div>Window: {id}</div>
</MosaicWindow>
)}
value={tree}
onChange={setTree}
/>
);
}Drag and Drop Events
<MosaicWindow
path={path}
title="Window"
onDragStart={() => {
console.log('Window drag started');
}}
onDragEnd={(type) => {
// type: 'drop' | 'reset'
console.log('Window drag ended:', type);
}}
>
<div>Content</div>
</MosaicWindow>Additional Controls (Drawer Menu)
<MosaicWindow
path={path}
title="Window"
additionalControls={
<div>
<button onClick={() => console.log('Action 1')}>Action 1</button>
<button onClick={() => console.log('Action 2')}>Action 2</button>
</div>
}
>
<div>Content</div>
</MosaicWindow>🎨 Style Customization
You can customize the theme using CSS variables:
:root {
--rm-border-color: #cbd5e1;
--rm-background: #ffffff;
--rm-window-bg: #f8fafc;
--rm-toolbar-bg: #f1f5f9;
--rm-split-color: #94a3b8;
--rm-split-hover: #64748b;
--rm-split-size: 4px;
--rm-toolbar-height: 40px;
}🔧 API
Mosaic Component
interface MosaicProps<T> {
renderTile: (id: T, path: MosaicPath) => JSX.Element;
value?: MosaicNode<T> | null;
initialValue?: MosaicNode<T> | null;
onChange?: (node: MosaicNode<T> | null) => void;
onRelease?: (node: MosaicNode<T> | null) => void;
className?: string;
zeroStateView?: JSX.Element;
mosaicId?: string;
createNode?: () => T | Promise<T>;
}MosaicWindow Component
interface MosaicWindowProps<T> {
title: string;
path: MosaicPath;
children: ReactNode;
createNode?: () => T | Promise<T>;
draggable?: boolean;
toolbarControls?: ReactNode;
additionalControls?: ReactNode;
renderToolbar?: (props: MosaicWindowToolbarProps<T>, defaultToolbar: ReactNode) => ReactNode;
onDragStart?: () => void;
onDragEnd?: (type: 'drop' | 'reset') => void;
className?: string;
}Utility Functions
// Tree manipulation
getLeaves(node: MosaicNode<T>): T[]
getNodeAtPath(node: MosaicNode<T>, path: MosaicPath): MosaicNode<T> | null
createBalancedTreeFromLeaves(leaves: T[]): MosaicNode<T> | null
// Tree updates
updateTree(root: MosaicNode<T>, updates: MosaicUpdate<T>[]): MosaicNode<T>
createRemoveUpdate(root: MosaicNode<T>, path: MosaicPath): MosaicUpdate<T>
createExpandUpdate(path: MosaicPath, percentage?: number): MosaicUpdate<T>🛠️ Tech Stack
- React 18+: UI library
- TypeScript 5: Type safety
- Rollup: Bundler
- Tailwind CSS v4: Styling (prefix:
rm-) - React DnD: Drag and drop
- Immer: Immutable state updates
- Vitest: Testing
- Bun: Package manager
📋 Features
✅ Modern React: React 18+ support
✅ TypeScript: Full type safety
✅ FSD Architecture: Scalable structure
✅ Tailwind CSS v4: Conflict-free styling (rm- prefix)
✅ Tree Structure: Flexible layouts
✅ Drag and Drop: Intuitive UI based on React DnD
✅ Built-in Controls: Replace, Split, Expand, Remove buttons
✅ Additional Controls: Drawer menu via additionalControls
✅ Drag Events: onDragStart, onDragEnd hooks
✅ Customization: Theme via CSS variables, full customization via renderToolbar
✅ Controlled/Uncontrolled: Both modes supported
🤝 Contributing
This project strictly follows FSD architecture and clean code principles. Before contributing, please review the Clean Code Guide and FSD Architecture.
Git Workflow
This project uses Husky for git hooks and follows conventional commit standards.
Commit Message Format
All commits must follow the conventional commit format:
type(scope?): subject
Examples:
feat: add window resize feature
fix(mosaic): resolve drag and drop issue
docs: update READMEAllowed types:
feat: New featurefix: Bug fixdocs: Documentation changesstyle: Code style changes (formatting, missing semicolons, etc.)refactor: Code refactoringtest: Adding or updating testschore: Maintenance tasksperf: Performance improvementsci: CI/CD changesbuild: Build system changesrevert: Revert previous commit
Pre-commit Hooks
Before each commit, the following checks run automatically:
- Linting (
bun run lint) - Type checking (
bun run typecheck) - Tests (
bun test)
If any check fails, the commit will be blocked.
Release Process
This project uses release-it for automated releases.
Creating a Release
# Patch release (1.0.0 → 1.0.1)
bun run release:patch
# Minor release (1.0.0 → 1.1.0)
bun run release:minor
# Major release (1.0.0 → 2.0.0)
bun run release:major
# Interactive release (choose version)
bun run release
# Dry run (test without publishing)
bun run release:dryThe release process will:
- Run all checks (lint, typecheck, tests)
- Build the project
- Update version in package.json
- Generate/update CHANGELOG.md
- Create a git tag
- Push to GitHub
- Create a GitHub release
- Publish to npm
📄 License
MIT
