Generate lexicographically ordered indexes for inserting items between existing ones — without reindexing your entire list. Built for collaborative apps, drag-and-drop, and real-time sync.
A focused, battle-tested library that handles the hard parts of fractional indexing so you don't have to.
Tested against edge cases including small gaps, bulk insertions, and move operations under high-frequency concurrent load.
Multiple validation layers prevent range violations. Stays within valid bounds and produces moderate-sized index values with bounded jitter.
Efficient insertion and relocation of multiple items in a single call with generateBulkIndexes and generateRelocationIndexes.
Handles high-frequency collaborative editing reliably. 100% reliability under 50+ concurrent operations.
Lightweight with no external dependencies. Works with Node.js (ES6+) and browsers (ES5 compatible build included).
Full type definitions included out of the box. Drop it into any TypeScript project with no extra setup required.
Import the functions you need and start generating indexes immediately.
const { generateFractionalIndex, generateBulkIndexes } = require('frac-indexes'); // Create first item in an empty list const first = generateFractionalIndex(null, null); // → "0.0005489960" // Add item at the end const last = generateFractionalIndex(first, null); // → "0.0016129989" // Insert between two items const middle = generateFractionalIndex(first, last); // → "0.001092958115464" // Bulk insert 5 items at once const bulk = generateBulkIndexes(first, last, 5);
<!-- Include the ES5 compatible build --> <script src="node_modules/frac-indexes/src/es5-indexing.js"></script> <script> // All functions under FractionalIndexing namespace var first = FractionalIndexing .generateFractionalIndex(null, null); var last = FractionalIndexing .generateFractionalIndex(first, null); var middle = FractionalIndexing .generateFractionalIndex(first, last); </script> // Or with Node.js ES5: var fi = require('frac-indexes/src/es5-indexing'); var idx = fi.generateFractionalIndex(null, null);
A focused API with clear, predictable behavior and full TypeScript support.
Generates a single fractional index that sorts between two existing indexes. Pass null for either boundary to indicate the start or end of the list.
| Parameter | Type | Description |
|---|---|---|
| prevIndex | string | null | The index of the item before the desired position. Pass null to insert at the beginning. |
| nextIndex | string | null | The index of the item after the desired position. Pass null to insert at the end. |
Generates multiple fractional indexes between two existing indexes in a single efficient call. Items are sequentially spaced within the gap.
| Parameter | Type | Description |
|---|---|---|
| prevIndex | string | null | The lower boundary index (or null for start of list). |
| nextIndex | string | null | The upper boundary index (or null for end of list). |
| count | number | Number of indexes to generate. Returns an empty array if count ≤ 0. |
Generates indexes for moving multiple items to a new position. Supports both evenly-distributed and sequential placement strategies.
| Parameter | Type | Description |
|---|---|---|
| targetPrevIndex | string | null | Index before the destination position. |
| targetNextIndex | string | null | Index after the destination position. |
| count | number | Number of items being relocated. |
| distributeEvenly | boolean | If true (default), items are spread evenly across the target gap. If false, uses sequential bulk insertion. |
Common patterns you can drop straight into your application.
const tasks = [ { id: 1, index: generateFractionalIndex(null, null), title: "First task" }, { id: 2, index: generateFractionalIndex(tasks[0].index, null), title: "Second task" } ]; // Insert between existing tasks const newIdx = generateFractionalIndex( tasks[0].index, tasks[1].index );
function insertMultipleItems(prev, next, items) { const indexes = generateBulkIndexes( prev?.index || null, next?.index || null, items.length ); return items.map((item, i) => ({ ...item, index: indexes[i] })); }
function moveItems(moving, target) { const newIndexes = generateRelocationIndexes( target.previous?.index || null, target.next?.index || null, moving.length ); return moving.map((item, i) => ({ ...item, index: newIndexes[i] })); }
Precise math under the hood to keep your indexes always valid and always ordered.
All indexes are high-precision decimal strings formatted so string comparison always matches numeric order.
Random variation is capped at 25% of the available gap on each side of the midpoint — so boundaries are never crossed.
Uses 15+ decimal places to gracefully handle microscopic gaps down to floating-point precision limits.
Multiple validation layers with IEEE 754 safety margins catch edge cases and fall back to safe midpoints automatically.
Verified against the scenarios that break most fractional indexing implementations.
Ensures small gaps never produce out-of-order indexes.
Survives 15+ sequential subdivisions of the same gap.
Handles gaps down to the floating-point precision limit.
100% reliability under 50+ concurrent operations.