In this post I will describe some of the ways I’ve found to conditionally include content in a Quarto-generate HTML document.
I’m using RStudio and the knitr rendering engine. If you know of Jupyter solutions please let me know! I will happily link to them for the disappointed Pythonistas who have ended up on my blog ;)
Quarto
The {{< include >}} shortcode
This isn’t conditional, but it is really useful.
If you have company marketing boilerplate, or headers that start all of your reports, a neat solution is to keep these in their own qmd file and bring them into any other qmd using the include shortcode.
In your report you would use the code below
{{< include _content.qmd >}}
This is akin to manually copying and pasting in the contents of content.qmd
prior to the report getting rendered.
We’ll come back to the shortcode later.
Conditional content
To my mind this one unfairly hogs the “conditional” title.
This is for when you have parts of a document that you only want rendered (or not) to a specific format, such as HTML, PDF, etc.
If you’re interested in this, here’s the Quarto guide.
Project profiles
In this scenario you will have content to be included (or excluded) based on the profile/s specified. The div below would only be included if the “advanced” profile is in use
::: {.content-visible when-profile="advanced"}
This content will only appear in the advanced version. :::
You can also specify configurations in a profile-specific YAML that can be fully, or selectively used, to override those in the default profile. Here are the docs.
Code flow
Most of the following examples follow the usual if/else structure.
I’ll be referring to params$my_boolean
throughout this section so let me take a moment to explain.
In the YAML header of a Quarto document you can pass in parameters. These get stored in a list params
that can be called throughout the document, just like any variables you make and keep in the global environment.
---
params:
my_boolean: FALSE
---
Reports made this way are known as parameterised reports.
If you’re using a Quarto project it is also possible to include variables in a root directory file _variables.yml
. I’m not doing that here, but if it sounds appropriate for you, here are the docs.
Inline code
You can conditionally execute inline code. If my_boolean
is TRUE
the example below results in a level 2 heading that says “Plots for mtcars”.
## Plots `r if (params$my_boolean) "for mtcars" else "for iris"`
In functions
Sometimes you just need a bit of perspective. Rather than something convoluted, do you just need to make a function with flow control of some kind?
plotting_func <- function(dat, col1, col2) {
if (params$my_boolean) {
dat |>
ggplot2::ggplot() +
ggplot2::geom_point(ggplot2::aes({{ col1 }}, {{ col2 }}))
} else {
dat |>
ggplot2::ggplot() +
ggplot2::geom_boxplot(ggplot2::aes({{ col1 }}, {{ col2 }}))
}
}
Or if the content is amenable to it, something like this
plotting_func <- function(dat, col1, col2) {
dat |>
ggplot2::ggplot() +
if (params$my_boolean) {
ggplot2::geom_point(ggplot2::aes({{ col1 }}, {{ col2 }}))
} else {
ggplot2::geom_boxplot(ggplot2::aes({{ col1 }}, {{ col2 }}))
}
}
You could put the function into a code block. Or you could hide it away in a folder R/
and a script functions.R
. That’s useful if you write long ugly functions like me (lol), but also if you have multiple functions. In this case, you could just source the functions silently along with library calls at the start of the Quarto doc.
```{r}
#| include: false
library(dplyr)
library(downloadthis)
library(reactable)
source(file.path("R", "functions.R"))
```
If your content is too complicated for flow control inside of a function, then just put the content into separate functions in functions.R
, source
them as above, and use flow control in the Quarto doc
```{r}
if (params$my_boolean) {
fancy_model_a(mtcars, wt, mpg)
else {
} fancy_model_b(iris, Species, Sepal.Length)
}```
knitr::knit_child
Saving the best for last, because this one is really cool!
Remember how I started with the {{< include >}}
shortcode and said that it wasn’t conditional?
{{< include _content.qmd >}}
If anyone knows how to (simply) use the shortcode in a conditional manner I’d love to hear about it. In the meantime I consider the conditional version of this is using knitr::knit_child
.
Let’s say I make interactive plots of my data using Observable code blocks and keep this code in a separate Quarto doc ojs-cells.qmd
. The code can be added to the main Quarto doc under certain conditions, as dictated by a boolean param ojs_include
in the YAML header
```{r}
#| output: asis
if (params$ojs_include) {
cat(sep = "\n",
::knit_child(quiet = TRUE,
knitrtext = readr::read_file("ojs-cells.qmd")
)
)
}```
What’s happening here is
-
readr::read_file
brings inojs-cells.qmd
as a string -
cat
restores the line-by-line formatting and ‘prints’ the content - The
output: asis
chunk option means Quarto treats the content as raw markdown output
Worth a mention - though not about conditionality - is this demo by Mickaël Canouil. It shows how the knit_child
method can be called in a for loop to create n code chunks, and therefore n outputs (like tables and plots). This is great when you don’t know how many plots there will be or even what they are called.
Summary
I hope these approaches give you a practical set of tools for including conditional content in Quarto-published HTML documents.
You can use
The shortcode
{{< include >}}
to bring in a sub-documentPublish content dependent on output format using “conditional content”
-
if/else statements either inline or in code chunks. This can be combined with
- Functional programming by putting code into functions - either in code blocks in the document, or in a separate sourced file
- With knitr’s
knit_child
to bring in entire sub-documents as required
Thank you to Bruno Rodrigues and John MacKintosh for proofreading a draft of this post.