Fractional Indexing
How CF2 uses fractional indexing to maintain element order within containers, enabling insertions between elements without reindexing.
Overview
Every element in a CF2 page tree has a fractionalIndex property that determines its position among siblings. This system allows inserting elements between any two existing elements without having to renumber all subsequent elements.
i
Library: CF2 fractional indexing is based on rocicorp/fractional-indexing
Basic Structure
Each element includes a fractionalIndex in its base properties:
JSON
{
"type": "Headline/V1",
"id": "6Z-HdLne-0",
"version": 0,
"parentId": "6Z-FlxCn-0",
"fractionalIndex": "a0"
}
Ordering Sequence
Fractional indices follow a lexicographic ordering pattern:
Sequence
a0 → a1 → a2 → ... → a9 → aA → aB → ... → aZ → b0 → b1 → ...
| Position | Index | Notes |
|---|---|---|
| First element | a0 |
Standard starting point |
| Second element | a1 |
Sequential increment |
| After a9 | aA |
Continues with uppercase letters |
| After aZ | b0 |
Increments first character |
| Between a0 and a1 | a0V |
Adds character to create midpoint |
Using the Library
Install the fractional-indexing library:
Bash
npm install fractional-indexing
Key Functions
| Function | Description |
|---|---|
generateKeyBetween(a, b) |
Generate a key between two existing keys |
generateNKeysBetween(a, b, n) |
Generate n keys between two existing keys |
Insertion Examples
Insert Between Elements
JavaScript
import { generateKeyBetween } from 'fractional-indexing';
// Insert between 'a0' and 'a1'
const newKey = generateKeyBetween('a0', 'a1');
// Returns 'a0V'
Insert at Beginning
JavaScript
import { generateKeyBetween } from 'fractional-indexing';
// Insert before 'a0' (at the beginning)
const firstKey = generateKeyBetween(null, 'a0');
// Returns 'Zz'
Insert at End
JavaScript
import { generateKeyBetween } from 'fractional-indexing';
// Insert after 'a9' (at the end)
const lastKey = generateKeyBetween('a9', null);
// Returns 'aA'
Insert Multiple Elements
JavaScript
import { generateNKeysBetween } from 'fractional-indexing';
// Insert 3 elements between 'a0' and 'a1'
const keys = generateNKeysBetween('a0', 'a1', 3);
// Returns ['a0G', 'a0V', 'a0l']
Practical Example
A container with three children in order:
JSON
{
"type": "FlexContainer/V1",
"id": "6Z-FlxCn-0",
"children": [
{
"type": "Headline/V1",
"id": "6Z-HdLn1-0",
"parentId": "6Z-FlxCn-0",
"fractionalIndex": "a0"
},
{
"type": "Paragraph/V1",
"id": "6Z-Prgph-0",
"parentId": "6Z-FlxCn-0",
"fractionalIndex": "a1"
},
{
"type": "Button/V1",
"id": "6Z-Buttn-0",
"parentId": "6Z-FlxCn-0",
"fractionalIndex": "a2"
}
]
}
To insert an Image between the Paragraph (a1) and Button (a2):
JavaScript
import { generateKeyBetween } from 'fractional-indexing';
const imageIndex = generateKeyBetween('a1', 'a2'); // 'a1V'
const newImage = {
type: "Image/V2",
id: "6Z-Image-0",
parentId: "6Z-FlxCn-0",
fractionalIndex: imageIndex // 'a1V'
};
The resulting order would be:
Order
Headline → a0
Paragraph → a1
Image → a1V (new element)
Button → a2
Sorting Elements
When rendering, sort children by their fractionalIndex:
JavaScript
// Sort children by fractionalIndex
const sortedChildren = children.sort((a, b) =>
a.fractionalIndex.localeCompare(b.fractionalIndex)
);
i
Tip: Fractional indices are designed to be compared lexicographically using standard string comparison.
Benefits
- No reindexing: Insert elements without updating sibling indices
- Conflict-free: Multiple users can insert elements simultaneously without conflicts
- Unlimited insertions: Can always generate a key between any two existing keys
- Simple comparison: Standard string comparison determines order