Armed with the knowledge from the previous sections, we can now build the ggviolin shinytableau extension. Structurally, this will be very close to the Data Summary extension from the previous section, but the ggviolin extension makes use of additional features from Shiny itself.

The preamble is the same, with the addition of library(ggplot2):

Dashboard UI

The dashboard view’s UI differs in that it uses fillPage, and notice that the plot’s height is explicitly set to 100%. We also define a brush.

ui <- function(req) {
    plotOutput("plot", height = "100%",
      brush = brushOpts("plot_brush", resetOnNew = TRUE)

Dashboard server

The dashboard view’s server function uses a function we haven’t discussed before, tableau_select_marks_by_brush_async. This does exactly what the name implies: using the selection defined from a plotOutput brush, it drives selection on a Tableau worksheet. (This function has several limitations, including only working with ggplot2-based plots and only when the plot and the worksheet in question have their dimensions in common; see the ?tableau_select_marks_by_brush_async help topic for more details.)

Note also that tableau_setting() is called to retrieve plot_title, xvar, and yvar settings; we’ll be saving those settings in the config dialog. When writing your own ggplot2 extensions that let the user select what column to map to certain aesthetics, you’ll want to follow the pattern shown below, where the character vector (e.g. xvar) is passed to aes() using !!as.symbol(...).

server <- function(input, output, session) {
  df <- reactive_tableau_data("data_spec")

  observeEvent(input$plot_brush, {
    worksheet <- req(tableau_setting("data_spec")$worksheet)
    tableau_select_marks_by_brush_async(worksheet, input$plot_brush)

  output$plot <- renderPlot({
    plot_title <- tableau_setting("plot_title")
    xvar <- tableau_setting("xvar")
    yvar <- tableau_setting("yvar")

    df() %...>% {
      ggplot(., aes(x = !!as.symbol(xvar), y = !!as.symbol(yvar))) +
        geom_violin(draw_quantiles = c(0.25, 0.5, 0.75)) +

Config UI

The ggviolin config dialog uses the choose_data module, just as the Data Summary example did. It also prompts the user for a title, creates a uiOutput (that we’ll populate with xvar and yvar select inputs), and previews the selected data table using tableOutput.

config_ui <- function(req) {
    textInput("title", "Title"),
    choose_data_ui("data", "Choose data"),

Config server

The config dialog’s server function is the most complicated piece. We’ll take it a chunk at a time.

Validation rules

In the beginning of the function, shinyvalidate validation rules are added, which ensure that the user provides these values before settings can be saved.

config_server <- function(input, output, session, iv) {
  iv$add_rule("title", sv_required())
  iv$add_rule("xvar", sv_required())
  iv$add_rule("yvar", sv_required())

Choosing and previewing data

Next, we invoke the choose_data() module, passing in the same "data" id that we just passed to choose_data_ui() a few moments ago.

  data_spec <- choose_data("data", iv = iv)

For the ggviolin extension, not only do we want to save this data spec to the settings, we actually need to use it immediately:

First, for preview purposes, we want the first five rows of the selected table, and we use reactive_tableau_data for this.

  data <- reactive_tableau_data(data_spec, options = list(maxRows = 5))

  output$preview <- renderTable({

Choosing data columns

Second, for letting the user choose which of the selected table’s columns should map to the x and y dimensions, we need the schema (which contains the fieldnames, among other things), and we use reactive_tableau_schema to get it.

Like reactive_tableau_data, the return value for reactive_tableau_schema is a reactive expression; but unlike reactive_tableau_data, this reactive expression is not asynchronous (doesn’t return a promise), so there’s no need to worry about using %...>%.

  schema <- reactive_tableau_schema(data_spec)

  output$var_selection_ui <- renderUI({
      selectInput("xvar", "Dimension", schema()$columns$fieldName),
      selectInput("yvar", "Measure", schema()$columns$fieldName)

Save settings

Finally, we fulfill the final basic requirement for config server functions: we define and return the function that saves the user’s input to Tableau settings.

  save_settings <- function() {
      plot_title = input$title,
      data_spec = data_spec(),
      xvar = input$xvar,
      yvar = input$yvar

Putting it all together

All that’s left now is to hand all of these pieces over to tableau_extension.

  manifest, ui, server, config_ui, config_server,
  options = ext_options(config_width = 600, config_height = 600, port = 2468)

You can find the full source code, along with the manifest.yml file, here.

Here again is the video from the introduction, to remind you what it all looks like in action:

The end

And that’s it for our tutorial—thank you for sticking with it!

To learn more, check out the Articles section, where we touch on UI considerations and deployment/hosting.