Standalone Files
Robert Challen
2026-06-12
Source:vignettes/standalone-files.Rmd
standalone-files.Rmdusethis::use_standalone is designed for R package
development, to share useful functions between packages without creating
hard dependencies. pkgtools extends this concept to
analysis projects — plain Git repositories that don’t
have package infrastructure (no DESCRIPTION, no
namespace).
This vignette explains when and how to use standalone files, how they are managed, and how pkgtools integrates them into your workflow.
When to Use Standalone Files
Standalone files are useful when:
- You have utility functions reused across multiple projects
- Creating a full package is overkill for the amount of shared code
- You want the utility code to be version-locked into each project (improving reproducibility)
- Your projects are managed by
renvor have no dependency manager at all
Standalone files are not suitable when:
- You need formal package exports and NAMESPACE management
- The codebase is large enough to warrant a dedicated package
- You want CRAN or r-universe deployment
Project Structure
A standalone file repository looks like this:
my-standalone-repo/
├── README.md
├── R/
│ ├── standalone-utils.R
│ ├── standalone-plot-utils.R
│ └── standalone-data-utils.R
Each .R file in R/ whose name starts with
standalone- is a candidate standalone file. The file
contains:
- A YAML metadata block between
# ---dividers - R code with no package dependencies (or with dependencies declared in YAML)
Example standalone file:
# Standalone file: do not edit by hand
# Source: https://github.com/author/repo/blob/HEAD/R/standalone-utils.R
# Generated by: pkgtools::use_standalone("author/repo", "utils")
# ----------------------------------------------------------------------
#
# ---
# repo: author/repo
# file: standalone-utils.R
# last-updated: '2025-01-15'
# license: https://unlicense.org
# imports:
# - dplyr
# - rlang
# ---
#' My utility function
#'
#' @param x input vector
#' @return processed output
my_util <- function(x) {
dplyr::filter(x, !is.na(.))
}The YAML metadata block contains:
-
repo: GitHub repository spec
(
organisation/project) - file: the standalone filename
- last-updated: date the file was last modified
-
license: license URL (defaults to
https://unlicense.org) - dependencies: other standalone files in the same repo (for cross-references)
- imports: R packages required by this file
Importing Standalone Files
Use use_standalone() to import a standalone file into
your project:
# Import from a remote GitHub repository:
pkgtools::use_standalone("author/repo", "my-standalone")
# Use a local copy (if a matching repo exists in ~/Git/):
pkgtools::use_standalone("my-standalone-repo", "my-standalone")This function:
- Downloads the standalone file (from remote or local)
- Prepends a header with source information and generation command
-
Renames the file:
standalone-foo.Rbecomesimport-standalone-foo.R - Resolves dependencies: if the standalone imports other standalones, they are recursively downloaded
-
Manages imports: based on your project type:
-
Package projects: adds packages to
DESCRIPTIONviause_package() - renv-managed projects: installs packages and snapshots
-
Plain projects: adds a fenced block to
.RProfilethat checks for required packages and sources all local standalones
-
Package projects: adds packages to
Automatic Header Generation
The imported file begins with a header that documents its provenance:
# Standalone file: do not edit by hand
# Source: https://github.com/author/repo/blob/HEAD/R/standalone-utils.R
# Generated by: pkgtools::use_standalone("author/repo", "utils")
# ----------------------------------------------------------------------
#This header allows you to:
- Trace the origin of any shared utility
- Regenerate the file with the correct import command
- Identify when the file was last synchronized
Import Management in Non-Package Projects
For projects without a DESCRIPTION file, pkgtools
manages imports through a fenced block in .RProfile:
# Check dependencies:
if (!requireNamespace("dplyr", quietly = TRUE)) {
message("Package `dplyr` must be installed.")
}
# Deal with stats/dplyr issues:
if (!requireNamespace("conflicted", quietly = TRUE)) {
message("Package `conflicted` must be installed.")
} else {
conflicted::conflicts_prefer(
dplyr::filter(),
dplyr::lag(),
.quiet = TRUE
)
}
# Load standalones:
try(source("R/import-standalone-utils.R"))
try(source("R/import-standalone-plot-utils.R"))This block is managed automatically by pkgtools — adding a new
standalone updates the block. It also handles
conflicted::conflicts_prefer() for common tidyverse
conflicts.
Updating Standalone Files
When editing a standalone file in RStudio, use
update_standalone() to refresh its metadata:
# From RStudio with a standalone file open:
pkgtools::update_standalone("author/repo")This function:
- Parses the existing YAML metadata block
- Detects any changes to the file
-
Updates the
last-updatedtimestamp - Recalculates package imports from the code
-
Prompts for changes via the
merge_code()interactive diff viewer
If you haven’t provided a repo argument, the function tries to detect it from the file’s existing metadata or remote.
Syncing to Master
When developing a standalone file, you may want to keep your local
copy in sync with a “master” version in a dedicated repository. Use
sync_standalone_to_master() to push your local changes
back:
# From RStudio with a standalone file open:
pkgtools::sync_standalone_to_master()This function:
- Reads the YAML metadata to find the master repository
- Finds the master file path in your local Git directory
- Commits any pending changes to the master
-
Updates the
last-updateddate - Merges changes if both local and master have been modified
Workflow Example
1. Create standalone in ~/Git/my-standalones/R/standalone-utils.R
2. Import into project with use_standalone("my-standalones", "utils")
3. Edit the imported file in the project
4. Use update_standalone() to refresh metadata
5. Use sync_standalone_to_master() to push changes to master repo
6. Other projects can then import the updated version
Cross-Repository Dependencies
Standalone files can depend on other standalone files within the same repository. This is declared in the YAML metadata:
# ---
# repo: author/repo
# file: standalone-plot-utils.R
# dependencies:
# - standalone-utils.R
# - standalone-data-utils.R
# ---When you import standalone-plot-utils.R, pkgtools
automatically:
- Downloads the listed dependencies first
- Resolves transitive dependencies recursively
- Ensures all required standalones are available
This creates a dependency graph of standalone files, similar to package dependencies, but managed entirely through Git repositories.
Standalone Import Management
The standalone_imports() internal function collects all
package imports from every standalone file in a project. This is used
to:
- Deduplicate package dependencies
- Select maximum required version across all standalones
-
Generate the DESCRIPTION or
.RProfileimport block
For package projects, the imports are added to
DESCRIPTION. For renv projects, packages are installed and
snapshotted. For plain projects, they appear in the
.RProfile fenced block.
Best Practices
-
Use descriptive names:
standalone-plot-utils.Ris better thanstandalone-utils.R - Keep files focused: each standalone should handle one coherent responsibility (e.g., all plot formatting, all data cleaning)
-
Document dependencies: always list required
packages in the YAML
importsfield - Version lock: by embedding the standalone version in the project, you ensure reproducible analyses even if the master repository changes
-
Regular syncs: use
sync_standalone_to_master()to keep local and master in sync during active development -
License clearly: choose a license in the YAML
metadata;
https://unlicense.orgis the default (public domain)
Troubleshooting
“No standalone files found”
This error occurs when the target repository has no files in
R/ starting with standalone-. Check that:
- The file exists at
R/standalone-*.Rin the repository - The repository is accessible (correct host, credentials)
“Local standalone file not found”
When using a local copy, the function searches
~/Git/<repo-spec>/R/. Check that:
- The Git repository exists in your Git directory
- The standalone file is in the
R/subdirectory - The filename matches
standalone-*.R
HTTP/2 Issues
Some Git repositories require HTTP/2 for large file transfers:
Sys.setenv("GIT_CONFIG_PARAMETERS" = "http.version=2")Or set it globally:
Missing Dependencies
If a standalone requires a package that is not installed:
-
Package projects:
use_standalone()adds it toDESCRIPTION - renv projects: the package is installed and snapshotted
-
Plain projects: a message is displayed when the
project is opened, and the fenced block in
.RProfilechecks at load time
Related Functions
| Function | Purpose |
|---|---|
use_standalone() |
Import a standalone file |
update_standalone() |
Update metadata in RStudio |
sync_standalone_to_master() |
Push edits to master |
standalone_choose() |
Interactive file selection (internal) |
as_standalone_file() |
Sanitise filename (internal) |
standalone_header() |
Generate header comment (internal) |
standalone_imports() |
Collect imports from all standalones (internal) |
Limitations
- Standalone files work best for small utility functions (under ~200 lines)
- They don’t support formal S3/S4 method registration
- Cross-language dependencies (Python, JavaScript) are not managed
- Large standalone files may slow down project loading in plain projects
Funding
The authors gratefully acknowledge the support of the UK Research and Innovation AI programme of the Engineering and Physical Sciences Research Council EPSRC grant EP/Y028392/1.