citezot citezot hex sticker: [@] in white on a red hexagon

The goal of citezot is to make it easy to cite references in Quarto or R Markdown documents using Zotero.

To use citezot, cite sources in your text as usual following Quarto (Pandoc) syntax. For example, manuscript.qmd might contain:

---
bibliography: references.bib
---

Species evolve by natural selection [@Darwin1859]. Extant land plants include two major lineages [@Kenrick1997a].

Both references should be in your Zotero library, with the citekeys Darwin1859 and Kenrick1997a saved for those items.

Calling cz_update_bib() generates the references.bib file containing only the references you cited, straight from Zotero:

library(citezot)
cz_update_bib("manuscript.qmd")
#> ✔ 2/2 cited keys found; 'references.bib' updated.
#> ℹ 2 from 1 document, 0 only in other files.

citezot can also look items up, add them from a DOI, and fix fields, always showing you what it is about to do first.

citezot talks to the Zotero web API, so it works without Zotero desktop running. This means you can use it, e.g., on a server, or to cite references in an automated document generated by GitHub Actions (automated CVs!).

citezot is not affiliated with or endorsed by Zotero.

Installation

citezot is not on CRAN. Install it from R-universe, which provides prebuilt binaries for macOS and Windows:

install.packages(
  "citezot",
  repos = c("https://joelnitta.r-universe.dev", "https://cloud.r-project.org")
)

To install the development version from GitHub instead, use pak:

# install.packages("pak")
pak::pak("joelnitta/citezot")

Setup

You need a Zotero API key and your user ID (kept in ~/.Renviron), and each item you cite needs a citekey. A read-only key is enough for everything except adding and editing items.

See the setup guide for step-by-step instructions.

Usage

Bibliography generation

cz_update_bib("manuscript.qmd") # writes the file named by `bibliography:`
cz_update_bib("manuscript.qmd", out = "references.yaml") # CSL-YAML instead of BibLaTeX

It will not quietly lose references

If a key you cited in the document is not in Zotero, cz_update_bib() stops and leaves the existing file untouched, because a bibliography that silently loses entries breaks the render. It will let you know the missing keys and what to do about them. (allow_incomplete = TRUE writes what was found anyway).

Fast enough to run on every render

Fetching a large library takes a while the first time (about 20 s for ~6,000 items, requested in parallel). With caching on, later runs download only what changed: about 0.3 s when nothing did.

# You can put this in your in .Rprofile
# The cache is stored in tools::R_user_dir("citezot", "cache")
options(citezot.cache = TRUE)

Caching is off unless you ask for it. The cache holds bibliographic metadata, never your key; cz_clear_cache() deletes it.

Finding, adding and fixing items

Everything that changes your library is a dry run unless you say otherwise: it reads, shows exactly what would happen, and writes nothing until you add dry_run = FALSE. There is no delete.

cz_find("darwin origin of species") # free text
#>       item           citekey author year
#> 1 MEIQ8FY5        Darwin1859 Darwin 1859
#> 2 MY4FLB35 katz2018extending   Katz 2018
#>                                                                                                                                   title
#> 1                On the origin of species by means of natural selection, or the preservation of favoured races in the struggle for life
#> 2 Extending the scope of Darwin’s ‘abominable mystery’: integrative approaches to understanding angiosperm origins and species richness
#>         doi            type
#> 1      <NA>            book
#> 2 10/gb4dc5 article-journal
cz_find(doi = "10.2307/1796430") # by DOI, ISBN or exact citekey
#>       item       citekey             author year
#> 1 JVIYHN3W MacArthur1967 MacArthur & Wilson 1967
#>                               title             doi type
#> 1 The theory of island biogeography 10.2307/1796430 book
cz_find(citekey = "Darwin1859")
#>       item    citekey author year
#> 1 MEIQ8FY5 Darwin1859 Darwin 1859
#>                                                                                                                    title
#> 1 On the origin of species by means of natural selection, or the preservation of favoured races in the struggle for life
#>    doi type
#> 1 <NA> book

cz_add("10.1016/0022-5193(64)90038-4", key = "auto")
#> ℹ Citekey formula `auth.lower + year + shorttitle(1).lower` read from your
#>   Zotero profile (Better BibTeX).
#> ℹ Dry run: nothing written. Use `dry_run = FALSE` to add this item.
#> • type: journalArticle
#> • title: The genetical evolution of social behaviour. I
#> • authors: Hamilton W.D.
#> • date: 1964-07
#> • in: Journal of Theoretical Biology
#> • DOI: 10.1016/0022-5193(64)90038-4
#> • citekey: hamilton1964genetical
#> This message is displayed once per session.

cz_update_item("Darwin1859", list(publisher = "John Murray"))
#> ℹ Dry run: nothing written. Use `dry_run = FALSE` to apply to item MEIQ8FY5 (On
#>   the origin of species by means of natural sele…).
#> • publisher: "J Murray" -> "John Murray"

Wherever a function takes an item, you can give a DOI, a Zotero item ID, an existing citekey, or a one-row result of cz_find().

Generating citekeys

key = "auto" builds the key from a Better BibTeX-style formula, such as auth.lower + year + shorttitle(1).lower (giving hamilton1964genetical).

The key formula comes from, in order: options(citezot.key_format = "..."); the pattern saved in your Zotero profile by Better BibTeX (citezot reads that one setting from Zotero’s prefs.js, only when it generates a key, and tells you once per session); or Better BibTeX’s default pattern. If the same key already exists, a letter is appended to the new one (a, b, …).

Only a documented subset of the formula language is supported; anything else is an error rather than a different key. Check what is in use with cz_key_format() and try a formula with cz_suggest_key().

item <- list(
  itemType = "journalArticle",
  title = "The genetical evolution of social behaviour. I",
  date = "1964-07",
  creators = list(list(
    creatorType = "author",
    firstName = "W. D.",
    lastName = "Hamilton"
  ))
)
cz_suggest_key(item, "auth.lower + year + shorttitle(1).lower")
#> [1] "hamilton1964genetical"
cz_suggest_key(
  item,
  "auth.lower + year + shorttitle(1).lower",
  existing = "hamilton1964genetical"
)
#> [1] "hamilton1964geneticala"

Automation

Instead of manually running cz_update_bib(), you may want to automatically run it every time your document is rendered.

A Quarto pre-render step in _quarto.yml will do this:

project:
  pre-render: Rscript -e 'citezot::cz_update_bib()'

If you use targets, you could specify a target that is re-checked on every tar_make():

tar_target(
  references,
  cz_update_bib("manuscript.qmd", quiet = TRUE),
  format = "file",
  cue = tar_cue(mode = "always")
)

How it differs from other packages

rbbt is built around RStudio, particularly its add-in for inserting citations while you write. It needs Zotero desktop running with the Better BibTeX plugin.

zot is a general-purpose Zotero client. It works at a lower (more direct) level, with versioned writes, batch plans, file attachments and read-only queries of the local database.

c2z imports references into Zotero from many sources (including DOIs, ISBNs and the Norwegian Cristin registry), and can edit, copy and delete items.

citezot seeks to be lightweight (minimal package dependencies), to make bibliography maintenance simple (one function call builds your bibliography), to be careful with your library (every write is a dry run unless you say otherwise, and nothing can be deleted), and to work across platforms, even without Zotero running.

These descriptions are based on each package’s documentation, not its code. Please check their pages for the current state.

Limitations

Development

To work on citezot, clone the repository and install its development dependencies (pak::local_install_dev_deps()). The checks that run before each commit use pre-commit; install it once (for example brew install pre-commit or pip install pre-commit), then enable the hooks in your clone:

pre-commit install

Getting help

Documentation, including a worked example, is at https://joelnitta.github.io/citezot/ (or vignette("manuscript") once installed). Please report bugs and ideas at https://github.com/joelnitta/citezot/issues.

License

citezot is released under the MIT license.