Something went wrong

Thank you for being patient! We're working hard on resolving the issue

Row Keys - Lona Docs Log in

Row Keys

Row keys are the stable identifiers for rows. Use them when a label is too human-facing or too likely to change.

Most app code uses a row key in three places:

  • opening a known row such as weather or tasks
  • storing a durable reference to a row
  • resolving a row into the current sheet or UI session

Key Formats

KindExampleUse
User rowr_0123456789abcdef0123456789abcdefA normal server-backed row
Lookup key:revenueA user-managed stable name
Reserved key:~weatherA builtin / canonical-provider row (see below)
Virtual row`virtual(r_0123456789abcdef0123456789abcdeflogs-0)`
System rowsystem(header)Framework-owned structure
Pending rowpending(0123456789abcdef0123456789abcdef)A local row that has not been persisted yet

The SDK accepts a few legacy forms when parsing, but these are the canonical forms you should expect to see in new code and serialized data.

Creating And Parsing Keys

import { RowKey } from "@tento-lona/sheets";

const userRow = RowKey.parse("r_0123456789abcdef0123456789abcdef");
const lookup = RowKey.lookup("revenue");
const builtin = RowKey.reserved("weather");
const system = RowKey.system("header");

RowKey.parse() is the right choice when you already have a string. The factory methods are clearer when you are constructing a key in code.

RowKey is canonically exported from @tento-lona/sheets. The SDK (@tento-lona) re-exports it for convenience, so both import paths work; prefer @tento-lona/sheets when writing library-layer code.

Lookup Keys And Reserved Keys

Lookup keys and reserved keys are both name-based, but they serve different purposes:

  • :name is user-managed
  • :~name is framework-managed ("reserved" / "canonical-provider" key)

Use a lookup key when you want a row to be referenced by a meaningful name such as :revenue or :resting-heart-rate.

Use a reserved key when you want a framework-owned canonical row such as :~weather or another integration-backed row that the app owns.

Canonical Provider Routing (:~)

Reserved keys are special: they point at sheet-invariant canonical rows. The same :~weather:{locationId} row is identical whether you open it from your home sheet or your vacation sheet — the cells come from a single canonical store, not per-sheet sourceCellData.

This matters for two reasons:

  1. Deduplication. Multiple sheets sharing a Client see the same canonical cells. Fetches are coalesced by SheetsAccessor's shared canonicalCells store.
  2. Routing. The SDK's CanonicalBackend intercepts reads for :~-keyed rows and routes them to a provider-aware fetch path (weather sync, Google Calendar sync, Garmin sync, etc.) instead of the normal row-by-id path.

Common canonical prefixes:

Prefix patternWhat it names
:~weather:{locationId}Daily weather for a location
:~google:{authId}:calendar:{calendarId}A Google Calendar
:~garmin:{metric}A Garmin-sourced metric (sleep, resting HR, …)
:~whoop:{metric}A Whoop-sourced metric

You generally don't mint these keys yourself — the SDK builds them from auth bindings and provider metadata. You just open the row:

const weather = sheet.row({ lookupKey: "[weather@lona.so]:~weather:2550002" });

See Row Roles & Facades for facade access to these rows (e.g. weather?.as(WEATHER_FACADE)).

Keys vs Labels

Labels are for people. Keys are for code.

  • labels are great for UI and quick queries
  • keys are better for persistence, formulas, and integrations
  • if a row must still be findable after being renamed, use a key
const row = sheet.row({ lookupKey: ":revenue" });

Resolving A Key

A RowKey can be resolved into a RowLocalId, which is the in-memory identity used by trees, maps, and UI state:

const key = RowKey.lookup("revenue");
const rowId = key.resolve();

See Row Identity for when to use RowKey versus RowLocalId.

See Also