R PROGRAMMING • GETTING STARTED AND TOOLING

Installing & Loading Packages — Install packages and load them with install.packages() and library()

Extend R's capabilities by tapping into thousands of community-built packages through CRAN and Bioconductor repositories.

Historical Context & Motivation

When R was first released in the mid-1990s, it shipped as a relatively compact statistical computing environment with a core set of functions for data manipulation, linear modeling, and basic graphics. Researchers who needed specialized algorithms—say, Bayesian inference, text mining, or geospatial analysis—had to write everything from scratch or port code from other languages. This friction was a significant barrier to adoption, particularly when competing platforms like SAS and MATLAB offered large proprietary libraries. The R community recognized early on that a standardized mechanism for sharing reusable code bundles would be essential for the language's growth and scientific reproducibility.

The solution was the package system—a formal structure for bundling R functions, documentation, data sets, and compiled code into distributable units. Coupled with a centralized repository, this system transformed R from a niche academic tool into one of the most extensible programming environments in data science. Understanding how to install and load packages is therefore not a peripheral skill; it is the gateway to virtually every serious analysis workflow in R.

1997
CRAN Established
The Comprehensive R Archive Network (CRAN) launches with a handful of contributed packages, modeled after CTAN and CPAN from the TeX and Perl communities.
2003
R 1.7 & Namespaces
R introduces namespace support, allowing packages to export only selected functions and reducing naming conflicts—a critical step toward a scalable ecosystem.
2004
Bioconductor Matures
Bioconductor, a domain-specific repository for genomics and bioinformatics, demonstrates that specialized package repositories can coexist alongside CRAN.
2014
Tidyverse Emergence
Hadley Wickham's collection of packages (dplyr, ggplot2, tidyr) popularizes the idea of meta-packages that bundle related tools, accelerating mainstream adoption of R.
2024
20,000+ CRAN Packages
CRAN surpasses 20,000 actively maintained packages, with automated testing infrastructure ensuring quality across multiple operating systems.

With this historical trajectory in mind, the central question becomes practical: how does a developer efficiently discover, install, and activate the right packages to extend base R for a given analytical task? The answer revolves around two core functions—install.packages() and library()—and a deeper understanding of R's package management architecture.

Core Principles & Definitions

Before diving into commands, it is important to establish a precise vocabulary. In the R ecosystem, a package is a structured directory containing R code (in the R/ folder), a DESCRIPTION file specifying metadata and dependencies, a NAMESPACE file controlling exports and imports, and optionally documentation, data, and compiled source code. Understanding the distinction between installing a package to disk and loading it into a running session is foundational—conflating the two is a common source of confusion.

1

Repository

A remote server hosting package tarballs. CRAN is the default; Bioconductor and GitHub are common alternatives. Repositories are specified via URL in install.packages().
2

Installation

The process of downloading a package from a repository and compiling/copying it into a local library directory on disk. This is a one-time operation per version.
3

Library (Directory)

A filesystem directory where installed packages reside. R searches multiple library paths, viewable via .libPaths(). Do not confuse with the library() function.
4

Loading (Attaching)

Calling library(pkg) attaches the package's namespace to the search path, making its exported functions directly callable. This must be done each session.
5

Dependencies

Packages that a given package requires (Imports) or enhances (Suggests). R's installer resolves and installs transitive dependencies automatically.
KEY TAKEAWAY
Think of install.packages() as downloading an app from an app store to your phone's storage—it only needs to happen once per version. Calling library() is like opening that app each time you want to use it. You don't re-download an app every time you launch it, and similarly you don't re-install a package every session—you just load it.

Visual Explanation — The Package Lifecycle

The diagram below illustrates the complete lifecycle of an R package from a remote repository to active use within an R session. Understanding this flow clarifies why installation and loading are separate operations and where common errors arise.

The lifecycle begins at a remote repository (left), passes through install.packages() which downloads and compiles the package to the local library, and concludes with library() which loads the namespace into the active R session.

Notice the critical architectural boundary between the persistent local library (green box) and the transient active session (pink box). When R restarts, the session is wiped clean, but installed packages remain on disk. This is why scripts typically begin with a series of library() calls but rarely include install.packages()—installation is a setup-time concern, while loading is a run-time concern. The pkg::func() syntax shown in the session box offers an alternative: you can call a function from an installed package without attaching the entire namespace, which is useful for avoiding name collisions in larger projects.

How It Works — Under the Hood

The install.packages() Function

The install.packages() function orchestrates several steps behind the scenes. First, it queries the specified repository (defaulting to your chosen CRAN mirror) for the package tarball. It then downloads the archive, checks dependencies listed in the DESCRIPTION file, recursively installs any missing dependencies, compiles any C/C++/Fortran source code if needed, and finally copies the resulting package directory into the first writable path returned by .libPaths(). The function signature exposes several important parameters.

BASIC SYNTAX
install.packages(pkgs, repos, lib, dependencies, type)
pkgs — character vector of package names; repos — repository URL (default: CRAN mirror); lib — installation directory; dependencies — logical or character vector controlling which dependency types to install; type — "source" or "binary".
Binary vs. Source
On Windows and macOS, CRAN provides precompiled binary packages that install instantly. On Linux, packages are typically compiled from source, which requires a C compiler (gcc) and may take longer. If a binary is unavailable for your R version, install.packages() will fall back to source compilation and prompt you accordingly.

The library() Function

Once a package is installed on disk, library() performs two distinct operations. First, it loads the package's namespace into memory, executing any .onLoad() hooks defined by the package author. Second, it attaches the namespace to the R search path (viewable via search()), which is what makes the package's exported functions available by name without the :: operator. If the package is not installed, library() throws an error and halts execution—this fail-fast behavior is by design, ensuring scripts don't silently proceed with missing tools.

LOADING SYNTAX
library(package, lib.loc, quietly, warn.conflicts)
package — unquoted or quoted package name; lib.loc — character vector of library paths to search; quietly — suppress package startup messages; warn.conflicts — warn when package masks existing functions.

require() vs. library()

R provides a second loading function, require(), which behaves identically to library() except in one critical respect: when the package is not found, require() returns FALSE and issues a warning rather than stopping execution. This makes require() appropriate inside functions where you want to programmatically check availability, but library() is preferred at the top of scripts because a missing critical package should be treated as a fatal error, not a silent warning.

Repository Ecosystem & Package Discovery

While CRAN is the default and largest repository, the R ecosystem supports multiple sources. Knowing which repository to use—and how to install from each—is essential for working with cutting-edge or domain-specific tools. The table below compares the major repositories.

Five major sources feed into the R session. CRAN, Bioconductor, GitHub, R-universe, and local source archives all install packages to the local library, from which library() loads them.
Common package sources and their installation commands
SourceInstall CommandUse Case
CRANinstall.packages("dplyr")Stable, reviewed packages for general use
BioconductorBiocManager::install("DESeq2")Genomics, bioinformatics, high-throughput data
GitHubdevtools::install_github("user/repo")Development versions, unreleased features
R-universeinstall.packages("pkg", repos="https://user.r-universe.dev")Author-curated package collections, CI/CD builds
Local tarballinstall.packages("pkg.tar.gz", repos=NULL, type="source")Offline environments, proprietary internal packages

Worked Example — Installing and Using ggplot2

Let us walk through the complete process of installing the ggplot2 package from CRAN, loading it, verifying it works, and handling a common error scenario. This example mirrors a realistic first-time setup workflow.

Installing and Using ggplot2 for the First Time
1
Step 1 — Check if the package is already installedBefore installing, check whether the package already exists in your library. Run "ggplot2" %in% installed.packages()[, "Package"] or equivalently requireNamespace("ggplot2", quietly = TRUE). If this returns FALSE, the package needs to be installed.
FALSE — package not found, installation needed.
2
Step 2 — Install from CRANRun install.packages("ggplot2") in the R console. R will prompt you to select a CRAN mirror (or use the default if one is already set). The installer will resolve dependencies—ggplot2 depends on packages like scales, tibble, and rlang—and install them all. On a fresh system this may install 20+ packages. You will see output lines like "trying URL...", "downloaded X MB", and "package 'ggplot2' successfully unpacked".
Console output: package 'ggplot2' successfully unpacked and MD5 sums checked
3
Step 3 — Load the package into the sessionRun library(ggplot2). If successful, R may print startup messages about masking functions. You can suppress these with suppressPackageStartupMessages(library(ggplot2)). After loading, verify attachment by running search() and confirming "package:ggplot2" appears in the search path.
search() now includes "package:ggplot2"
4
Step 4 — Use a function from the packageCreate a quick scatter plot using the built-in mpg dataset: ggplot(mpg, aes(x = displ, y = hwy)) + geom_point(). If a plot renders, the package is correctly installed and loaded. This confirms the entire pipeline—from CRAN download to function execution.
A scatter plot of engine displacement vs. highway MPG renders in the graphics device.
5
Step 5 — Handle a common errorIf you restart R and try ggplot(mpg, aes(x = displ, y = hwy)) without first calling library(ggplot2), you will get: Error in ggplot(...): could not find function "ggplot". This is the most common beginner mistake—the package is installed but not loaded for the current session. The fix is simply to add library(ggplot2) to the top of your script.
Solution: Always include library() calls at the top of every script.

Comparing Package Management Approaches

R offers several functions and tools that interact with the package system, and choosing the right approach depends on your context. The following table compares the primary methods, highlighting when each is most appropriate and what pitfalls to avoid.

Comparison of R package management functions and tools
Function / ToolBehaviorBest Use CasePitfall
library()Loads and attaches; errors if missingTop of scripts and notebooksNone—fail-fast is desirable
require()Loads and attaches; returns FALSE if missingInside functions for conditional logicCan mask errors if not checked
pkg::func()Loads namespace only; no attachmentAvoiding namespace collisions; package developmentVerbose for heavy usage
pacman::p_load()Installs if missing, then loadsQuick prototyping, teaching environmentsBlurs install/load separation; not reproducible
renv::restore()Restores exact versions from lockfileProduction projects needing reproducibilityLearning curve; project-level setup required
KEY TAKEAWAY
In software engineering terms, install.packages() is analogous to pip install or npm install (dependency resolution and download to disk), while library() is analogous to Python's import or JavaScript's require() (making symbols available in the current runtime). The key difference is that R separates installation and loading more explicitly than Python, where import fails if a package isn't installed but never attempts to install it.

Connection to Reproducibility & Environment Management

The basic install.packages() and library() workflow is sufficient for exploratory analysis and coursework, but production-grade data science and collaborative research demand stronger guarantees. A script that runs today with dplyr 1.1.4 may break when a colleague installs dplyr 1.2.0 and a deprecated function is removed. This is the reproducibility problem, and R has developed several tools to address it.

Basic vs. advanced package management strategies
FeatureBasic WorkflowAdvanced (renv / Docker)
Version pinningInstalls latest version; no version controlrenv.lock records exact versions of every dependency
IsolationPackages shared across all projects via global libraryProject-local library; each project has its own package versions
Collaboration"Works on my machine" — collaborator must guess versionsLockfile committed to Git; renv::restore() reproduces exact environment
CI/CDMust manually specify packages in build scriptsAutomated restoration from lockfile; Docker images freeze entire OS + R + packages

As you progress in your R journey, you will encounter renv (the successor to packrat), which creates project-level library snapshots. The workflow is straightforward: renv::init() initializes a project, renv::snapshot() captures the current state, and renv::restore() recreates it on another machine. Think of it as a package-lock.json for R. Mastering install.packages() and library() is the prerequisite to understanding why tools like renv exist and how they enhance the basic workflow.

Practice Problems

PROBLEM 1CONCEPTUAL
Explain the difference between installing a package and loading a package in R. Why are these two separate steps, and what would happen if you tried to call ggplot() immediately after running install.packages("ggplot2") without calling library(ggplot2)?
PROBLEM 2BASIC
Write the R commands needed to: (a) install the stringr package from CRAN, (b) load it into your session, and (c) verify it is attached to the search path.
PROBLEM 3INTERMEDIATE
You are writing a function inside a package that optionally uses jsonlite for JSON export. You want the function to work even if jsonlite is not installed, falling back to a CSV export. Write the conditional logic using the appropriate loading function, and explain why you chose it over the alternative.
PROBLEM 4APPLIED
You need to use the DESeq2 package (a Bioconductor package for differential gene expression analysis) and the development version of tidymodels from GitHub. Write the complete installation commands for both, including any prerequisite packages you need to install first.
PROBLEM 5CRITICAL THINKING
A colleague shares an R script that begins with install.packages(c("dplyr", "ggplot2", "readr")) at the top, followed by the corresponding library() calls. Critique this approach from the perspectives of (a) performance, (b) reproducibility, (c) security, and (d) etiquette in shared computing environments. Propose a better alternative.

Summary

R's power lies in its extensible package ecosystem, hosted primarily on CRAN with over 20,000 contributed packages. The function install.packages() downloads a package from a repository, resolves its dependencies, compiles any native code, and stores the result in a local library directory on disk—a persistent, one-time operation. The function library() then loads and attaches that package's namespace to the search path of the current R session, making exported functions directly callable.

Key distinctions to remember: install.packages() requires quoted package names and modifies disk state, while library() accepts unquoted names and modifies session state. Use require() for conditional loading inside functions, and the pkg::func() syntax to call functions without full attachment. For production and collaborative workflows, tools like renv build on these fundamentals to provide version-locked, reproducible environments.

Varsity Tutors • R Programming • Installing & Loading Packages