










It’s been a long while since I announced updates in knitr last time, so I’m writing this post to cover notable changes in knitr v1.46–v1.52. For the full changelog, please see the release notes on GitHub. I hope you will find this 3-year review helpful.
tab.capYou can now set the table caption in the chunk header instead of inside
kable(), e.g.,
```{r tab.cap="Motor Trend car data (first six rows)"}
knitr::kable(head(mtcars))
```
Previously you had to write knitr::kable(head(mtcars), caption = "..."),
which is inconvenient when you want to keep chunk options together and the R
code clean
(#1679).
spin() recognizes # %% and #|Two more chunk delimiters work in spin() now. For example, a VS Code /
Jupyter-style script:
# My report
# %%
plot(cars)
# %% results="hide"
invisible(summary(cars))
And Quarto-style pipe comments:
#| label: fig-cars
#| fig-cap: "Speed vs. distance"
plot(cars)
Both can be spun into a document with knitr::spin("script.R"). The old #-
delimiter no longer works; please use #+ or any of the above new delimiters
(#2307, #2320).
...Before, every chunk hook had to declare arguments before, options, envir,
and name explicitly, e.g.,
# old: had to list explicitly
knitr::knit_hooks$set(my_hook = function(before, options, envir, name) {
if (before) cat("starting\n")
})
Now ... can be used to capture all and knitr will pass all arguments
implicitly to ... (if your hook doesn’t support any of these arguments, they
will be silently dropped):
knitr::knit_hooks$set(my_hook = function(before, ...) {
if (before) cat("starting\n")
})
If optipng or pngquant is on your PATH, vignette PNG plots are
automatically compressed during R CMD build. No code change needed—just
install the tools. PNG plots on CRAN are often surprisingly
large; for vignettes
with several plots this can meaningfully reduce the installed package size.
Any chunk header/footer mismatch now throws an error, e.g.,
````{r}
1 + 1
```
The opening fence has four backticks; the closing fence must match exactly (#2306).
R CMD checkknitr no longer skips tangling vignettes during R CMD check. The tangled R
script is then run, so check failures can now come from the vignette code
rather than the vignette build. CRAN sets
_R_CHECK_VIGNETTES_SKIP_RUN_MAYBE_=true so this mostly affects non-CRAN
checks (e.g., GitHub Actions). Add that environment variable to your workflow
if you want the old behavior.
kable() rows via global optionLarge data frames accidentally passed to kable() can generate enormous tables.
Set a global cap if you want to prevent an accidental huge table, e.g.,
options(knitr.kable.max_rows = 30)
knitr::kable(big_df) # only first 30 rows rendered, rest silently dropped
This release contains bug fixes only, to which you don’t need to pay attention.
ref.chunk = FALSEknitr’s <<label>> syntax for reusing chunk
code silently
mangles R code that legitimately uses <</>> as delimiters—for example,
glue::glue() variants that change the open/close markers:
myglue <- function(..., .envir = parent.frame()) {
glue(..., .open = "<<", .close = ">>", .envir = .envir)
}
x <- "<<NAME>> <- function(x) { <<BODY>> }"
Any <<...>> pattern on its own line gets silently removed by knitr, so
x ends up with missing lines in the knitted output. Set ref.chunk = FALSE
to turn off that substitution for the affected chunk
(#2360), e.g.,
```{r ref.chunk=FALSE}
...
x <- "<<NAME>> <- function(x) { <<BODY>> }"
```
fig.alt for LaTeXAlt text for figures now works in LaTeX output, e.g.,
```{r fig.alt="A scatter plot of speed versus stopping distance"}
plot(cars)
```
knitr emits \includegraphics[alt={A scatter plot...}]{...}, which is
supported by recent versions of LaTeX and enables accessible PDFs
(#2378).
knit_global() now accepts an environment argument, so you can redirect all
chunk evaluation to a custom environment. For example, the recommended pattern
saves and restores the old environment:
new_env <- new.env(parent = globalenv())
old_env <- knitr::knit_global(new_env)
on.exit(knitr::knit_global(old_env), add = TRUE)
knitr::knit("report.Rmd") # all chunks evaluated in new_env
This is useful for packages like multiverse that need to execute chunks in separate environments per analysis rather than the shared global environment.
Previously, an error in an inline expression like `r log(-1)` reported
the line range of the surrounding paragraph. Now it points to the exact line,
making it much faster to locate the offending expression in a long document
(#2387).
R CMD buildWhen a vignette fails to build, the full R traceback is now printed. Before, you only got the error message and had to reproduce the failure interactively to see where it came from (#2390).
kable() column alignment for Org ModeAlignment specifiers now render correctly in Org Mode output, e.g.,
knitr::kable(head(iris), format = "org", align = c("l","r","r","r","l"))
Previously the alignment was ignored and all columns appeared left-aligned (#2391).
combine_words() and write_bib() moved to xfunBoth functions gained a proper home in xfun (my base for miscellaneous functions), e.g.,
xfun::join_words(c("apples", "oranges", "bananas"))
# "apples, oranges, and bananas"
xfun::pkg_bib(c("knitr", "rmarkdown"), file = "refs.bib")
The knitr versions (combine_words(), write_bib()) remain as wrappers
and are not deprecated, but new code should prefer the xfun versions.
With otel / otelsdk installed and a tracer configured, knitting automatically emits spans you can send to any OpenTelemetry backend, e.g.,
library(otelsdk)
# configure your exporter, then:
knitr::knit("report.Rmd")
# → spans: "knitr processing", "knitr output", one "knit" span per chunk
Each per-chunk span records the chunk label and engine, so you can profile which chunks are slow in a large document (#2422).
You can run setup or teardown code around the entire knit, not just around each chunk, e.g.,
knitr::knit_hooks$set(
before.knit = function() {
con <<- DBI::dbConnect(RSQLite::SQLite(), "data.db")
},
after.knit = function() {
DBI::dbDisconnect(con)
}
)
fig.alt = '' emits empty alt attributeSetting fig.alt = "" now produces <img alt="" ...> instead of omitting the
attribute entirely. Use fig.alt = NA to omit it, e.g.,
# decorative image: alt="" tells screen readers to skip it
knitr::opts_chunk$set(fig.alt = "") # → <img alt="" ...>
# no attribute at all (old behavior for empty alt):
knitr::opts_chunk$set(fig.alt = NA) # → <img ...> (no alt)
The distinction matters for accessibility audits: alt="" explicitly marks
a figure as decorative; a missing alt is flagged as an error by most tools
(#2415).
fig.noteAdd a source note or explanatory note below a figure, separate from the caption, e.g.,
```{r fig.cap="US population", fig.note="Source: US Census Bureau"}
plot(uspop)
```
For HTML this becomes <p class="figure-note">Source: US Census Bureau</p>
inside the figure <div>, which you can style with CSS. For LaTeX it emits
\figurenote{Source: US Census Bureau} inside the figure environment (you can
redefine that command in the preamble, e.g., to use floatrow’s
\floatfoot). Typst output is also supported
(#2022).
.Rtyp input and knitr::rtyp vignette engineI wrote a dedicated post about this feature. In
short, you can now write R + Typst documents with a .Rtyp extension and knit
them like R Markdown, e.g.,
knitr::knit("report.Rtyp") # → report.typ
knitr::knit2pdf("report.Rtyp") # → report.pdf (via Typst)
I’m not sure how many people would actually use this at all, but at least I’m glad that Frank Harrell has been exploring his exciting new journey with the Typst support in knitr (#2401, #2283).
A new vignette engine means packages can ship Typst vignettes with just two
lines in DESCRIPTION:
VignetteBuilder: knitr
Suggests: knitr
and a vignette header:
%\VignetteEngine{knitr::rtyp}
%\VignetteIndexEntry{My Typst Vignette}
(#2447)
hook_plot_tex() respects animation.hookPreviously only hook_plot_html() was extensible via animation.hook /
animation.fun. Now the LaTeX hook respects the same option, e.g., to use
xmpmulti for Beamer overlays instead of animate:
knitr::opts_chunk$set(
animation.hook = function(x, options) {
# use \multiinclude from xmpmulti for Beamer overlays
paste0("\\multiinclude[format=png]{", xfun::sans_ext(x[1]), "}")
}
)
Without this change, LaTeX animations were locked to the animate package (#2452).
ragg::agg_webp()For example:
knitr::opts_chunk$set(dev = "agg_webp", fig.ext = "webp")
Requires ragg >= 1.5.0. WebP is typically 25–35% smaller than PNG at equivalent visual quality, useful for HTML reports where file size matters (#2434).
dev.args works with gridSVGFor example:
knitr::opts_chunk$set(
dev = "gridSVG",
dev.args = list(strict = FALSE, pointsize = 12)
)
knitr now routes strict to gridSVG::grid.export() and pointsize to
grDevices::svg(), so you no longer need a custom hook just to pass options
to gridSVG
(#2450, #2451).
When a single chunk produced more than one figure with captions, only the first caption survived because Pandoc treated consecutive images in one paragraph as inline (uncaptioned) images. knitr now inserts a blank line between captioned figures, e.g.,
```{r fig.cap=c("First plot","Second plot")}
plot(cars)
plot(pressure)
```
Both captions now appear correctly in HTML and PDF output (#2032, #1524, #1760).
include_graphics() paths when output dir differsFor example:
# Rendered with rmarkdown::render("report.Rmd", output_dir = "docs/")
knitr::include_graphics("images/fig1.png")
Previously this produced a broken path in the output because knitr resolved the path relative to the input directory, not the output directory. It now uses the output directory communicated by rmarkdown >= 2.32 (#2171).
knit2html() and friends use litedown directlyknit2html(), stitch(), knit_rd(), and the knitr::knitr vignette engine
now call litedown::mark() directly instead of going through the markdown
package. This is potentially a breaking change. Previously, markdown::mark()
silently fixed several common problems in vignette YAML headers before handing
off to litedown; now those fixes no longer happen. If your vignette was
relying on that silent repair, it will break. Specifically:
yes/no in YAML. YAML boolean values must be true/false in
litedown. If your vignette header contains e.g. toc: yes, change it to
toc: true.
bibliography without rbibutils. If your YAML has a bibliography:
field but the rbibutils package is not installed, markdown::mark() would
skip the field silently during R CMD check. Now you will get an error, so
add rbibutils to Suggests in DESCRIPTION if you use bibliography
in vignettes.
List items with - in YAML. litedown uses a different YAML syntax than
standard YAML for lists. Items starting with - at the beginning of a line
are not supported; see the litedown YAML
syntax for alternatives.
For packages using the knitr::knitr vignette engine, also update
DESCRIPTION to depend on litedown instead of markdown, e.g.,
Suggests: litedown
or if you need the bibliography support:
Suggests: litedown, rbibutils
Thanks to all contributors who filed issues or submitted pull requests for these releases: Abhraneel Sarma (@abhsarma), @aksigkvgithub, Arnaud Gallou (@arnaudgallou), @atusy, Sebastian Meyer (@bastistician), @blset, Alex Reinhart (@capnrefsmmat), Christophe Dervieux (@cderv), Patrick R (@codeZeilen), David Cser (@dcser123), Deepayan Sarkar (@deepayan), @DeliciousRoastPotato, @dlampart, David Kaplan (@dmkaplan2000), Chao Cheng (@fenguoerbian), Florian Kohrt (@fkohrt), Floris Vanderhaeghe (@florisvdh), Garrick Aden-Buie (@gadenbuie), @ggrothendieck, Hadley Wickham (@hadley), Watal M. Iwasaki (@heavywatal), Hedvig Skirgård (@HedvigS), Doug Hemken (@Hemken), Michael Higgins (@Higgs32584), Jameel Alsalam (@jameelalsalam), Jennifer Bryan (@jennybc), Jeroen Ooms (@jeroen), Johan Larsson (@jolars), Kevin Ushey (@kevinushey), @knokknok, Kyle F Butts (@kylebutts), Lee Mendelowitz (@LeeMendelowitz), Leonidas Zhak (@LeonidasZhak), L. Sandig (@lsandig), Markus Schlegel (@markschl), Max Schmit (@maxschmi), @mbs2016, @mclements, Matthew Michalska-Smith (@mjsmith037), Nicolás A. Méndez (@naikymen), Nan Xiao (@nanxstats), Olivia Box Power (@Olivia-Box-Power), Ott Toomet (@otoomet), Sebastian Kopf (@sebkopf), @shangeconnew, Charlie Gao (@shikokuchuo), Toby Dylan Hocking (@tdhock), Felix Turbanisch (@turbanisch), Ulrik Lyngs (@ulyngs).
It’s kind of hard to believe that knitr is almost 15 years old now. It has accompanied me for more than 1/3 of my life. Needless to say, this project wouldn’t be possible without the support from the wider R community, especially the 2500+ GitHub issues/PRs these years. If there’s anything else you need, please always feel free to file new issues, comment on/upvote old issues, or send pull requests: https://github.com/yihui/knitr Thank you!
As a freelancer (currently working as a contractor) and a dad of three kids, I truly appreciate your donation to support my writing and open-source software development! Your contribution helps me cope with financial uncertainty better, so I can spend more time on producing high-quality content and software. You can make a donation through methods below.
Venmo: @yihui_xie, or Zelle: [email protected]
Paypal
If you have a Paypal account, you can follow the link https://paypal.me/YihuiXie or find me on Paypal via my email [email protected]. Please choose the payment type as “Family and Friends” (instead of “Goods and Services”) to avoid extra fees.
If you don’t have Paypal, you may donate through this link via your debit or credit card. Paypal will charge a fee on my side.
Other ways:
| WeChat Pay (微信支付:谢益辉) | Alipay (支付宝:谢益辉) |
|---|---|
![]() |
![]() |
When sending money, please be sure to add a note “gift” or “donation” if possible, so it won’t be treated as my taxable income but a genuine gift. Needless to say, donation is completely voluntary and I appreciate any amount you can give.
Please feel free to email me if you prefer a different way to give. Thank you very much!
I’ll give back a significant portion of the donations to the open-source community and charities. For the record, I received about $30,000 in total (before tax) in 2024-25, and gave back about $15,000 (after tax).
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。