1---2title: "The .012 format"3sidebar_position: 44---56# The .012 format78`.012` files are JSON with one line per cell, keyed by address, so diffs9read naturally and files merge reasonably in version control. This page10is the format at version 7, the newest; [Versions](#versions) says what11each version adds and what 012 promises about old files. A cell12without formatting is just what was typed; a formatted cell is a small13object:1415```json16{17 "version": 2,18 "widths": {"A": 14},19 "cells": {20 "A1": "Rent",21 "B1": {"input":"1450","format":"currency","decimals":2},22 "B2": {"input":"95","note":"Due on the 1st"},23 "B3": "=SUM(B1:B2)"24 }25}26```2728A formatted cell has its `input` and the formatting that isn't the29default: `format` (as `number_format` in30[macros](../reference/macro-api.md#cells) names it: `currency`, `percent`, `date`, ...), `decimals`, `pattern` for a31custom format, `bold`, `italic`, `underline`, `strikethrough`,32`align` (`left`, `center`, `right`), `valign` (`top`, `middle`,33`bottom`), `wrap` (`wrap` or `clip`) and `borders`, each edge's line by34name, with `topColor`, `bottomColor`, `leftColor` and `rightColor` for35a line in a [color](../sheets/formatting.md#border-colors):36`"borders":{"top":"thin","bottom":"double","left":"thick","right":"thin","bottomColor":"red"}`37([formatting](../sheets/formatting.md#borders)).3839Only what's typed is saved. What 012 computes is computed again when the40file opens: formula results, [pivot tables](#pivot-tables)' results, and41the arrays formulas [spill](../formulas/arrays.md), whose cells are kept42only for their formatting and notes, and the rows of43[regions](#regions), made again by their commands or read again from44their files.4546## Reading and writing4748Any JSON with the same fields reads the same: the order of keys,49spacing, and the case of field names don't matter. A cell given twice50takes its last entry, though an invalid earlier one is refused; the51`version` or the `sheets` list given twice around cells is refused.5253012 writes one form of each workbook: the fields in the order this page54shows them, cells by row and then column, widths and heights by55position, column and row formats with the whole sheet's first, then56columns', then rows', names by name, and charts, rules, regions and57macros in the order they were made. Saving a file 012 wrote, without58changing it, gives back the same bytes.5960012 reads and writes the file as a stream, each cell straight into or61out of its sheet, so opening a file takes about the memory of the62workbook however large the file; what that costs, and why there is no63binary format beside this one, is in64[Bounds of support](../contributing/limits.md#the-012-file).6566## Versions6768012 writes the lowest version that holds the workbook, so older builds69open what they can, and refuses a version newer than it knows rather70than lose sheets or pivots. Version 1 files (cells as plain strings)71still load.7273| Version | Written when | Adds |74|---|---|---|75| 2 | One sheet, using none of the below | `cells`, `widths` |76| 3 | A sheet has named ranges, frozen panes or a filter | `names` (each name's range, `#REF!` once its cells were deleted), `freeze` (`rows`, `cols`), `filter` (its `range`, and `columns` by letter, each with `hidden` values or a `condition` and its `value`) |77| 4 | Several sheets, or a formula naming a sheet | `sheets`, a list; see [Several sheets](#several-sheets) |78| 5 | A pivot table | a sheet's `pivot`; see [Pivot tables](#pivot-tables) |79| 6 | A table | a sheet's `tables`; see [Tables](#tables) |80| 7 | A linked source | a sheet whose region is `paged`; see [Linked sources](#linked-sources) |8182What stays the same from release to release:8384- Every file a release saved opens in every later build, and saving it85 unchanged gives back its bytes. Each release keeps workbooks it saved86 as fixtures (`internal/sheet/testdata/fixtures/<release>`), which every87 build's tests open and save again88 ([Releasing](../contributing/releasing.md#file-fixtures)).89- Fields are only added, never renamed or given another meaning; the90 version rises only for a field older builds can't do without, and the91 rest are listed under [Fields that need no version](#fields-that-need-no-version).92- An older build ignores a field it doesn't know, but refuses a value it93 doesn't know in a field it does (a chart type, a rule's condition such94 as a date period), rather than open the file with the rule or chart95 quietly changed.9697## Fields that need no version9899Builds that don't know these fields ignore them (and drop them if they100save), so they raise no version:101102| Field | On | Holds |103|---|---|---|104| `note` | a cell | Its [note](../sheets/notes-protection.md#notes) |105| `own` | a cell | `true` when its formatting is its own, not its column's or row's (Automatic in a currency column) |106| `text` | a cell | How its `input` reads when its `format` would say otherwise: `true` for text typed into a Plain text cell whose format changed since (`{"input":"007","text":true}`), `false` for a number or formula typed before the cell was formatted as Plain text; either stays as typed, as in Sheets. Older builds read the input as the format says |107| `wrap`, `borders` | a cell or a line | How its text [wraps](../sheets/formatting.md#wrapping) and its [borders](../sheets/formatting.md#borders); older builds show the text overflowing, without lines |108| `valign`, and colors in `borders` | a cell or a line | Where its text sits in a [tall row](../sheets/formatting.md#vertical-alignment), and its borders' colors; older builds show the text at the bottom and the lines in the text's ink |109| `lines` | a sheet | [Column and row formats](#column-and-row-formats) |110| `heights` | a sheet | [Row heights](../sheets/formatting.md#row-heights) set by hand, in lines, by row number: `"heights": {"3": 2, "7": 4}` |111| `merges` | a sheet | [Merged cells](../sheets/formatting.md#merged-cells), by range: `"merges": ["A1:C1", "D2:D5"]` |112| `name`, `hidden` | a sheet | Its name when renamed; `true` when hidden (a file whose sheets are all hidden opens with the first one shown) |113| `charts` | a sheet | One chart per line: `type` (`column`, `bar`, `line`, `pie`, `area`, `scatter`), `data`, `at`, `width`, `height`, `byRow`, `header`, `labels`, `title`, and options left out at their defaults: `stack` (`stacked`, `percent`), `trend`, `min`, `max`, `log`, `gridlines` (only when off), `legend` (`right`, `none`). A build that charts but lacks a chart's type refuses the file |114| `protected` | a sheet | [Protected ranges](../sheets/notes-protection.md#protected-sheets-and-ranges): `{"range":"B2:C9","description":"Totals"}`, or `{"sheet":true}` |115| `conditionalFormats`, `validations` | a sheet | [Rules](#conditional-formats-and-data-validation), one per line |116| `arithmetic` | the workbook | `decimal` for [decimal arithmetic](../formulas/decimal.md) |117| `locale` | the workbook | The [locale](../sheets/locale.md) it's typed and shown in (`"de-DE"`), when File > Settings > Locale chose one; without it, the file follows the reader's `locale` setting. Cells are stored the same way in every locale: `input` is always as typed in en-US (`1,234.5`, `9/26/2026`, `=ROUND(A1,2)`) |118| `macros`, `macroOrigin` | the workbook | Macros as Starlark scripts, and the computer they were made or trusted on (macros', notebook cells' and [linked files'](following.md#files-from-elsewhere) alike): see [Macro scripting API](../reference/macro-api.md#in-the-file). Opening a file never runs them |119| `regions` | a sheet | Its [linked files](following.md) and notebook cells' [outputs sent there](../nushell/notebooks.md#send-to-a-sheet): see [Regions](#regions). Older builds open the sheet without them |120| `tab`, `reactive`, `notebookCells` | a sheet | A [notebook](../nushell/notebooks.md) tab and its cells: see [Notebooks](#notebooks). Older builds open it as an empty sheet |121122## Column and row formats123124[Formats of whole columns and rows](../sheets/editing.md#rows-and-columns) are kept125in a `lines` field after the widths, runs of lines with the same126formatting together; `A:XFD` is the whole sheet's format:127128```json129 "lines": {"A:XFD": {"italic":true}, "B:D": {"format":"currency","decimals":2}, "1:1": {"bold":true}},130```131132## Several sheets133134In version 4 the sheets are a list, each with its name and the fields a135version 3 file has at the top, and named ranges say their sheet. The136workbook's settings (named ranges, decimal arithmetic, the locale, the sheet shown137when saved as `active`) stay at the top:138139```json140{141 "version": 4,142 "names": {"Rent": "Budget!B1"},143 "active": 1,144 "sheets": [145 {146 "name": "Budget",147 "cells": {148 "A1": "Rent",149 "B1": "1450"150 }151 },152 {153 "name": "Q3 plan",154 "hidden": true,155 "cells": {156 "A1": "=Budget!B1*3"157 }158 }159 ]160}161```162163## Pivot tables164165A pivot's sheet has a `pivot` field, on one line after its cells,166holding the definition. The results are never saved; they are computed167again when the file opens, so the file stays small and can't disagree168with its data.169170```json171{172 "name": "Pivot Table 1",173 "cells": {},174 "pivot": {"source":"Sales!A1:D200","rows":[{"column":"B"},{"column":"A","order":"desc","sortBy":1}],"columns":[{"column":"C"}],"values":[{"column":"D","summarize":"sum"},{"column":"D","summarize":"counta","showAs":"percent_of_total","name":"Share"}],"filters":[{"column":"A","hidden":["North"]}],"rowTotals":true,"columnTotals":false}175}176```177178- `source` is the data, with its sheet; `Sales!#REF!` once the range was179 deleted. The sheet is by name, as in formulas, and follows renames.180- `rows` and `columns` are fields by column letter on the source sheet,181 with `order` `desc` for Z to A and `sortBy` the value, counting from 1,182 whose totals order the groups.183- `values` summarize a column: `sum`, `counta`, `count`, `countunique`,184 `average`, `max`, `min`, or `rows` (every row, blank or not, which185 frequency tables use); `showAs` is `percent_of_row`,186 `percent_of_column` or `percent_of_total`; `name` replaces the header.187- `filters` take a filter column's criteria: `hidden` values and a188 `condition` with its `value`.189- `rowTotals` and `columnTotals` are the grand total row and column.190191## Tables192193A sheet's [tables](../sheets/tables.md) are a `tables` list among its194view's fields, before its cells:195196```json197 "tables": [{"name":"Sales","range":"A1:C20","columns":["Region","Month","Amount"],"banded":true,"header":true}],198```199200- `name` is what formulas name it by (`Sales[Amount]`).201- `range` is its cells, header row included.202- `columns` are its columns' names, left to right, as its header row203 shows them. Left out, or of another number than the range's columns,204 they're read from the header row.205- `banded` shades every other data row, and `header` styles the header206 row.207208Tables need version 6: a build without them can't read the formulas209that read them, and refuses the file rather than show them as errors.210211## Regions212213A sheet's regions, [linked files](following.md) and notebook cells'214[outputs sent there](../nushell/notebooks.md#send-to-a-sheet), are a215`regions` list after its cells, charts and rules, one per line, in the216order they were made. The file keeps where each region is and what feeds217it, never its table: opening the file reads linked files again and sends218each output from its cell's saved output.219220```json221 "regions": [222 {"name":"big","at":"A1","output":true},223 {"name":"app","at":"F1","path":"logs/app.csv","window":500}224 ]225```226227- `name` is what formulas name it by (`nu.big`, `nu.app`) and notebook228 cells read a linked file as (`$app`); an output's is its cell's.229- `at` is its table's first cell, the header row's.230- `output` marks a notebook cell's output.231- A linked file has `path`: the file, relative to the workbook's folder232 when it's saved there or below, and otherwise as it was linked.233 `format` names the file's format (`CSV`, `TSV`, `JSON`, `NUON`,234 `XLSX`, `SQLite`, `Parquet`) when the extension doesn't tell it,235 `table` or `query` say what to read from a SQLite database, and236 `window` keeps the last rows under the header.237238The table's cells are saved only for their formatting and notes, as a239spill's are. A region with a `command`, which the notebook sheets of240earlier versions wrote (with `input`, the range it read as `$in`, and241`rows`, `cols`, `reads` and `sort`, which opening it doesn't need),242opens as a code cell of the workbook's notebook, its table sent where it243was ([Notebook sheets](../nushell/notebooks.md#notebook-sheets)).244245## Linked sources246247A [linked source](sources.md)'s tab is a sheet with no cells whose one248region, at `A1`, is `paged`: the file keeps what the source reads and249how its tab orders the rows, never the rows.250251```json252 {253 "name": "trips",254 "cells": {},255 "regions": [256 {"name":"trips","at":"A1","path":"trips.parquet","paged":true,"order":{"sort":[{"column":"C","desc":true}],"filter":[{"column":"B","condition":"eq","value":"card"}]}}257 ]258 }259```260261- `name` is the table formulas read (`trips[fare]`, `nu.trips`); the262 tab is named the same when linked, and keeps its own name when263 renamed.264- `path`, `format`, `table` and `query` are what a linked file's are265 ([Regions](#regions)).266- `paged` marks the region as a source, read in place.267- `order` is how the tab sorts and filters the rows: `sort`, the columns268 by letter, `desc` for Z to A, and `filter`, each a column by letter269 with a `condition` and its `value`, as a filter's column has270 ([Filter](../sheets/sort-filter.md#filter)). Formulas read the271 source in its own order whatever the tab shows.272273Sources need version 7: a build without them would read the region as274a linked file and load the whole source into cells.275276## Notebooks277278A notebook tab is a sheet with `"tab": "notebook"`, `"reactive": true`279when it's [reactive](../nushell/notebooks.md#stale-outputs-and-reactive-notebooks),280and its cells in `notebookCells`, one per line:281282```json283 "tab": "notebook",284 "notebookCells": [285 {"kind":"note","source":"# Files"},286 {"source":"files = ls","output":"[[name, size]; [go.mod, 812b]]"},287 {"source":"$files | nope","error":"Command `nope` not found","detail":"help: ..."},288 {"source":"ls **/*","unsaved":true}289 ]290```291292- `kind` is `note` for Markdown, left out for code; `source` is what the293 cell holds, a code cell's name and all, as `files` above.294- `output` is what the cell's last run printed, as NUON text, kept when295 it fits the `nu-save-cell-kb` and `nu-save-notebook-kb` caps296 ([Configuration](../reference/config.md#nushell-notebooks)); `unsaved`297 marks one left out for its size.298- `error` and `detail` are a failed run's message and help line, `note`299 what an output left out.300301Run counts, timings and stale marks aren't saved: an output read from302the file is what its cell's source and inputs gave.303304## Conditional formats and data validation305306A sheet's rules are two lists after its cells and charts, one rule per307line:308309```json310 "conditionalFormats": [311 {"ranges":"A2:A6","condition":"formula","values":["=$D2"],"text":"green","strikethrough":true},312 {"ranges":"C2:C6","scale":[{"type":"min","color":"green"},{"type":"percentile","value":"50","color":"yellow"},{"type":"max","color":"red"}]},313 {"ranges":"E2:E6","dataBar":{"color":"blue","min":{"type":"min"},"max":{"type":"max"}}},314 {"ranges":"F2:F6","iconSet":{"icons":"arrows","points":[{"type":"percent","value":"33"},{"type":"percent","value":"67"}]}}315 ],316 "validations": [317 {"ranges":"B2:B6","criteria":"list","items":["Ann","Bo","Cy"],"help":"Pick who does it"},318 {"ranges":"D2:D6","criteria":"checkbox"},319 {"ranges":"C2:C6","criteria":"number","condition":"between","values":["0","80"],"reject":true}320 ]321```322323- `ranges` are the rule's ranges, `A2:A9,C2:C9`.324- A single-color rule has a `condition` (`empty`, `not_empty`, `contains`,325 `not_contains`, `starts_with`, `ends_with`, `exactly`, `date_is`,326 `date_before`, `date_after`, `gt`, `ge`, `lt`, `le`, `eq`, `ne`,327 `between`, `not_between`, `formula`, and those comparing a cell with328 the rest: `top`, `bottom`, `top_percent`, `bottom_percent`,329 `above_average`, `below_average`, `duplicate`, `unique`), its `values`330 as typed (a date rule's may be a period: `this week`), and its style:331 `text` and `fill` colors (`red`, `yellow`, `green`, `cyan`, `blue`,332 `magenta`) and `bold`, `italic`, `underline`, `strikethrough`.333- A color scale has `scale`: two or three points, each a `type` (`min`,334 `max`, `num`, `percent`, `percentile`), a `value` where it takes one,335 and a `color`.336- A data bar has `dataBar`: its `color`, its `min` and `max` points as a337 scale's, and `barOnly` to hide the values. An icon set has `iconSet`:338 its `icons` (`arrows`, `circles`, `symbols`, `rating`), the `points`339 where each icon after the first starts, `reverse` for the other way340 round and `iconOnly` to hide the values. Older builds leave both out.341- A validation rule has `criteria` (`list`, `range`, `checkbox`,342 `number`, `date`, `length`, `formula`) with its `items`, `source`343 (`Lists!A1:A20`), or `condition` (`between`, `not_between`, `eq`, `ne`,344 `gt`, `ge`, `lt`, `le`; none for any date) and `values`; `reject` refuses345 invalid entries, and `help` replaces the rule's own help text. A346 checkbox's `items`, when it has them, are its checked and unchecked347 values (`["Yes", "No"]`); a dropdown's `display` is `chip` or `plain`348 when it doesn't show the arrow. Older builds draw every dropdown with349 its arrow, and a checkbox with values of its own as invalid entries.350351A line of either list is also what Add conditional format rule and Add352data validation rule take (in the palette, and as `run(...,353answer=...)` in macros), which is how a macro records a rule added from354the panel.