Getting Started
MosaicJS is a lightweight, event-driven drag-and-drop engine for the DOM. It focuses on predictability, rollback safety, and zero framework lock-in.
Installation
Install MosaicJS via npm:
npm install @whittakertech/mosaicMosaicJS ships as a standard ES module and works in modern browsers and bundlers.
Basic Usage
Create a Mosaic instance by providing a root element and a selector for draggable nodes.
import { Mosaic } from "@whittakertech/mosaic";
const mosaic = new Mosaic({
root: document.getElementById("root")!,
selectors: {
node: ".item"
}
});
mosaic.initialize();That’s it.
Once initialized, MosaicJS immediately begins managing drag interactions.
No framework required
MosaicJS works directly with the DOM.
It does not depend on React, Vue, Svelte, or any other framework. You can use it in plain HTML, legacy apps, or alongside any UI framework without adapters, hooks, or wrappers.
Required Markup
MosaicJS identifies draggable elements using data-mosaic-id.
Each draggable node must have a stable, unique identifier.
<div id="root">
<div class="item" data-mosaic-id="item-1">Item 1</div>
<div class="item" data-mosaic-id="item-2">Item 2</div>
<div class="item" data-mosaic-id="item-3">Item 3</div>
</div>Mosaic does not generate or mutate IDs for you — this guarantees deterministic behavior and safe rollback.
What MosaicJS Manages for You
Once initialized, MosaicJS automatically handles:
- Drag lifecycle state transitions
- Ghost element creation and positioning
- DOM snapshot creation before mutations
- Safe rollback when constraints fail
- Constraint evaluation and mutation validation
- Predictable state emission via global events
MosaicJS does not impose styling, animation, or layout decisions. Those are entirely under your control.
Common Pitfalls
Missing data-mosaic-id
Every draggable element must have a stable data-mosaic-id.
If IDs are missing, duplicated, or change between renders, MosaicJS cannot guarantee correct behavior or safe rollback.
Mutating the DOM during a drag
Avoid modifying the DOM manually while a drag is active.
MosaicJS maintains internal snapshots and state transitions. External mutations during a drag can invalidate rollback assumptions.
If you need custom behavior, use lifecycle events instead.
Expecting MosaicJS to style elements
MosaicJS does not inject CSS.
Classes like mosaic--active, mosaic--ghost, and mosaic--drop-target are hooks, not visual styles.
You are expected to define all visuals yourself.
Treating events as callbacks
MosaicJS emits global, descriptive events, not inline callbacks.
These events are designed for:
- logging
- analytics
- validation
- orchestration
They are not intended to directly mutate DOM state.
Initializing multiple times
Calling initialize() more than once on the same instance will lead to duplicate listeners and undefined behavior.
Create one instance per root and initialize it once.
When Not to Use MosaicJS
MosaicJS is intentionally low-level and opinionated about correctness. It may not be the right tool in these situations:
You want a drop-in UI component with prebuilt styles and animations
(MosaicJS provides behavior only, not visuals)Your drag state lives entirely inside a framework’s virtual DOM
and you don’t want DOM-level eventsYou need cross-window, cross-iframe, or OS-level dragging
You want automatic ID generation or implicit element tracking
Your use case does not require rollback, constraints, or deterministic behavior
If you need a quick sortable list with minimal setup and no guarantees, a higher-level UI library may be a better fit.
MosaicJS is designed for situations where correctness matters more than convenience.
Next Steps
From here, you can explore MosaicJS in increasing depth:
- 👉 Interactive Demos
- 👉 Core Concepts Demos
- 👉 Drag Lifecycle Guide
- 👉 Constraints Design Guide
- 👉 CSS Class Contract
These sections build on each other, but you can jump to any one depending on what you need.