A modern React tiling window manager with FSD architecture, Rollup bundling, and Tailwind CSS v4
npm install react-mosaic-uiA modern React tiling window manager built with FSD architecture, TypeScript, and Tailwind CSS v4.
> Inspired by react-mosaic
.test.ts file rules```
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
, add-to-cart.ts
- Test files: my-component.test.ts
- Type definitions: my-component.types.ts$3
`
shared ā entities ā features ā widgets
`$3
- Prefix: rm- (react-mosaic)
- Scoped: Only applied within .react-mosaic class
- CSS Variables: User customizableš Getting Started
$3
`bash
bun install
`$3
`bash
Build library
bun run buildRun example (recommended)
cd example && bun install && bun run devRun tests
bun run testType checking
bun run typecheck
`$3
The example app runs in a separate directory:
`bash
cd example
bun install
bun run dev
`Open
http://localhost:5173 in your browser.š¦ Usage
$3
`typescript
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>({
direction: 'row',
first: 'a',
second: {
direction: 'column',
first: 'b',
second: 'c',
},
});
return (
renderTile={(id, path) => (
Window ${id}}>
Content for {id}
)}
value={tree}
onChange={setTree}
/>
);
}
`$3
`typescript
import {
Mosaic,
MosaicWindow,
createBalancedTreeFromLeaves,
getLeaves,
} from 'react-mosaic-ui';function App() {
const [tree, setTree] = useState | null>(null);
const createNode = () =>
window-${Date.now()}; const autoArrange = () => {
if (!tree) return;
const leaves = getLeaves(tree);
const balanced = createBalancedTreeFromLeaves(leaves);
setTree(balanced);
};
return (
renderTile={(id, path) => (
path={path}
title={id}
createNode={createNode}
onDragStart={() => console.log('Drag started')}
onDragEnd={(type) => console.log('Drag ended:', type)}
additionalControls={
}
>
Window: {id}
)}
value={tree}
onChange={setTree}
/>
);
}
`$3
`typescript
path={path}
title="Window"
onDragStart={() => {
console.log('Window drag started');
}}
onDragEnd={(type) => {
// type: 'drop' | 'reset'
console.log('Window drag ended:', type);
}}
>
Content
`$3
`typescript
path={path}
title="Window"
additionalControls={
}
>
Content
`šØ Style Customization
You can customize the theme using CSS variables:
`css
: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
$3
`typescript
interface MosaicProps {
renderTile: (id: T, path: MosaicPath) => JSX.Element;
value?: MosaicNode | null;
initialValue?: MosaicNode | null;
onChange?: (node: MosaicNode | null) => void;
onRelease?: (node: MosaicNode | null) => void;
className?: string;
zeroStateView?: JSX.Element;
mosaicId?: string;
createNode?: () => T | Promise;
}
`$3
`typescript
interface MosaicWindowProps {
title: string;
path: MosaicPath;
children: ReactNode;
createNode?: () => T | Promise;
draggable?: boolean;
toolbarControls?: ReactNode;
additionalControls?: ReactNode;
renderToolbar?: (props: MosaicWindowToolbarProps, defaultToolbar: ReactNode) => ReactNode;
onDragStart?: () => void;
onDragEnd?: (type: 'drop' | 'reset') => void;
className?: string;
}
`$3
`typescript
// Tree manipulation
getLeaves(node: MosaicNode): T[]
getNodeAtPath(node: MosaicNode, path: MosaicPath): MosaicNode | null
createBalancedTreeFromLeaves(leaves: T[]): MosaicNode | null// Tree updates
updateTree(root: MosaicNode, updates: MosaicUpdate[]): MosaicNode
createRemoveUpdate(root: MosaicNode, path: MosaicPath): MosaicUpdate
createExpandUpdate(path: MosaicPath, percentage?: number): MosaicUpdate
`š ļø 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.
$3
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?): subjectExamples:
feat: add window resize feature
fix(mosaic): resolve drag and drop issue
docs: update README
`Allowed types:
-
feat: New feature
- fix: Bug fix
- docs: Documentation changes
- style: Code style changes (formatting, missing semicolons, etc.)
- refactor: Code refactoring
- test: Adding or updating tests
- chore: Maintenance tasks
- perf: Performance improvements
- ci: CI/CD changes
- build: Build system changes
- revert: Revert previous commit#### Pre-commit Hooks
Before each commit, the following checks run automatically:
1. Linting (
bun run lint)
2. Type checking (bun run typecheck)
3. Tests (bun test)If any check fails, the commit will be blocked.
$3
This project uses release-it for automated releases.
#### Creating a Release
`bash
Patch release (1.0.0 ā 1.0.1)
bun run release:patchMinor release (1.0.0 ā 1.1.0)
bun run release:minorMajor release (1.0.0 ā 2.0.0)
bun run release:majorInteractive release (choose version)
bun run releaseDry run (test without publishing)
bun run release:dry
``The release process will:
1. Run all checks (lint, typecheck, tests)
2. Build the project
3. Update version in package.json
4. Generate/update CHANGELOG.md
5. Create a git tag
6. Push to GitHub
7. Create a GitHub release
8. Publish to npm
MIT