Written and maintained by CASRAI Editorial Board
Last updated
The readxl package in R is the standard tool in the tidyverse for importing tabular data from Excel workbooks directly into R data frames and tibbles without requiring external software installations or Java runtimes. In research environments where experimental data, instrument exports, and administrative rosters are routinely archived in Microsoft Excel formats, readxl provides deterministic control over sheet selection, cell ranges, column type coercion, and missing value representations.
What it is used for in research
In academic and clinical research, tabular datasets frequently originate in proprietary spreadsheet formats rather than plain text. Laboratory instruments, clinical trial management systems, electronic lab notebooks, and inter-institutional collaborators regularly exchange datasets formatted as legacy binary Excel files (.xls) or OpenXML spreadsheets (.xlsx). The readxl package is designed to bridge these spreadsheet files into reproducible analytical pipelines in R.
Unlike general-purpose spreadsheet software that permits mixed data types, arbitrary annotations, and embedded charts within a single sheet, downstream statistical workflows—such as linear regression via modeling in R or factorial ANOVA through afex—require rectangular, well-typed data frames. readxl extracts rectangular tables into tidyverse tibbles while offering fine-grained control over column type guessing, date serial number decoding, and cell coordinate boundaries.
Maintained by Jennifer Bryan at Posit under an MIT open-source license, readxl is built on embedded C and C++ parsing libraries: libxls for binary .xls files (Excel 97–2004) and RapidXML for modern .xlsx files. Because these libraries are compiled directly into the R package binary, researchers can ingest spreadsheets without installing Microsoft Office or configuring Java dependencies on their local workstations or high-performance compute clusters. For broader data ingestion across multiple software formats, researchers often combine readxl with other file readers described in the guide on how to load data into R.
Install or access (official source only)
The readxl package is distributed via the Comprehensive R Archive Network (CRAN) and can be installed directly from the official R console:
install.packages("readxl")
Although readxl is part of the tidyverse ecosystem, it is not a core tidyverse package loaded automatically by library(tidyverse). To use its functions in an R script or analysis notebook, load it explicitly:
library(readxl)
The package includes built-in sample spreadsheets that researchers can use to test import pipelines before pointing code at experimental files. The helper function readxl_example() locates these bundled files on your local filesystem:
# List all bundled sample spreadsheets
readxl_example()
# Get the path to a specific example workbook
datasets_path <- readxl_example("datasets.xlsx")
Step-by-step task (numbered, with menu names or commands exactly as in the current docs)
The following five-step workflow demonstrates how to inspect an Excel workbook, read target sheets, restrict cell boundaries, define column data types, and batch-import multiple sheets into a unified dataset.
1. Inspect available sheets in the workbook
Before reading data, determine the names and order of the sheets contained in the workbook using excel_sheets():
library(readxl)
# Path to the target workbook
workbook_file <- "study_results.xlsx"
# Retrieve sheet names as a character vector
sheet_names <- excel_sheets(workbook_file)
print(sheet_names)
# Example output: [1] "Metadata" "Cohort_A" "Cohort_B" "Summary"
2. Read a specific worksheet
By default, read_excel() imports the first sheet in the workbook. To target an experimental cohort or specific analysis tab, specify the sheet by its exact string name or its 1-based integer index:
# Read by sheet name
cohort_a <- read_excel(workbook_file, sheet = "Cohort_A")
# Read by sheet index (second sheet)
cohort_b <- read_excel(workbook_file, sheet = 2)
read_excel() automatically identifies whether the file is binary .xls or OpenXML .xlsx by inspecting the file signature. If you prefer explicit function calls, you can call read_xls() or read_xlsx() directly.
3. Restrict import to a specific cell rectangle
Research spreadsheets often contain introductory title banners, protocol notes, or summary blocks that violate strict tabular layout. Use the range argument to isolate the rectangular data matrix:
# Using standard Excel A1 notation (with or without sheet prefix)
subset_data <- read_excel(
workbook_file,
sheet = "Cohort_A",
range = "B5:G120"
)
For programmatic range selection, readxl exports helper functions from the cellranger package:
# Read rows 5 through 50 across all columns
rows_subset <- read_excel(workbook_file, range = cell_rows(5:50))
# Read columns B through E across all populated rows
cols_subset <- read_excel(workbook_file, range = cell_cols("B:E"))
# Specify upper-left and lower-right coordinates: c(row, col)
bounded_data <- read_excel(
workbook_file,
range = cell_limits(ul = c(5, 2), lr = c(120, 7))
)
Specifying range takes precedence over the skip and n_max arguments.
4. Enforce column types and configure missing values
Excel allows arbitrary data types within the same column. To prevent unexpected type conversions, specify column types explicitly using the col_types argument. Supported types are "skip", "guess", "logical", "numeric", "date", "text", and "list":
# Explicitly declare types for each column
cleaned_data <- read_excel(
workbook_file,
sheet = "Cohort_A",
col_types = c("text", "text", "numeric", "date", "logical", "skip"),
na = c("", "NA", "N/A", "ND", "-999")
)
If you have a sheet where all columns should be imported as strings (for instance, to preserve leading zeros in subject identification numbers), pass a single string: col_types = "text".
5. Batch-import multiple sheets iteratively
When data across experimental sites or treatment cohorts is split across multiple sheets with identical column headers, use purrr::map() and purrr::list_rbind() to combine them into a single longitudinal tibble:
library(readxl)
library(purrr)
# Identify data sheets, excluding metadata
all_sheets <- excel_sheets(workbook_file)
data_sheets <- all_sheets[all_sheets != "Metadata"]
names(data_sheets) <- data_sheets
# Iterate, read each sheet, and bind rows with a cohort indicator column
combined_study <- map(
data_sheets,
~ read_excel(workbook_file, sheet = .x, guess_max = 5000)
) |>
list_rbind(names_to = "cohort")
Output and how to read it
The primary return object of read_excel() is a tibble (class tbl_df, tbl, data.frame). When printed in the R console, the tibble displays the total dimensions (rows × columns) and annotates each column header with its inferred or specified data type:
<chr>(character): Text values and categoricals.<dbl>(double/numeric): Continuous floating-point and integer measurements.<dttm>(POSIXct date-time): Calendar dates and timestamps.<lgl>(logical): Boolean TRUE or FALSE values.<list>: Complex list columns holding heterogeneous cell types whencol_types = "list"is specified.
Understanding how readxl parses dates is critical for research reproducibility. Excel does not store dates as ISO 8601 text strings; instead, it stores them as floating-point serial numbers representing elapsed days from an origin date. Excel supports two distinct date systems:
- 1900 date system (default on Windows and modern Excel): Starts on January 1, 1900. Because Microsoft Excel intentionally preserved an intentional bug from Lotus 1-2-3 that treated 1900 as a leap year, the effective underlying calculation epoch is December 30, 1899.
- 1904 date system (historical default on macOS Excel): Starts on January 1, 1904, and does not contain the leap year bug. Dates in this system are exactly 1,462 days ahead of the 1900 system.
readxl inspects the internal workbook metadata file (such as workbook.xml in .xlsx archives) to determine whether the workbook utilizes the 1900 or 1904 system, automatically translating serial numbers to the correct R POSIXct date-times without requiring manual origin offsets. For research data repositories requiring long-term archiving, validating these structures aligns with standard data curation tools and institutional preservation workflows.
Troubleshooting
When working with complex research workbooks, users commonly encounter the following parsing issues:
- Subject IDs losing leading zeros: Excel frequently classifies numeric identifiers (such as medical record numbers or ZIP codes) as numbers, stripping leading zeros. Supplying
col_types = "text"or setting that specific column to"text"incol_typespreserves the string representation intact. - Numeric columns converting to NA with warning messages: By default,
readxlscans only the first 1,000 rows (guess_max = 1000) to infer column data types. If a column contains numbers for the first 1,000 rows followed by text annotations (such as “below detection limit” or “<LOD”),readxlguessesnumeric, causing later text entries to be coerced toNAwith a warning. Resolve this by increasing the guessing threshold (guess_max = Inf) or explicitly designating the column ascol_types = "text". - Custom missing value codes: Field surveys and electronic lab systems often encode missing values using domain codes such as
-999,"ND"(not detected), or"NULL". Add all expected codes to the missing value vector:na = c("", "NA", "ND", "-999"). - Password-encrypted .xlsx workbooks:
readxlincludes the embeddedtiny-AES-clibrary, permitting decryption of password-protected OpenXML files directly via thepasswordargument:read_excel("secure_data.xlsx", password = "study_password"). - Accented and non-ASCII character corruption:
readxldecodes characters in UTF-8. If non-ASCII characters appear corrupted, confirm that the originating spreadsheet was saved with standard UTF-8 encoding in modern Excel rather than an obsolete regional code page.
Free alternatives (table)
The following table compares open-source R packages for managing Excel spreadsheet workflows in research, noting system dependencies and functional scope:
| Package | Read Support | Write Support | System Dependencies | Primary Strengths and Trade-offs |
|---|---|---|---|---|
| readxl | .xls, .xlsx | No | None (embedded C/C++) | Fastest reading performance; zero external dependencies; outputs native tidyverse tibbles; cannot modify or write Excel files. |
| openxlsx | .xlsx only | Yes | None (C++ via Rcpp) | Comprehensive workbook creation; supports cell styling, formulas, borders, and conditional formatting; does not read legacy .xls. |
| writexl | No | .xlsx only | None (embedded libxlsxwriter C library) | Lightweight and zero-dependency writer; ideal companion to readxl for exporting clean rectangular data frames without Java. |
| xlsx | .xls, .xlsx | Yes | Java Runtime (rJava / Apache POI) | Full-featured read/write capabilities, but requires configuring Java Development Kit (JDK); prone to JVM memory bottlenecks on large sheets. |
| readODS | .ods only | Yes | None (C++ via Rcpp) | Specialized reader and writer for OpenDocument Spreadsheets (.ods) used in LibreOffice and open-source data curation environments. |
FAQ
How do I read a specific sheet by name using readxl in R?
Pass the sheet name as a character string to the sheet argument of read_excel(): read_excel("experiment.xlsx", sheet = "Trial_1"). You can also specify the 1-based numerical index of the sheet (for example, sheet = 1).
Why does readxl convert numbers or dates to NA?
This typically occurs when a column contains mixed data types. readxl uses the first 1,000 rows by default to guess the column type. If those rows contain numeric values but subsequent rows contain text notes, readxl assigns the column a numeric type and coerces non-numeric cells to NA. You can resolve this by increasing the row inspection sample with guess_max = Inf or by explicitly setting the column type using col_types = "text".
Does readxl require Java or external system libraries to read Excel files?
No. readxl is completely self-contained. It incorporates embedded C and C++ libraries (libxls for .xls and RapidXML for .xlsx) that compile directly into the package. It does not require Java, Python, or Microsoft Office to be installed on the system.
How can I read all sheets from an Excel workbook at once?
Combine excel_sheets() with lapply() in base R or purrr::map() in the tidyverse. For example: sheets <- excel_sheets("file.xlsx"); data_list <- lapply(sheets, function(s) read_excel("file.xlsx", sheet = s)). This returns a named list of data frames corresponding to each sheet.
Can readxl export or write modified data back to an Excel file?
No. readxl is strictly a file-reading package. To export data from R back into an Excel workbook without installing Java, use the companion package writexl (via writexl::write_xlsx()) or use openxlsx for advanced styling and multi-tab authoring.
Sources
- Comprehensive R Archive Network (CRAN). “Package readxl: Read Excel Files.” Version 1.6.0. Accessed November 8, 2026. https://cran.r-project.org/package=readxl
- Posit Software, PBC / tidyverse. “readxl: Overview and Documentation.” Accessed November 8, 2026. https://readxl.tidyverse.org/
- Posit Software, PBC / tidyverse. “readxl Workflows and Sheet Manipulation Vignette.” Accessed November 8, 2026. https://readxl.tidyverse.org/articles/readxl-workflows.html
- Microsoft Support. “Differences between the 1900 and the 1904 date system in Excel.” Accessed November 8, 2026. https://learn.microsoft.com/en-us/office/troubleshoot/excel/1900-and-1904-date-system







