Save R to CSV
write.csv(), write_csv()
and fwrite() explained.
Choose your function, set the options — the R code updates live. Full comparison of base R write.csv(), readr write_csv() and data.table fwrite() — parameters, defaults and when to use each. No data leaves your browser.
Base R, readr and data.table each handle CSV writing differently. Here is the decision guide.
| Function | Package | Row names by default | Speed | Best for |
|---|---|---|---|---|
| write.csv() | base R | ⚠ Yes — add row.names=FALSE |
Baseline | Scripts with no external dependencies, small files |
| write_csv() | readr | ✓ No row names by default | 1.5–2× faster | tidyverse workflows, consistent UTF-8 output |
| fwrite() | data.table | ✓ No row names by default | 5–20× faster | Large files (1M+ rows), multi-threaded performance |
| write.csv2() | base R | ⚠ Yes | Baseline | European locale — uses ; as separator and , as decimal |
The parameters you'll need most often, with defaults and when to change them.
| Parameter | Default | When to change it |
|---|---|---|
| row.names | TRUE | Set row.names=FALSE in almost every case. Without it, base R writes the integer row names (1, 2, 3…) as an unnamed first column in the CSV. |
| sep | ',' | Change to '\t' for TSV or ';' for European locales. Note: write.csv2() uses ';' by default and ',' as decimal mark. |
| na | "NA" | Change to "" for empty cells, or "NULL" for database import. In write_csv() the parameter is na= with the same values. |
| append | FALSE | Set append=TRUE to add rows to an existing file. Pair with col.names=FALSE (write.csv) or append=TRUE (write_csv) to skip re-writing the header. |
| fileEncoding | "" | Set fileEncoding="UTF-8" explicitly for cross-platform compatibility. R's default encoding is the system locale — on Windows this is often CP1252, which breaks non-ASCII characters on Mac/Linux. |
| quote | TRUE | Base R quotes all character and factor columns by default. Set quote=FALSE for cleaner output when the data has no commas or newlines in text fields. |
Recipes for the write-to-CSV situations that come up most often.
R analysis pipelines — data cleaning, modelling, aggregation — write their final output to CSV for handoff to reporting tools, databases or colleagues who don't use R. write_csv() is the standard final step.
R results shared with colleagues who use Excel. Use fileEncoding="UTF-8" in write.csv() or write_csv() (handles UTF-8 automatically) to prevent garbled characters when the file is opened on Windows.
fwrite() from data.table is 5–20× faster than write.csv() for large data frames. For data frames with millions of rows — genomics, log analysis, survey data — fwrite() is the only practical choice.
R analysis scripts in academic research write cleaned and processed data to CSV as reproducibility artifacts. The CSV is included alongside the R script so reviewers can verify results without running the full pipeline.
Three functions compared,
one live generator. No row.names surprises.
The most common R-to-CSV mistake is forgetting row.names=FALSE in write.csv() — the integer row indices become an unnamed first column in every CSV. The generator above defaults to this correctly for every function. write_csv() and fwrite() skip row names by default.
The comparison table shows the real trade-off: write.csv() for zero dependencies, write_csv() for tidyverse consistency, fwrite() when performance matters. For files over a million rows, fwrite()'s multi-threaded writing is the only practical option.
write.csv(df, 'output.csv', row.names=FALSE). tidyverse: library(readr); write_csv(df, 'output.csv'). data.table: library(data.table); fwrite(df, 'output.csv'). Use write_csv() for most cases — no row names by default and consistent UTF-8 handling.write.csv(df, 'output.csv', row.names=FALSE). write_csv() and fwrite() skip row names by default.library(data.table); fwrite(df, 'output.csv'). fwrite() is multi-threaded and typically 5–20× faster than write.csv() for large data frames. Set nThread=parallel::detectCores() to use all CPU cores. For a 10M row data frame, fwrite() takes seconds where write.csv() takes minutes.write_csv(df, 'output.csv', append=TRUE) — automatically skips the header. With write.csv(): write.csv(df, 'output.csv', append=TRUE, col.names=FALSE, row.names=FALSE) — the col.names=FALSE is critical to prevent duplicate headers.