# todo.export-csv Writes a list of todos as CSV text: a header row and one row per todo, in the order given. Use it for a "download my todos" button, a backup, or handing the list to a spreadsheet or another app. todo.import-csv reads the same text back. ## The file format CSV as in [RFC 4180, Common Format and MIME Type for Comma-Separated Values (CSV) Files](https://www.rfc-editor.org/rfc/rfc4180). The same format is written by todo.export-csv and read by todo.import-csv. - A header row, then one row per todo. todo.export-csv writes these 13 columns in this order: `id,title,notes,done,priority,due,tags,recurrenceFrequency,recurrenceInterval,recurrenceAnchor,createdAt,completedAt,order` - `done` is `true` or `false`. `tags` are joined with single spaces. The repeat takes three columns, all empty when the todo does not repeat. `order` and `recurrenceInterval` are decimal integers. A null field (`notes`, `due`, `completedAt`) is empty. - A field is quoted with double quotes exactly when it contains a comma, a double quote, CR or LF, and a double quote inside it is doubled. Nothing else is quoted, and spaces are kept as they are. - Every row, the last one included, ends with CRLF. **Caveat for spreadsheets.** Nothing is escaped against formula injection: a title such as `=1+1` or `=HYPERLINK(...)` is written as it is, because prefixing it with `'` would change the title when the file is read back. If your app offers the file for opening in Excel, LibreOffice or Google Sheets, warn people, or write a separate spreadsheet export that escapes cells starting with `=`, `+`, `-` or `@`. ## Why it is shaped this way One column per field, with the repeat split into three plain columns rather than packed into one, so the file is readable in a spreadsheet and easy for other tools to produce. Columns are named after the `Todo` fields, so the header documents itself. CRLF line endings and minimal quoting are what RFC 4180 describes and what spreadsheets expect; quoting only when needed keeps the file diff-friendly. ## Edge cases - No todos: just the header line, still ending in CRLF. - Notes that are `""` are written as an empty field, the same as null notes, so they read back as null. Every other valid list reads back exactly: `importCsv(exportCsv(todos))` equals `todos`. - Line breaks inside notes (LF, CRLF or a lone CR) are kept inside quotes as they are. - It does not validate and never throws: a todo is written as it is, even an invalid one (a negative order is written `-1`). Validate on the way in, or let todo.import-csv refuse the file. - The text is a string; encode it as UTF-8 when saving. No byte order mark is written (some versions of Excel need one to read UTF-8; add `\uFEFF` in front if yours does, todo.import-csv accepts it).