Library / SDK
gesistsa/rio avatar
gesistsa/rio

rio: two functions for reading and writing data files in R

🐟 A Swiss-Army Knife for Data I/O

621 stars77 forksRLicense varies

At a glance

What is it?
rio is an R package whose entire public idea is a pair of functions, import() and export(), that pick the right reader and writer from a file extension. It covers CSV, Excel, SAS, Stata, SPSS and other formats, and it keeps the install slim by leaving some of them to suggested packages.
Who is it for?
Adopt rio if you work in R and your scripts move between data formats, because import() and export() remove the branch on file extension that otherwise appears in every script, and because the format table makes the coverage explicit rather than folklore.
Can I use it commercially?
Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly R, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A whole data I/O layer behind two function names

The aim of rio, stated in the README, is to make data file input and output in R as easy as possible by implementing two main functions in Swiss-army knife style. The first is import(), which provides a painless data import experience by automatically choosing the appropriate import or read function based on the file extension, or on a format argument you pass explicitly. The second is export(), which provides the same painless file recognition for writing. That is the whole idea, and it is a good one for the case it targets: an analysis script that reads a spreadsheet in one run and a CSV in the next does not need two code paths. The examples make the point by reading the same dataset twice, once from a spreadsheet file and once from a CSV file, and both calls produce the same data frame. The package is on CRAN, the README carries a CRAN version badge and a download badge, and the topics list names the formats that matter in practice, csv, csvy, excel, sas, spss, stata, and cran. The audience is an R user who moves data between systems and does not want to remember which function reads which format.

How import() decides which reader to use

The selection is by extension, with an explicit override available when the extension is wrong or missing. The format table in the README is the reference for what that means in practice, and it is worth reading because it shows how much of the work is delegated. Comma-separated data, European comma-separated data and pipe-separated data all go through data.table, and CSVY, which is CSV with a YAML metadata header, is handled by the same package. Excel, in its modern xlsx form, uses readxl for import and writexl for export, while the legacy xls and the read-only variants xlsm, xltx and xltm can be read but not written. The foreign package covers the statistical formats that R has always read, Epiinfo, rec, Minitab and mtp, and the base utils package handles the data interchange format and Fortran data, the latter with the note that it has no recognised extension. Compressed and archive formats are handled by base packages: tar and zip go through utils, while bz2 and gz are handled by base. Two entries show how the fallback works. The ambiguous.dat extension is attempted as delimited text data through data.table, and Google Sheets is treated as comma-separated data, which is a pragmatic simplification of a remote service. The design keeps the decision table in one visible place, which is the difference between a package you can reason about and one whose format coverage you learn by trial and error.

export() writes, and the archive shortcut removes a step

Export is the mirror of import, and the examples show the same three calls with different extensions, a CSV, an R serialized file and an SPSS file, each with a comment naming the format. The extension is the format, which is the same rule as on the reading side. The feature the README singles out is compression. Because the archive formats are part of the same table, a single export call can write a compressed file, so exporting a large table to a zipped TSV is one operation rather than an export followed by a separate compression step in the shell or in a second call.

r
export(mtcars, "mtcars.tsv.zip")

The second capability is writing several data frames at once. A list of data frames can be exported to their respective sheets of an Excel workbook, and the same applies to an HTML file, which is how a report with several tables is produced in one call.

r
export(list(mtcars = mtcars, iris = iris), file = "mtcars.xlsx")

That combination, one call for a workbook with one sheet per data frame, is the kind of thing that is tedious in base R and is the reason rio exists at all. The reading side is symmetric with it, so a workflow that produces several tables and writes them to a workbook and then reads that workbook back needs one function on each side.

Suggested packages keep the install small, and two functions tell you what is missing

The package deliberately does not install everything it can read. Several formats are supported through Suggests packages which are not installed or loaded by default, and the README explains the reason, which is to keep the package slim. The consequence is that a fresh install can read less than the table advertises, and the package provides two commands for dealing with that. show_unsupported_formats() reports which formats are not currently supported in your environment, and install_formats() installs the suggested packages, either all of them or, since the README says you can install the suggested packages individually depending on your own needs, a subset you choose.

r
library(rio)
install_formats()

The pragmatic workflow is to install rio, call show_unsupported_formats() once, and add only the readers and writers your pipeline actually needs. That keeps the dependency graph smaller than a package that hard depends on every backend, and it makes a missing format an explicit fact rather than a runtime error three months into a project. The trade-off is that the import function can fail at call time rather than at install time, and that a script that assumes a format is available will work on one machine and not on another. For a team that pins its environment this is a non-issue. For a script shared between colleagues it is worth a line of documentation.

The generated format table, and the data-raw pipeline behind it

The repository shows how the format table is maintained, and it is a small but instructive piece of engineering. The Makefile at the root has three targets that matter. The NAMESPACE file is regenerated from the sources in R with devtools::document(). The internal data file R/sysdata.rda is built from data-raw/single.json by running the script data-raw/convert.R, which means the format metadata lives in a JSON file and is compiled into the package at build time rather than being duplicated as R code. And the README itself is generated with devtools::build_readme(), from README.Rmd, which is why the repository contains both the Rmd source and the rendered markdown. The package follows the standard R package conventions around that, with a DESCRIPTION file, a NAMESPACE, a man directory for the generated help pages, a tests directory, a vignettes directory, and a pkgdown configuration in _pkgdown.yml for the documentation site. The starwars.csv and starwars.xlsx files at the root are the fixtures used in the README examples, and the Makefile regenerates the sysdata file from the JSON source, so the format table in the documentation and the table the package uses at runtime come from the same place. For an R package, that is a well behaved setup, and it is one of the reasons the project has survived a long life on CRAN.

rio against base R, and the state of the project

The alternative is what most R scripts already do, which is to call read.csv, read.table, readxl or foreign::read.spss by hand and pick the right one yourself. The difference is not speed or fidelity, since rio delegates to the same readers, and it is not format coverage, since the same backends appear in both cases. The difference is the shape of the code, and it is a real one in a script that has to handle whatever a colleague sends over. The base approach puts a switch on the filename in every script; the rio approach puts it in one function that has already made the decision, and the decision is documented in a table rather than in the head of whoever wrote the script. There is a cost, and it is the same cost described above, that rio pulls in data.table, and optionally readxl, writexl and foreign, so a project that only ever read CSVs is now depending on a package that wants to be a general answer. On maintenance, the releases are recent, with v1.3.0 on 2026-04-02, v1.2.4 on 2025-09-28 and v1.2.3 on 2024-09-26, and the last push was on 2026-07-20, so the code is being maintained. The one administrative gap worth naming is that the repository does not state a licence, so before you vendor the source into a closed product, check the DESCRIPTION file and the CRAN page for the terms.

Editorial conclusion

Adopt rio if you work in R and your scripts move between data formats, because import() and export() remove the branch on file extension that otherwise appears in every script, and because the format table makes the coverage explicit rather than folklore. It does not fit a project that wants a single dependency and no extras, since several formats live in suggested packages that you install on demand, and it does not fit a pipeline where the format is already known and the base R functions are enough. Before you adopt it, run show_unsupported_formats() to see what your environment is missing, and check the version you get from CRAN against the current release, v1.3.0 from 2026-04-02, since the licence for the package itself is not stated in the repository.

Frequently asked questions

What does the rio R package do?

It provides two functions, import() and export(), that read and write data files in many formats by choosing the right reader or writer from the file extension, or from a format argument you pass. The aim stated in the README is to make data file input and output in R as easy as possible.

How do I install rio in R?

The package is on CRAN, so install.packages("rio") is enough. The development version on GitHub can be installed with remotes::install_github("gesistsa/rio"), and install_formats() adds the suggested packages for the optional formats.

Which formats does rio support?

The README table covers archive, compression, delimited and statistical formats, including csv, csv2, psv, csvy, fwf, xlsx and legacy xls, tar and zip archives, plus Epiinfo, Minitab, Fortran data, Google Sheets as comma-separated data, and formats handled by the foreign package. Suggests packages provide several of them, so the coverage depends on what is installed.

How do I check which formats my rio installation can handle?

Call show_unsupported_formats(), which reports the formats that are not currently supported in your environment. The reason is that several formats come from suggested packages that are not installed or loaded by default, to keep the package slim, and install_formats() installs them.

Can rio write several data frames into one Excel workbook?

Yes. The README shows export() taking a list of data frames and writing each to its own sheet, and it notes that the same applies to an HTML file, with the example export(list(mtcars = mtcars, iris = iris), file = "mtcars.xlsx").

Official sources

  1. gesistsa/rio on GitHub
  2. Issues
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/gesistsa-rio.svg)](https://hysenlabs.com/projects/gesistsa-rio)

Was this useful?