> For the complete documentation index, see [llms.txt](https://docs.rowsncolumns.app/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.rowsncolumns.app/configuration/features/named-ranges.md).

# Named ranges

Named ranges let you give a friendly name to a cell range, scalar value, formula, or LAMBDA — and reference it from any formula. Spreadsheet supports four shapes that all interop with Excel:

| Shape             | Example                    | Use it as                                                  |
| ----------------- | -------------------------- | ---------------------------------------------------------- |
| **Range-typed**   | `MyRange = A1:B10`         | `=SUM(MyRange)`                                            |
| **Static value**  | `MyVal = 42`               | `=MyVal*2` — scalar returned directly                      |
| **Formula-typed** | `MyVar = =SUM(A1:A10)`     | `=MyVar` — virtual cell, recomputes when precedents change |
| **Named LAMBDA**  | `MyFunc = =LAMBDA(x, x*2)` | `=MyFunc(5)` returns 10                                    |

## Basic usage

Pass `namedRanges` to `useSpreadsheetState` + `CanvasGrid`. The hook returns CRUD callbacks the UI can wire to:

```tsx
import {
  SpreadsheetProvider,
  CanvasGrid,
  NamedRange,
} from "@rowsncolumns/spreadsheet";
import { useState } from "react";
import {
  useSpreadsheetState,
  NamedRangeEditor,
} from "@rowsncolumns/spreadsheet-state";

const MySpreadsheet = () => {
  const [namedRanges, onChangeNamedRanges] = useState<NamedRange[]>([]);
  const {
    onCreateNamedRange,
    onUpdateNamedRange,
    onDeleteNamedRange,
    onRequestDefineNamedRange,
    onRequestUpdateNamedRange,
  } = useSpreadsheetState({
    namedRanges,
    onChangeNamedRanges,
  });

  return (
    <>
      <CanvasGrid namedRanges={namedRanges} />
      <NamedRangeEditor
        namedRanges={namedRanges}
        onCreateNamedRange={onCreateNamedRange}
        onUpdateNamedRange={onUpdateNamedRange}
        onDeleteNamedRange={onDeleteNamedRange}
      />
    </>
  );
};

const App = () => (
  <SpreadsheetProvider>
    <MySpreadsheet />
  </SpreadsheetProvider>
);
```

The `NamedRangeEditor` component handles all four shapes — the user picks "Text or Formula" in the editor, and the engine resolves the type at evaluation time.

## Scoping — workbook vs sheet

Each name is either workbook-scoped (visible everywhere) or sheet-scoped (visible only on that sheet). When a sheet-scoped name shadows a workbook-scoped one with the same display name, Excel's lookup rules apply: **sheet-scope wins first**, then falls back to workbook scope.

```ts
const namedRanges: NamedRange[] = [
  // Workbook-scoped — visible everywhere
  { namedRangeId: "1", name: "Tax", value: "=0.08" },
  // Sheet-scoped — only on sheetId 1; overrides Tax on that sheet
  { namedRangeId: "2", name: "Tax", value: "=0.20", sheetId: 1 },
];

// On sheet 1: `=Tax` → 0.20
// On any other sheet: `=Tax` → 0.08
```

## Formula-typed names — the virtual-cell model

When `value` is a formula (`=SUM(A1:A10)`, `=price * 1.08`, etc.), the name becomes a first-class node in the calculation DAG. Cell formulas that reference the name get a real DAG input edge to that node, so the unified topological sort recomputes them whenever the named range's value changes.

```ts
const namedRanges: NamedRange[] = [
  { namedRangeId: "1", name: "price", value: "=A1" },
  { namedRangeId: "2", name: "fullPrice", value: "=price * 1.08" },
];

// Cell B1: =fullPrice
// When A1 changes → price recomputes → fullPrice recomputes → B1 recomputes
```

Order of registration doesn't matter — the engine does a two-pass batch register so `fullPrice` can reference `price` even if it's listed first.

## Named LAMBDAs — callable names

Set `value` to a LAMBDA expression and the engine treats the name as a callable function:

```ts
const namedRanges: NamedRange[] = [
  {
    namedRangeId: "1",
    name: "Commission",
    value: "=LAMBDA(sales, IF(sales > 1000, sales*0.1, sales*0.05))",
  },
];

// =Commission(500)  → 25
// =Commission(5000) → 500
```

Three resolution shapes are accepted on `onVariable`: a pre-built lambda value, a `{ref, value}` wrapper, or the raw `"=LAMBDA(...)"` source string (parsed + applied at call time). The fast-formula-parser engine handles all three identically.

## JSON round-trip

The DAG including all name → dependent edges is serializable. `Dag.toJSON` and `Dag.fromJSON` round-trip both directions of the cell ↔ named-range edges, so a rehydrated calculator matches the in-memory model exactly.
