Skip to main content

Official websites use .gov
A .gov website belongs to an official government organization in the United States.

Secure .gov websites use HTTPS
A lock ( ) or https:// means you’ve safely connected to the .gov website. Share sensitive information only on official, secure websites.

Duplicating Quarto elements with code templates to reduce copy and paste errors

Quarto provides easier-than-ever ways to create data-driven, reproducible documents. This blog demonstrates how to use custom code templates to easily replicate code chunks with a reproducible USGS streamgage example!

Date Posted May 20, 2025 Last Updated June 2, 2025
Author Althea A. Archer
Reading Time 7 minutes Share

Introduction

The traditional approach for creating a series of figures, tables, or statistics in R Markdown or Quarto is to create a separate chunk for each set of data. This often relies on copy + paste, is tedious, and error prone. The template approach allows you to automatically apply a template chunk of code to each set of data. This approach is more reproducible, automated, and easily expanded to many sets of data.

It is a common situation in data science and analysis: We want to create a series of figures, tables, or summary statistics for a set of data. Maybe we’re studying different species of irises or penguins or the current flow conditions for various streamgages (the example used below), and we want a different summary figure or table for each. One common approach is to write code for one set of the data, such as setting up the graphing parameters in a ggplot data visualization or calculating a series of statistics for a single species/streamgage. Then, once happy with that, copying and pasting the code for each entity, modifying the code slightly for each iteration.

There are problems with this copy + paste + modify approach: It can be tedious and is error-prone. It’s difficult to do this for more than a handful of instances, and if we want to change something later, we’re faced with making sure all the changes are recreated in each chunk. Learn other ways to improve iteration in our previous blog post .

The goal with this blog is to provide a simpler way of creating a report or slideshow in markdown (either .Rmd or Quarto’s .qmd format) that is automated and relies on a template, making this type of process easier. I will first provide a simple example of the code to implement this approach. Then I will also demonstrate a couple more advanced examples specific to creating PowerPoint slides from Quarto code.

Iterating with the traditional approach

To begin, I will use streamflow data downloaded from our USGS dataRetrieval package, which is demonstrated in a previous blog . In this scenario, I am trying to create a PowerPoint presentation with a graph that shows a monthly hydrograph for each of three streamgages. I create a new Quarto document, create a code chunk to make the hydrograph for the first gage and then copy + paste + modify that chunk for the other two gages. This is what that Quarto might look like:

---
title: "Streamgage Example"
format: pptx
---

```{r, echo=FALSE}
library(tidyverse)
# Data will be downloaded from dataRetrieval
library(dataRetrieval)
flow_ks <- readNWISdata(
  # State Code
  stateCd = "KS",
  # Parameter Codes
  parameterCd = c("00060"),
  # Timeframe
  startDate = "2024-02-15",
  endDate = "2024-03-15",
  # Daily values
  service = "dv") |> 
  # Clean up the column names a bit
  renameNWISColumns() |>
  # Select the 3 focal gages
  filter(site_no %in% c("07170500", "07172000", "07182260"))
```

## Streamgage 07170500 summary

```{r}
ggplot(data = flow_ks |> filter(site_no == "07170500"), 
       aes(x = dateTime, y = Flow)) + 
  geom_line() +
  ggtitle("Gage 07170500") +
  theme_minimal()
```

## Streamgage 07172000 summary
  
```{r}
ggplot(data = flow_ks |> filter(site_no == "07172000"), 
       aes(x = dateTime, y = Flow)) + 
  geom_line() +
  ggtitle("Gage 07172000") +
  theme_minimal()
```

## Streamgage 07182260 summary
  
```{r}
ggplot(data = flow_ks |> filter(site_no == "07182260"), 
       aes(x = dateTime, y = Flow)) + 
  geom_line() +
  ggtitle("Gage 07182260") +
  theme_minimal()
```

The final PowerPoint looks like this:

A preview of a PowerPoint file that shows four simple slides. The first has the title of the document, Streamgage Example. The second through fourth slides each are specific to one gage and have a title of Streamgage ID summary and a simple hydrograph.

One strength to this “copy + paste + modify” approach is that it’s easy to implement, since we only slightly modify the same piece of code for each entity in our data set. A major weakness is that it’s difficult/tedious to generalize to more than a few entities, since this piece of code needs to be manually copied + pasted for each entity. In the next section, I will introduce a template-based approach that automates this process.

Iterating with the (better) template approach

The basic approach is to take advantage of the knitr package’s knit_child() function, which takes a child document or “template” and pastes a character string in the main document. Any R or markdown code we include in this template document will be treated as if we had written it in the main document. Here, I will keep the same structure of the parent Quarto document used in the previous section, which will have the YAML header that indicates I’m going to create a PowerPoint slide deck.

In the global header chunk of code, I will create a vector called gages containing the unique gage IDs and a wrapper function called child_function that will use knitr::knit_child(), the template slide layout called gage_example_child.qmd, and input data.

I can then use a for-loop (or purrr:map() if you prefer) to iterate over the elements of the gages vector, inserting each gage ID into the gage_example_child.qmd template and pasting the resulting R code into the main document. See further below for the contents of the gage_example_child.qmd template.

Note that you do need to put the chunk argument results="asis" for the Quarto markdown rendering engine to read the input child code as executable markdown and R code.

I then end the looping function with cat(unlist(out_slide), sep = " ") which helps the Quarto rendering engine recognize the text from the child function as executable.

This is what the new main quarto document looks like:

---
title: "Streamgage Example with template"
format: pptx
---

```{r, echo=FALSE}
library(tidyverse)
# Data will be downloaded from dataRetrieval
library(dataRetrieval)

# What are you looping over? Here, the focal gages
gages <- c("07170500", "07172000", "07182260")

# Data will be downloaded from dataRetrieval
library(dataRetrieval)
flow_ks <- readNWISdata(
  # State Code
  stateCd = "KS",
  # Parameter Codes
  parameterCd = c("00060"),
  # Timeframe
  startDate = "2024-02-15",
  endDate = "2024-03-15",
  # Daily values
  service = "dv") |> 
  # Clean up the column names a bit
  renameNWISColumns() |>
  # Select the 3 focal gages
  filter(site_no %in% gages)

# Function that reads in the template markdown
child_function <- function(this_gage){
  knitr::knit_child("gage_example_child.qmd",
                    envir = environment(),
                    quiet = TRUE)
}
```

```{r, results="asis"}
for(ss in 1:length(gages)){
  # Create slide content for the site
  out_slide <- child_function(this_gage = gages[ss])
  
  # Cat and unlist reads in the child function in and converts it to quarto code
  cat(unlist(out_slide), sep = "\n")
}
```

The template child gage_example_child.qmd file is simple and does not include a YAML header. This is the entire document:


## Streamgage `r this_gage` summary
  
```{r}
library(ggplot2)
library(tidyverse)
ggplot(data = flow_ks |> filter(site_no == this_gage), 
       aes(x = dateTime, y = Flow)) + 
  geom_line() +
  ggtitle(sprintf("Gage %s", this_gage)) +
  theme_minimal()
```

Note that the gage ID used in the child_function() call is inserted into this code in place of this_gage.

Now, when you render the main Quarto document, you get the same set of slides as before:

A preview of a PowerPoint file that shows four simple slides. The first has the title of the document, Streamgage Example with template. The second through fourth slides each are specific to one gage and have a title of Streamgage ID summary and a simple hydrograph.

The benefits of using the template approach

This is a very simplified example with only three gages, and it may not feel worth it to set up this template when you are only modifying three slides. However, one of the best benefits to this approach is that edits to the slides are easier. Instead of changing each gage’s chunk manually, you can just change the template once and all slides will get updated (see next two examples). Another benefit is that this approach easily expands to more than three data sets. For example, we would simply need to add to the gages character vector if we wanted hydrographs for additional gages. Anything that makes future you happy is a good thing to do!

The following sections show how you can add more features to the PowerPoint slides by adding to the template document. See the PowerPoint Quarto documentation for more tips and tricks.

Advanced Quarto -> PowerPoint Examples

1: Add columns and table to slides

A preview of a PowerPoint file that shows four simple slides. The first has the title of the document, Streamgage Example with template and columns. The second through fourth slides each are specific to one gage. Each gage slide has a title of Streamgage ID summary, a simple hydrograph in the left column, and a simple summary table in the second column.

With the template format set up for each gage’s slide, it’s very easy to add in additional features to the PowerPoint by changing the template. Changing the code in the template will change all of the resulting PowerPoint slides. For example, the template code below shows how you can add columns to each slide to make room for a summary table. Columns are added in Quarto markdown by first delineating a space to define columns with :::: columns (start) and :::: (end). Then, each column’s code will be placed in a new column beginning with ::: {.column} and ending with :::. This is the updated child item’s code to create two columns per slide. Note that the original “parent” markdown does not need to change at all!


## Streamgage `r this_gage` summary

:::: columns

::: {.column}
```{r}
library(tidyverse)
ggplot(data = flow_ks |> filter(site_no == this_gage), 
       aes(x = dateTime, y = Flow)) + 
  geom_line() +
  ggtitle(sprintf("Gage %s", this_gage)) +
  theme_minimal()
```
:::

::: {.column}

Variable    |     mean
----- | -- 
Mean flow | `r mean(flow_ks$Flow[flow_ks$site_no == this_gage])` cfs
Max flow | `r max(flow_ks$Flow[flow_ks$site_no == this_gage])` cfs

:::

::::

2: Add speaker notes

A preview of a PowerPoint file that shows one example slide and its speaker notes. The gage's slide has a title of Streamgage 07170500 summary, a simple hydrograph, and automatically populated speaker notes that states for gage 07170500, mean flow was 471.0266667 cfs and max flow was 2190 cfs.

One more handy thing that you can do with the Quarto-generated PowerPoint slides is add in speaker notes . Here, instead of being put into a table as in the prior example, the statistics for mean and max streamflow are added as speaker notes. In a Quarto presentation, the notes section begins with ::: notes and ends with :::. Again, with the template style, all we need to change is the template markdown document to change all of the slides in the final PowerPoint!


## Streamgage `r this_gage` summary

```{r}
library(ggplot2)
library(tidyverse)
ggplot(data = flow_ks |> filter(site_no == this_gage), 
       aes(x = dateTime, y = Flow)) + 
  geom_line() +
  ggtitle(sprintf("Gage %s", this_gage)) +
  theme_minimal()
```

::: notes

For Gage `r this_gage`, mean flow was `r mean(flow_ks$Flow[flow_ks$site_no == this_gage])` cfs and max flow was `r  max(flow_ks$Flow[flow_ks$site_no == this_gage])` cfs.

:::

Conclusions

By applying a template markdown document as a “child” of your main “parent” Quarto document, you can easily create and modify a series of figures, tables, or slides. The example shared here is simple, but can work as a reproducible guide for your next Quarto project. I hope that using this type of child-parent template format will save your future self lots of headaches and time!

Share:

Related Posts

  • Formatting guidance for USGS Samples Data Tables

    May 6, 2025

    Recently, changes were made to the delivery format of USGS samples data (learn more here ). In this blog, we describe the impact to users and show an example of how to use R to convert WQX-formatted water quality results data to a tabular, or “wide” view.

  • Reproducible Data Science in R: Iterate, don't duplicate

    July 18, 2024

    Illustration of a personified, cartoon water droplet wearing a yellow construction hat and working to build pipelines on a conveyer belt. Text: Iterate, don't duplicate. Reproducible Data Science with R. Easy steps to improve the reusability of your code! A W.D.F.N. Blog Series.

    Overview

    This blog post is part of a series that works up from functional programming foundations through the use of the targets R package to create efficient, reproducible data workflows.

  • Reproducible Data Science in R: Writing better functions

    June 17, 2024

    A smiling raindrop in a hardhat works on a pipe assembly line beneath the blog series title, Reproducible Data Science with R and to the left of the blog post title, Writing better functions.

    Overview

    This blog post is part of a series that works up from functional programming foundations through the use of the targets R package to create efficient, reproducible data workflows.

  • Reproducible Data Science in R: Writing functions that work for you

    May 14, 2024

    A smiling raindrop in a hardhat works on a pipe assembly line beneath the blog series title, Reproducible Data Science with R and to the left of the blog post title, Writing functions that work for you.

    Overview

    This blog post is part of a series that works up from functional programming foundations through the use of the targets R package to create efficient, reproducible data workflows.

  • Calculating Moving Averages and Historical Flow Quantiles

    October 25, 2016

    This post will show simple way to calculate moving averages, calculate historical-flow quantiles, and plot that information. The goal is to reproduce the graph at this link: PA Graph . The motivation for this post was inspired by a USGS colleague that that is considering creating these type of plots in R. We thought this plot provided an especially fun challenge - maybe you will, too!