---
title: "The CSV target — quario"
description: "Rendering a quario definition to CSV records, what the target withdraws, and the injection guard that changes your text."
url: "https://getquario.com/docs/diving-deeper/csv-target"
---

# The CSV target

## Intent

`@quario/csv` flattens the walk to records. It reads no styles, no geometry and
no images, and it has no runtime dependencies of its own. Use it when
something downstream parses rather than reads.

## Design decisions

**This target withdraws rather than approximates.** It reads no styles, column
widths, page columns, page bands, paper, or images. A declared `format` kind
revives nothing and presents nothing, so a field keeps the author’s own string.
That’s a decision, not a gap.

**A group’s `collapsed` withdraws too.** The file writes every record, and an
order is all a record has. Dropping the rows would change the data rather than
how it opens. A reader who wants the summary alone filters the file.

**A report that’s one table gives a clean file.** Its column headers are
record 1. Any other band makes the file ragged, because a band item is a record
with one field.

**Spans keep the record rectangular.** A covering cell writes its value in the
first field and leaves the rest empty, so every record is the same width. This
target reads no geometry, so a merge isn’t something it could mean.

**A split is one record.** Its slots are the fields across it, because slots
are values beside each other and that’s what a record is.

**The injection guard changes your text, deliberately.** A display-text field
starting with `=`, `+`, `-`, `@`, TAB or CR gets a leading apostrophe, so a
spreadsheet opening the file can’t execute it. Those fields don’t round-trip
through a parser. Typed numbers aren’t guarded, so `-1` stays `-1`.

**Quoting is RFC 4180 and the line ending isn’t.** LF separates records, there
is no BOM, and the string ends with a newline. The file isn’t an RFC 4180
document, and saying so is more useful than implying it is.

**The marking is weaker here than elsewhere.** An unlicensed render writes the
wording as one trailing line, so a parser reading headers from record 1 still
can. Whether a parser that stops before EOF sees the marking is best-effort.

## API walkthrough

### Rendering

```bash title="shell"
npm install @quario/csv
```

Import the `csv` factory and render the same compiled report. The result is a
string, and you encode the file. `csv()` takes no options, and
refuses one rather than discarding it.

### What a field holds

A cell whose value is one bare interpolation writes its pre-stringify value as
text. It writes a finite number as ECMAScript ToString, so `1000`, a boolean as
`true` or `false`, and a `Date` as ISO 8601 UTC.

Every other cell joins to display text, then passes the injection guard, then
RFC 4180 quoting. A field containing a quote, a comma, LF, or CR takes quotes
around it, and a quote inside it doubles.

Numbers therefore arrive computable after a parse, which is the one thing this
target shares with the workbook.

### Getting a clean file

```json title="a table-only report"
{
  "data": "$.orders[*]",
  "detail": {
    "columns": [
      { "header": { "value": "Order" }, "value": "{{ @.number }}" },
      { "header": { "value": "Amount" }, "value": "{{ @.amount }}" }
    ]
  }
}
```

No header band, no footer, no groups. Record 1 is the column headers, and every
later record is a row.

Add a report header and the first record becomes that item, with one field, and
the file runs ragged from there. That’s expected. It’s what flattening a banded
document to records means. It’s also the reason a definition meant for CSV is
usually a plainer document than one meant for a page.

### Choosing between this and a workbook

Both flatten the same walk in the same order. The workbook keeps types, styles
and a frozen header, and mangles nothing. This target keeps neither types nor
styles, guards formula-like text by changing it, and has no dependencies at
all.

Send a workbook to a person. Send CSV to a program, and check whether that
program minds the guard.

```json
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://getquario.com/"},{"@type":"ListItem","position":2,"name":"Docs","item":"https://getquario.com/docs"},{"@type":"ListItem","position":3,"name":"The CSV target","item":"https://getquario.com/docs/diving-deeper/csv-target"}]}
```
