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
Note

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:

Connect doesn’t support the following Quarto capabilities:

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).

Note

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.

Note

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.

Note

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.

The rendered HTML produced by the initial example.

Rendered HTML

The email message preview produced by the initial example.

Email message preview

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}

This is the message body. It is contained within an `email` block.
:::

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"

Important

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.
Visit the [hosted report]({{< env RSC_REPORT_URL >}})
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}
sales are `{python} compared_to`
:::

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}
sales are `{r} compared_to`
:::

This email message has a dynamic subject and embeds the same
scatter plot that is in the rendered document.

```{r}
#| echo: false

scatter
```

:::