An introduction to renv

Create reproducible environments for projects

November 2025

Nicolas Casajus

Senior data scientist
@FRB-CESAB    

Context

  Research compendium for file organization

  Writing functions for code optimization

  Quarto (or RMarkdown) for literate programming

  git for version control

  GitHub for sharing & collaboration


These tools aim to make your code and data more reproducible.


 What about the computational environment?

  •  packages (and their versions)
  •  version
  • Software and system libraries
  • Operating system

Context

  Research compendium for file organization

  Writing functions for code optimization

  Quarto (or RMarkdown) for literate programming

  git for version control

  GitHub for sharing & collaboration


These tools aim to make your code and data more reproducible.


 What about the computational environment?

  •  packages (and their versions)
  •  version
  • Software and system libraries
  • Operating system

What is the issue with packages?

  • Packages rapidly change over time (versions)
  • Code breaks between major versions
  • Accessible from different repositories (CRAN, GitHub, R Universe, etc.)
  • Only one version of a package can be installed
  • A package can have many dependencies


Questions

  • Will my project work on another laptop?
  • Will my project work in the future?
  • Bonus: How to deal with projects requiring different versions of a same package?


 We have to specify, the required packages, their versions, and the repositories in which they are accessible!

The renv package


# Install 'renv' package ----
install.packages("renv")


Table: Main functions of renv
Function Description
init() Initialize renv in a project
status() Check consistencies between lockfile & project library
snapshot() Record current state of the project library in the lockfile
restore() Restore project library from a lockfile
install() Install packages in the project library
remove() Remove packages from the project library
deactivate() Temporary deactivate renv for the project
activate() (Re)activate renv in the project

Library vs. package

PACKAGE

A well-structured collection of functions, documentation, data, and tests.


LIBRARY

A directory in which packages will be installed.


# Available libraries ----
.libPaths()

# [1] "~/.local/bin/R/x86_64-redhat-linux-gnu-library/4.5"
# [2] "/usr/lib64/R/library"


[1] is the User library
[2] is the System library

Library vs. package

PACKAGE

A well-structured collection of functions, documentation, data, and tests.


LIBRARY

A directory in which packages will be installed.


# Available libraries ----
.libPaths()

# [1] "~/.local/bin/R/x86_64-redhat-linux-gnu-library/4.5"
# [2] "/usr/lib64/R/library"


[1] is the User library
[2] is the System library

# Locate core package ----
find.package("utils")

# [1] "/usr/lib64/R/library/utils"

Library vs. package

PACKAGE

A well-structured collection of functions, documentation, data, and tests.


LIBRARY

A directory in which packages will be installed.


# Available libraries ----
.libPaths()

# [1] "~/.local/bin/R/x86_64-redhat-linux-gnu-library/4.5"
# [2] "/usr/lib64/R/library"


[1] is the User library
[2] is the System library

# Locate core package ----
find.package("utils")

# [1] "/usr/lib64/R/library/utils"


# Locate other package ----
find.package("rmarkdown")

# [1] "~/.local/bin/R/x86_64-redhat-linux-gnu-library/4.5/rmarkdown"

Library system

Default  behaviour

Library system

Default  behaviour

Library system with renv

Library system

Default  behaviour

Library system with renv

Initialize renv in a project

  • renv::init() to intialize renv in an existing project


R version 4.5.2 (2025-10-31) -- "[Not] Part in a Rumble"
Copyright (C) 2025 The R Foundation for Statistical Computing
Platform: x86_64-redhat-linux-gnu

- Project '~/Documents/myproject' loaded. [renv 1.1.5]
>

Alternatively, for a new project with Positron:

What happened?

.
├── .Rprofile
│
├── renv/
│   ├── .gitignore
│   ├── activate.R
│   ├── library/
│   └── settings.dcf
│
└── renv.lock

What happened?

.
├── .Rprofile          # Activate renv on project opening
│
├── renv/
│   ├── .gitignore
│   ├── activate.R
│   ├── library/
│   └── settings.dcf
│
└── renv.lock

What happened?

.
├── .Rprofile          # Activate renv on project opening
│
├── renv/
│   ├── .gitignore     # Ignore large renv files (e.g. packages)
│   ├── activate.R     # R script to launch renv
│   ├── library/       # R packages
│   └── settings.dcf   # renv settings
│
└── renv.lock

What happened?

.
├── .Rprofile          # Activate renv on project opening
│
├── renv/
│   ├── .gitignore     # Ignore large renv files (e.g. packages)
│   ├── activate.R     # R script to launch renv
│   ├── library/       # R packages
│   └── settings.dcf   # renv settings
│
└── renv.lock          # Packages metadata, a.k.a the Lockfile

The lockfile

It’s a simple JSON (JavaScript Object Notation) file that specifies package metadata:

  • package name
  • package version
  • package repository URL


Preview of a lockfile (renv.lock)

{
  "R": {
  
    "Version": "4.5.2",
    "Repositories": [
      {
        "Name": "CRAN",
        "URL": "https://cloud.r-project.org"
      }
    ]
  },
  
  "Packages": {
  
    "renv": {
      "Package": "renv",
      "Version": "1.1.5",
      "Source": "Repository",
      "Repository": "CRAN",
      "Requirements": [
         "utils"
      ],
      "Hash": "47623f66b4e80b3b0587bc5d7b309888"
    }
  }
}

The lockfile

It’s a simple JSON (JavaScript Object Notation) file that specifies package metadata:

  • package name
  • package version
  • package repository URL


Preview of a lockfile (renv.lock)





  To collaborate you only need to share the renv.lock

{
  "R": {
  
    "Version": "4.5.2",
    "Repositories": [
      {
        "Name": "CRAN",
        "URL": "https://cloud.r-project.org"
      }
    ]
  },
  
  "Packages": {
  
    "renv": {
      "Package": "renv",
      "Version": "1.1.5",
      "Source": "Repository",
      "Repository": "CRAN",
      "Requirements": [
         "utils"
      ],
      "Hash": "47623f66b4e80b3b0587bc5d7b309888"
    }
  }
}

Working w/ renv

  • renv::status() is your friend

Use this function at any time to check inconsistencies between lockfile, library and code

  Package used but not installed in the project library

renv::status()

# The following package(s) are used in this project, but are 
# not installed:
# - fs

  use renv::install()

Working w/ renv

  • renv::status() is your friend

Use this function at any time to check inconsistencies between lockfile, library and code

  Package used but not installed in the project library

renv::status()

# The following package(s) are used in this project, but are 
# not installed:
# - fs

  use renv::install()

  Package used and installed but not recorded in the lockfile

renv::status()

# The following package(s) are in an inconsistent state:
# 
# package installed recorded used
# fs      y         n        y 

  use renv::snapshot()

Working w/ renv

  • renv::status() is your friend

Use this function at any time to check inconsistencies between lockfile, library and code

  Package used but not installed in the project library

renv::status()

# The following package(s) are used in this project, but are 
# not installed:
# - fs

  use renv::install()

  Package used and installed but not recorded in the lockfile

renv::status()

# The following package(s) are in an inconsistent state:
# 
# package installed recorded used
# fs      y         n        y 

  use renv::snapshot()


  Package not used but recorded in the lockfile

renv::status()

# The following package(s) are in an inconsistent state:
# 
# package installed recorded used
# fs      y         y        n  

  use renv::snapshot() and renv::remove("fs") (optional)

Working w/ renv

  • renv::status() is your friend

Use this function at any time to check inconsistencies between lockfile, library and code

  Package used but not installed in the project library

renv::status()

# The following package(s) are used in this project, but are 
# not installed:
# - fs

  use renv::install()

  Package used and installed but not recorded in the lockfile

renv::status()

# The following package(s) are in an inconsistent state:
# 
# package installed recorded used
# fs      y         n        y 

  use renv::snapshot()


  Package not used but recorded in the lockfile

renv::status()

# The following package(s) are in an inconsistent state:
# 
# package installed recorded used
# fs      y         y        n  

  use renv::snapshot() and renv::remove("fs") (optional)

  Package used, installed and recorded in the lockfile

renv::status()

# No issues found -- the project is in a consistent state. 

  Nothing to do!

Installing packages

  • renv::install() to install packages in your project library

You can install the latest version of a package, a specific version of a package, a package from GitHub, GitLab, etc.


# Install the latest version from CRAN ----
renv::install("fs")

# Install a specific version ----
renv::install("fs@1.6.0")

# Install the development version from GitHub ----
renv::install("r-lib/fs")


  You can install all required packages (automatically detected by renv) as follow:


# Install (latest versions of) all required packages ----
renv::install()

Updating the lockfile

  • renv::snapshot() to update the lockfile (package metadata)

This function will add and/or remove package metadata in the lockfile


# Update the lockfile ----
renv::snapshot()

# The following package(s) will be updated in the lockfile:
# 
# # CRAN ----------------------------------------------------
# - fs    [1.6.6 -> *]
# - cli   [* -> 3.6.5]


  • Remove the fs package metadata from the lockfile
  • Add the cli package metadata to the lockfile

Cleaning project library

  • renv::remove(...) to uninstall package(s) from project library (optional)


# Uninstall package ----
renv::remove("fs")

# - Removing package(s) from project library ...
#   Removing package 'fs' ... Done!

Collaborating w/ renv

  You just need to share the renv.lock file


1. Colleague A

  • Initialize renv in the project w/ renv::init()
  • Add .Rprofile & renv/ to .gitignore
  • Install required packages w/ renv::install()
  • [ Use new packages in code ]
  • [ Install new packages w/ renv::install() ]
  • Update the lockfile w/ renv::snapshot()
  • Stage, commit and push changes to GitHub

Collaborating w/ renv

  You just need to share the renv.lock file


1. Colleague A

  • Initialize renv in the project w/ renv::init()
  • Add .Rprofile & renv/ to .gitignore
  • Install required packages w/ renv::install()
  • [ Use new packages in code ]
  • [ Install new packages w/ renv::install() ]
  • Update the lockfile w/ renv::snapshot()
  • Stage, commit and push changes to GitHub

2. Colleague B / C / …

  • Pull changes from GitHub
  • Initialize renv w/ renv::init(bare = TRUE)
  • Restore project library from a lockfile w/ renv::restore()
  • [ Use new packages in code ]
  • [ Install new packages w/ renv::install() ]
  • Update the lockfile w/ renv::snapshot()
  • Stage, commit and push changes to GitHub

Collaborating w/ renv

  You just need to share the renv.lock file


1. Colleague A

  • Initialize renv in the project w/ renv::init()
  • Add .Rprofile & renv/ to .gitignore
  • Install required packages w/ renv::install()
  • [ Use new packages in code ]
  • [ Install new packages w/ renv::install() ]
  • Update the lockfile w/ renv::snapshot()
  • Stage, commit and push changes to GitHub

2. Colleague B / C / …

  • Pull changes from GitHub
  • Initialize renv w/ renv::init(bare = TRUE)
  • Restore project library from a lockfile w/ renv::restore()
  • [ Use new packages in code ]
  • [ Install new packages w/ renv::install() ]
  • Update the lockfile w/ renv::snapshot()
  • Stage, commit and push changes to GitHub


3. Colleague A

  • Pull changes from GitHub
  • Restore project library from a lockfile w/ renv::restore()
  • [ Use new packages in code ]
  • [ Install new packages w/ renv::install() ]
  • Update the lockfile w/ renv::snapshot()
  • Stage, commit and push changes to GitHub

Collaborating w/ renv

  You just need to share the renv.lock file


1. Colleague A

  • Initialize renv in the project w/ renv::init()
  • Add .Rprofile & renv/ to .gitignore
  • Install required packages w/ renv::install()
  • [ Use new packages in code ]
  • [ Install new packages w/ renv::install() ]
  • Update the lockfile w/ renv::snapshot()
  • Stage, commit and push changes to GitHub

2. Colleague B / C / …

  • Pull changes from GitHub
  • Initialize renv w/ renv::init(bare = TRUE)
  • Restore project library from a lockfile w/ renv::restore()
  • [ Use new packages in code ]
  • [ Install new packages w/ renv::install() ]
  • Update the lockfile w/ renv::snapshot()
  • Stage, commit and push changes to GitHub


3. Colleague A

  • Pull changes from GitHub
  • Restore project library from a lockfile w/ renv::restore()
  • [ Use new packages in code ]
  • [ Install new packages w/ renv::install() ]
  • Update the lockfile w/ renv::snapshot()
  • Stage, commit and push changes to GitHub

4. Colleague B / C / …

  • Pull changes from GitHub
  • Restore project library from a lockfile w/ renv::restore()
  • [ Use new packages in code ]
  • [ Install new packages w/ renv::install() ]
  • Update the lockfile w/ renv::snapshot()
  • Stage, commit and push changes to GitHub

  Git conflicts on the lockfile



Solution 1

  Define ONE person in charge of updating the renv.lock with renv::snapshot()


1. Colleague A (team leader)

  • Initialize renv in the project w/ renv::init()
  • Add .Rprofile & renv/ to .gitignore
  • Install required packages w/ renv::install()
  • [ Use new packages in code ]
  • [ Install new packages w/ renv::install() ]
  • Update the lockfile w/ renv::snapshot()
  • Stage, commit and push changes to GitHub

2. Colleague B / C / …

  • Pull changes from GitHub
  • Initialize renv w/ renv::init(bare = TRUE)
  • Restore project library from a lockfile w/ renv::restore()
  • [ Use new packages in code ]
  • [ Install new packages w/ renv::install() ]
  • Update the lockfile w/ renv::snapshot()
  • Stage, commit and push changes to GitHub


3. Colleague A (team leader)

  • Pull changes from GitHub
  • Restore project library from a lockfile w/ renv::restore()
  • [ Use new packages in code ]
  • [ Install new packages w/ renv::install() ]
  • Update the lockfile w/ renv::snapshot()
  • Stage, commit and push changes to GitHub

4. Colleague B / C / …

  • Pull changes from GitHub
  • Restore project library from a lockfile w/ renv::restore()
  • [ Use new packages in code ]
  • [ Install new packages w/ renv::install() ]
  • Update the lockfile w/ renv::snapshot()
  • Stage, commit and push changes to GitHub

Solution 2

  Use renv at the end of the project to freeze your packages environment


Team leader

  • Initialize renv in the project w/ renv::init()
  • Add .Rprofile & renv/ to .gitignore
  • Install required packages w/ renv::install()
  • Update the lockfile w/ renv::snapshot()
  • Stage, commit and push changes to GitHub



  Alternatively, visit this page

Thanks!