Written and maintained by CASRAI Editorial Board
Last updated
To write a CSV file in R, call write.csv(df, "out.csv", row.names = FALSE) in base R, readr::write_csv(df, "out.csv") in the tidyverse, or data.table::fwrite(df, "out.csv"). The three look alike but differ in defaults that change what lands in the file: whether row names are written, what a missing value looks like, how text is quoted, and how the file is encoded. This guide sets those defaults side by side, from each function’s own reference page.
What it is used for in research
Writing a data frame to CSV is the usual last step before a result leaves R: a cleaned analysis dataset deposited alongside a paper, a table of model estimates opened in a spreadsheet by a collaborator, or an export that another tool ingests. CSV is plain text, so the choices you make at write time (row names, missing-value string, encoding, delimiter) decide whether the next reader sees clean columns or a stray index column and garbled accents. If you are on the receiving end of such a file, see reading CSV into R.
Install or access (official source only)
write.csv() and write.csv2() live in the utils package, which is part of base R, so nothing needs installing. write_csv() comes from the readr package and fwrite() from the data.table package; install either from CRAN once, then load it:
install.packages("readr")
install.packages("data.table")
library(readr)
library(data.table)
New to package installation? See how to install R packages. For the wider picture of getting data in and out, see how to load data into R.
Step-by-step: write a data frame to CSV in R
Write a CSV with write.csv() in base R
- Have a data frame, here called
df. - Run
write.csv(df, file = "out.csv", row.names = FALSE). - Check the file in a text editor or reload it with
read.csv("out.csv").
write.csv() calls write.table() with the CSV settings fixed: separator ,, decimal point ., and double quotes inside text values written as doubled quotes. The same help page notes that trying to change append, col.names, sep, dec or qmethod in a write.csv() call is ignored with a warning.
Write a CSV with write_csv() from readr
- Load readr with
library(readr). - Run
write_csv(df, "out.csv"). The first argument must be a data frame or tibble.
readr documents that, unlike write.csv(), its writers do not include row names as a column in the file, so there is no row.names argument to set.
Write a CSV with fwrite() from data.table
- Load data.table with
library(data.table). - Run
fwrite(df, "out.csv").
The data.table reference describes fwrite() as “As write.csv but much faster (e.g. 2 seconds versus 1 minute) and just as flexible,” and sets row.names = FALSE by default, unlike write.csv().
Defaults compared: what each writer puts in the file
| Setting | write.csv() (base R) | write_csv() (readr) | fwrite() (data.table) |
|---|---|---|---|
| Row names written | Yes (row.names = TRUE) |
No row-names column | No (row.names = FALSE) |
Missing values (na) |
"NA" |
"NA" |
Empty string |
| Quoting | Character and factor columns, and row and column names, in double quotes (quote = TRUE) |
quote = "needed" |
quote = "auto" |
| Encoding | Set with fileEncoding |
All columns written as UTF-8 | encoding = "" |
| Byte order mark | Not an argument | None (use write_excel_csv() to add one) |
bom = FALSE (set TRUE to add EF BB BF) |
| Date-times | Not specified on the help page | ISO 8601, converted to UTC | dateTimeAs = "ISO" |
| Append to an existing file | Ignored with a warning | append = TRUE |
Not covered here |
Output and how to read it
The extra first column from write.csv()
With the default row.names = TRUE, write.csv() writes the row names as the first column and sets col.names to NA, which adds a blank column name for that first column. The help page says this is the CSV convention expected by spreadsheets. To read such a file back without an extra column, use read.csv("out.csv", row.names = 1). If the row names are just 1, 2, 3 you do not need them: pass row.names = FALSE.
Quotes, missing values and numbers
Text and factor columns are wrapped in double quotes by write.csv(), and readr quotes only where needed. Numbers are written with up to 15 significant digits; the help page points to the scipen option, and suggests applying format() to a column first if you need finer control. In readr, missing values are never quoted, while a string that matches the na value is always quoted. Because fwrite() writes empty strings for missing values, a file written by fwrite() and one written by write.csv() show missing data differently when opened in a text editor. Related: replacing NA with 0 in R.
Troubleshooting
An unexpected first column appears when I reopen the file
That is the row-names column from write.csv(). Write with row.names = FALSE, or read with row.names = 1.
Why does write.csv() warn that sep or col.names was ignored?
Because write.csv() fixes sep, dec, qmethod, append and col.names; changing them is ignored with a warning. For a different delimiter call write.table() directly, which has sep as an argument.
Accented characters look wrong in Excel
readr states that write_csv() encodes every column as UTF-8 and adds no byte order mark. write_excel_csv() adds a byte order mark so that Excel reads the file as UTF-8. With fwrite() the same effect is bom = TRUE. In base R, fileEncoding declares the encoding used for a file.
My spreadsheet expects semicolons and decimal commas
Use write.csv2() in base R (separator ;, decimal ,) or write_csv2() in readr (semicolon delimiter). The TSV file guide covers the tab-separated alternative.
Can I add rows to an existing file?
With readr, write_csv(df, "out.csv", append = TRUE) appends, and col_names defaults to !append so the header is not repeated. write.csv() ignores append.
Alternatives to write.csv()
| Function | Package | What the reference page says it does |
|---|---|---|
write.csv() |
utils (base R) | Passes arguments to write.table() with comma separator; adds a row-names column by default |
write.csv2() |
utils (base R) | Semicolon separator and decimal comma |
write_csv() |
readr | No row names; UTF-8; ISO 8601 date-times in UTC |
write_excel_csv() |
readr | Comma-delimited with a byte order mark so Excel reads UTF-8 |
write_csv2() |
readr | Semicolon delimiter |
fwrite() |
data.table | Described as write.csv but much faster; no row names by default; missing values written empty |
FAQ
How do I write a CSV in R?
Call write.csv(df, "out.csv", row.names = FALSE) for base R, or write_csv(df, "out.csv") after loading readr. Both write a data frame to a comma-separated text file.
What is the difference between write.csv in R and write.table?
write.csv() is write.table() with CSV settings fixed (comma separator, period decimal, doubled quotes), which is why changing sep in it is ignored.
How do I remove row names when I write.csv in R?
Set row.names = FALSE. The readr and data.table writers omit them by default.
How do I write a CSV in R without quotes?
In write.csv() set quote = FALSE; in readr set quote = "none". Without quoting, a value that contains a comma will split across columns when the file is read.
How do I append to a CSV in R?
Use write_csv(df, "out.csv", append = TRUE); write.csv() ignores append.
Sources (official docs)
- R documentation,
utils::write.table(coverswrite.csvandwrite.csv2): https://stat.ethz.ch/R-manual/R-devel/library/utils/html/write.table.html, checked 2026-10-29. - readr reference,
write_delimandwrite_csv: https://readr.tidyverse.org/reference/write_delim.html, checked 2026-10-29. - data.table reference,
fwrite: https://rdatatable.gitlab.io/data.table/reference/fwrite.html, checked 2026-10-29.







