Quarto
Quarto is an open-source scientific and technical publishing system built on Pandoc.
Content authored using Quarto can be published to Posit Connect. Quarto must be installed on your system and configured in Connect prior to its use. Contact your Posit Connect administrator if Quarto support is not available. Information about setting up Quarto in Connect is available in the Quarto chapter of the Admin Guide
For information about authoring Quarto content, see the Quarto Guide.
Publishing Quarto content
There are multiple ways to publish Quarto content to Connect. See the linked documentation for each method for more information on how to use it to publish Quarto content.
Tool | Method | Supported Runtimes |
---|---|---|
RStudio | Push-button publishing | knitr , jupyter |
The quarto CLI |
quarto publish connect |
knitr , jupyter |
The quarto R package |
quarto_publish_[site|app|doc]() |
knitr , jupyter |
The rsconnect R package | deployApp(quarto = TRUE) ,writeManifest(quarto = TRUE) |
knitr , jupyter |
The rsconnect-python CLI |
rsconnect deploy quarto ,rsconnect write-manifest quarto |
jupyter |
The rsconnect-python CLI |
rsconnect deploy manifest |
knitr , jupyter |
If your Quarto content contains R code, you cannot use the rsconnect-python
CLI’s rsconnect deploy quarto
function. You can still use rsconnect deploy manifest
to deploy content for which a manifest has already been generated.
Quarto feature support
Quarto content supports executable code in multiple languages, and supports interactivity through a number of technologies.
Connect supports most Quarto capabilities, including:
- Rendering Python, R, and ObservableJS code.
- Running interactives written with Shiny, ObservableJS, and R
htmlwidgets
. - Rendering Quarto projects, documents, websites, and books.
Connect doesn’t support the following Quarto capabilities:
- Rendering Julia code chunks.
- Running interactives written with Jupyter Widgets.
Quarto project profiles
Quarto project profiles let your content adapt to different scenarios. For example:
Your development data sources might be different from the ones available in your deployed environment.
You might want to produce different versions from the same source code (e.g., including additional diagnostic information during development).
Quarto v1.2 adds support for project profiles. Older versions of Quarto do not automatically receive a value for QUARTO_PROFILE
.
Connect configures the QUARTO_PROFILE
environment variable when running Quarto content, which informs Quarto which profile is in use. By default, the value “connect” is used. Your Connect installation may override this default.
Add QUARTO_PROFILE
as a custom environment variable for your content if you need to use a different profile from the server default.
Some installations may not allow overriding QUARTO_PROFILE
. Attempts to set a value for QUARTO_PROFILE
will fail when this is the case.
Email customization
Posit Connect can send an email upon completion of scheduled execution or when manually requested in the dashboard. By default, the email message indicates when the results were generated and links to the hosted version of your content. Your Quarto content can customize this email message, letting you distribute results directly to your audience. An email message is not automatically sent during publishing or when manually run in the dashboard.
Email customization requires Quarto 1.4.
Getting started
Here is a Quarto document that renders to HTML and also produces a custom email message. For simplicity, this example does not contain any executable code blocks. Examples using Python and R are available below.
---
title: Something wonderful
format: email
---
Some of the content in this document makes its way to the rendered
HTML and some of it is destined for a custom email message. The`email` block customizes the subject and the body of the email
message, while the rendered HTML contains only this paragraph.
::: {.email}
::: {.subject}
Buy groceries
:::
Remember to pick up groceries on you way home. Tonight is "breakfast
for dinner" and we're having French Toast!
* Bread
* Eggs
* Butter
* Milk
:::
Rendering this document produces the HTML result in example.html
and a preview of the email message is contained in the email-preview
directory.
Details
Enabling email processing
Use format: email
to enable email processing for your Quarto document.
---
format: email
---
HTML body
The email message is defined within an email
block. Content outside the email
block is included in the HTML result and not the email message.
::: {.email}
`email` block.
This is the message body. It is contained within an :::
The email
block can appear anywhere in the HTML document, but can only appear once.
Images included in the email portion of the document (for example, plots) will be embedded in the email message. The email message body is self-contained and does not load external resources.
Email messages must be kept simple by necessity and cannot include interactive or otherwise complex elements.
Message subject
A custom message subject is specified by a subject
block. The subject
block is optional and must be contained within the email
block.
::: {.email}
::: {.subject}
The subject line.
:::
:::
Plain-text body
A text-only version of the message is specified within an email-text
block. The email-text
block is optional and must be contained within the email
block. When included, this text serves as a fallback when an email client cannot display HTML messages.
::: {.email}
::: {.email-text}
An optional text-only version of the email message.
:::
The HTML email content.
:::
Attachments
Add attachments to the email message with the email-attachments
document metadata field. The attachment filenames can exist alongside the Quarto document or be produced as a side-effect of rendering the document.
---
email-attachments:
- raw.csv
- summary.csv
---
Suppressing email
Use an email-scheduled
block to conditionally send email. The email-scheduled
block is optional and must be contained within the email
block.
In order to use this feature, the scheduling configuration for your content must request an email after update. Your code can use the email-scheduled
block to suppress sending of this scheduled email.
Prevent email sending by giving the content of this block FALSE
, False
, or "no"
(something falsy). Permit email sending by having the content of this block resolve to TRUE
, True
, or "yes"
(something truthy).
Here is an example to randomly suppress email.
```{python}
#| echo: false
import random
# Compute if we should send an email message.
send_email = random.choice([True, False])
```
::: {.email}
::: {.email-scheduled}`{python} send_email`
:::
This message is occasionally sent. :::
```{r}
#| echo: false
# Compute if we should send an email message.
send_email <- sample(c("yes", "no"), 1)
```
::: {.email}
::: {.email-scheduled}`{r} send_email`
:::
This message is occasionally sent. :::
Email preview
An HTML email preview is written into the email-preview
directory alongside your rendered HTML. Prevent creation of the preview using the email-preview
document metadata field.
---
email-preview: false
---
Environment variables
You may want to use the original subject or include links to your content in the email message. The following environment variables are provided to the Quarto render that can be referenced from your code.
RSC_EMAIL_SUBJECT
The default subject used when sending email. Computed when your content is rendered.
Example:
"Quarterly Sales Summary"
RSC_REPORT_NAME
The title of your content. Augmented with the variant name, when in use.
Example:
"Quarterly Sales Summary"
RSC_REPORT_URL
The default URL for this content. Includes a path to the variant, when in use. Contains placeholder text when rendering; replaced with the final URL when sending email.
Example:
"http://connect.company.com/content/42/"
RSC_REPORT_RENDERING_URL
The URL for this specific rendering of the content. Contains placeholder text when rendering; replaced with the final URL when sending email.
Example:
"http://connect.company.com/content/42/_rev1/"
RSC_REPORT_SUBSCRIPTION_URL
The URL that a subscriber can use to remove themselves. Contains placeholder text when rendering; replaced with the final URL when sending email.
Example:
"http://connect.company.com/connect/#/apps/42/subscriptions"
The final values of the URL environment variables are not known prior to rendering. Posit Connect renders your document using placeholder values for these environment variables. The placeholder values are replaced when constructing the final email message.
When rendering your content outside of Posit Connect, these environment variables will not have values.
Access these environment variables from code using os.environ
and os.getenv
in Python, Sys.getenv()
in R, or the Quarto env
shortcode.
You can use these variables anywhere within your email body or footer. These URLs are not appropriate for use in the body of your report.
For example, you might add a link to the hosted version of your content with the following Markdown code:
::: {.email}
Some very important information is presented here!
This email contains a summary of the most recent results.[hosted report]({{< env RSC_REPORT_URL >}})
Visit the
for additional details. :::
Use placeholder values for these environment variables in your development environment; they will be used when the content is rendered locally.
Terminal
export RSC_REPORT_URL="https://posit.co/"
Complete examples
These examples (in Python and R) dynamically construct both the rendered content and the email message. The document contains a scatter plot and a histogram, while the email message has a dynamic subject and includes the same scatter plot.
---
title: "Recent sales trends"
format: email
---
```{python}
#| echo: false
import datetime
import pandas as pd
import random
import matplotlib.dates as mdates
# Randomly generate data that acts as 30-day trailing unit sales.
today = datetime.datetime.today()
data = pd.DataFrame({
'Date': pd.date_range(
today - datetime.timedelta(days=30),
today - datetime.timedelta(days=1),
),
'Sales': [random.randint(75, 100) for _ in range(1, 31)],
})
last = data['Sales'].values[-1]
previous = data['Sales'].values[-2]
compared_to = "constant"
if last > previous:
compared_to = "higher"
elif last < previous:
compared_to = "lower"
def scatter():
ax = data.plot(
title = "Unit sales (30 days)",
x = "Date",
y = "Sales",
kind = "scatter",
grid = True)
# The x-axis is too crowded if we include every day.
ax.xaxis.set(
major_locator=mdates.WeekdayLocator(),
)
def hist():
ax = data.plot(
title = "Unit sale frequency (30 days)",
y = "Sales",
xlabel = "Sales",
legend = False,
kind = "hist",
bins = 5,
grid = True)
```
This document summarizes recent sales information, produces some
plots, and composes an email message containing the most important
details.
The scatter plot showing recent sales information is included in
both the rendered document and the email message.
```{python}
#| echo: false
scatter()
```
The histogram is only included in the rendered document.
```{python}
#| echo: false
hist()
```
::: {.email}
::: {.subject}`{python} compared_to`
sales are
:::
This email message has a dynamic subject and embeds the same
scatter plot that is in the rendered document.
```{python}
#| echo: false
scatter()
```
:::
---
title: "Recent sales trends"
format: email
---
```{r}
#| echo: false
library(ggplot2)
# Randomly generate data that acts as 30-day trailing unit sales.
today <- Sys.Date()
data <- data.frame(
Date = today - 1:30,
Sales = sample(75:100, 30, replace = TRUE)
)
last <- data$Sales[length(data$Sales)]
previous <- data$Sales[length(data$Sales) - 1]
compared_to <- if (last == previous) {
"constant"
} else if (last > previous) {
"higher"
} else {
"lower"
}
scatter <- ggplot(data = data, aes(x = Date, y = Sales)) +
geom_point() +
ggtitle("Unit sales (30 days)") +
theme_minimal()
hist <- ggplot(data = data, aes(y = Sales)) +
geom_histogram(bins = 5) +
ggtitle("Unit sale frequency (30 days)") +
theme_minimal()
```
This document summarizes recent sales information, produces some
plots, and composes an email message containing the most important
details.
The scatter plot showing recent sales information is included in
both the rendered document and the email message.
```{r}
#| echo: false
scatter
```
The histogram is only included in the rendered document.
```{r}
#| echo: false
hist
```
::: {.email}
::: {.subject}`{r} compared_to`
sales are
:::
This email message has a dynamic subject and embeds the same
scatter plot that is in the rendered document.
```{r}
#| echo: false
scatter
```
:::