R to CSV — write.csv · write_csv · fwrite — live generator

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.

Live R code generator
3 functions compared
fwrite() for large files
All parameters covered
Always free
R to CSV — configure and copy your write call  No data uploaded
Set your options — the R code updates live below
R — ready to paste

Three functions — which to use

Base R, readr and data.table each handle CSV writing differently. Here is the decision guide.

FunctionPackageRow names by defaultSpeedBest 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

write.csv() parameters — complete reference

The parameters you'll need most often, with defaults and when to change them.

ParameterDefaultWhen to change it
row.namesTRUESet 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.
appendFALSESet 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.
quoteTRUEBase 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.

Common patterns

Recipes for the write-to-CSV situations that come up most often.

Append rows to an existing CSV
library(readr) output_path <- "output.csv" # First write — include header write_csv(df_first_batch, output_path) # Subsequent writes — append without header write_csv( df_next_batch, output_path, append = TRUE # write_csv handles col.names automatically )
write_csv(append=TRUE) omits the header automatically. With base R write.csv, use append=TRUE and col.names=FALSE together — otherwise the column names re-appear as a data row.
Write large data frame fast with fwrite()
library(data.table) # fwrite() is multi-threaded — typically 5-20x faster than write.csv() fwrite( df, file = "large_output.csv", sep = ",", na = "", row.names = FALSE, nThread = parallel::detectCores() # use all CPU cores ) # Split large file into chunks of 500k rows: chunk_size <- 500000L for (i in seq(1, nrow(df), by = chunk_size)) { chunk <- df[i:min(i + chunk_size - 1, nrow(df)), ] fwrite(chunk, sprintf("output_part_%d.csv", ceiling(i / chunk_size))) }
fwrite() uses parallel compression and writing threads. nThread=parallel::detectCores() uses all available CPU cores. Ideal for data frames over 1 million rows.
Save selected columns with formatted numbers
library(tidyverse) df %>% select(name, email, amount, created_at) %>% mutate( amount = round(amount, 2), # 2 decimal places created_at = format(created_at, "%Y-%m-%d") # ISO date ) %>% write_csv( "clean_export.csv", na = "NULL" )
Pipe the mutate() transformations directly into write_csv() — no need to create an intermediate object. format() on dates prevents write_csv writing full POSIXct timestamps.
Write multiple data frames to separate CSV files
library(tidyverse) # Split by a column and write one CSV per group df %>% group_by(region) %>% group_walk(~ write_csv(.x, paste0("export_", .y$region, ".csv"))) # Or use a named list of data frames: list_of_dfs <- list(sales = df_sales, returns = df_returns) iwalk(list_of_dfs, ~ write_csv(.x, paste0(.y, ".csv")))
group_walk() splits the data frame by group and calls the function for each. iwalk() iterates over a named list — .x is the data frame, .y is the name.

When do you need R to CSV?

Pipeline output

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.

Excel handoff

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.

Large data export

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.

Reproducible research

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.

Related CSV tools

All data science and bi tools

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.

Three functions covered

write.csv(), write_csv() and fwrite() — with a decision table so you choose the right one.

Live code generator

Switch between functions, set separator, NA string and append mode — the correct call generated instantly.

Four code patterns

Append mode, fwrite() with nThread, column selection with date formatting, and group_walk() multi-file export.

Free, no conditions

No account, no upload. Funded by display advertising only.
Frequently asked questions

How do I save a data frame to CSV in R?

Base R: 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.

Why does write.csv() add an extra first column?

That's the row names. Base R's write.csv() writes row names by default — they appear as an unnamed first column (1, 2, 3…) in the CSV. Fix: write.csv(df, 'output.csv', row.names=FALSE). write_csv() and fwrite() skip row names by default.

What is the difference between write.csv and write_csv?

write.csv() is base R — always available, adds row names by default, always uses comma. write_csv() is from readr (tidyverse) — no row names by default, 1.5–2× faster, consistent UTF-8, column types written more reliably. For most use cases write_csv() is preferred unless you need zero package dependencies.

How do I write a large R data frame to CSV fast?

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.

How do I append rows to an existing CSV in R?

With write_csv(): 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.