Deploy a Shiny for R app to Connect Cloud

Deploy a Shiny for R app to Connect Cloud from GitHub, Positron or VS Code, RStudio, or the command line. Compare methods and follow the steps.

Skip the guide and deploy the sample app

Deploy the example Shiny for R app to Connect Cloud

In this guide, you deploy a Shiny app written in R to Connect Cloud and get a URL you can share. You can deploy from GitHub, from Positron or VS Code, from RStudio, or from the command line. The sample app compares two types of free trial at converting users into customers, with filters, charts, and a summary table.

A dark-themed Shiny app with sidebar filters, a line chart of conversions over time, a bar chart of conversion rates, summary boxes, and a table

The sample Shiny for R app running locally

The source code is available in the examples-shiny-r repository.

Getting started

Whichever method you choose, you need the following:

  • A Connect Cloud account.
  • A Shiny for R app. This guide uses the sample app. Its main file (Connect Cloud calls it the primary file) is app.R. It also has a data file at data/expansions.csv and a logo at www/logo.png.
  • If you publish from GitHub, a renv.lock or manifest.json file that lists the R packages your content uses. Posit Publisher and push-button publishing do this for you.

Save this file as app.R.

app.R
# Load packages used by the app. Install missing packages, if needed.
library(shiny)
library(bslib)
library(thematic)
library(tidyverse)
library(gitlink)

# Read data from a CSV file and perform data preprocessing
expansions <- read_csv("data/expansions.csv") |>
  mutate(evaluation = factor(evaluation, levels = c("None", "A", "B")),
         propensity = factor(propensity, levels = c("Good", "Average", "Poor")))

# Compute expansion rates by trial and group
expansion_groups <- expansions |>
  group_by(industry, propensity, contract, evaluation) |>
  summarize(success_rate = round(mean(outcome == "Won")* 100),
            avg_amount = round(mean(amount)),
            avg_days = round(mean(days)),
            n = n()) |>
  ungroup()

# Compute expansion rates by trial
overall_rates <- expansions |>
  group_by(evaluation) |>
  summarise(rate = round(mean(outcome == "Won"), 2))

# Restructure expansion rates by trial as a vector
rates <- structure(overall_rates$rate, names = overall_rates$evaluation)

# Define lists for propensity, contract and industry choices
propensities <- c("Good", "Average", "Poor")
contracts <- c("Monthly", "Annual")
industries <- c("Academia",
                "Energy",
                "Finance",
                "Government",
                "Healthcare",
                "Insurance",
                "Manufacturing",
                "Non-Profit",
                "Pharmaceuticals",
                "Technology")

# Set the default theme for ggplot2 plots
ggplot2::theme_set(ggplot2::theme_minimal())

# Apply the CSS used by the Shiny app to the ggplot2 plots
thematic_shiny()


# Define the Shiny UI layout
ui <- page_sidebar(

  # Set CSS theme
  theme = bs_theme(bootswatch = "darkly",
                   bg = "#222222",
                   fg = "#86C7ED",
                   success ="#86C7ED"),

  # Add title
  title = "Effectiveness of DemoCo App Free Trial by Customer Segment",

  # Add sidebar elements
  sidebar = sidebar(title = "Select a segment of data to view",
                    class ="bg-secondary",
                    selectInput("industry", "Select industries", choices = industries, selected = "", multiple  = TRUE),
                    selectInput("propensity", "Select propensities to buy", choices = propensities, selected = "", multiple  = TRUE),
                    selectInput("contract", "Select contract types", choices = contracts, selected = "", multiple  = TRUE),
                    "This app compares the effectiveness of two types of free trials, A (30-days) and B (100-days), at converting users into customers.",
                    tags$img(src = "logo.png", width = "100%", height = "auto")),

  # Layout non-sidebar elements
  layout_columns(card(card_header("Conversions over time"),
                      plotOutput("line")),
                 card(card_header("Conversion rates"),
                      plotOutput("bar")),
                 value_box(title = "Recommended Trial",
                           value = textOutput("recommended_eval"),
                           theme_color = "secondary"),
                 value_box(title = "Customers",
                           value = textOutput("number_of_customers"),
                           theme_color = "secondary"),
                 value_box(title = "Avg Spend",
                           value = textOutput("average_spend"),
                           theme_color = "secondary"),
                 card(card_header("Conversion rates by subgroup"),
                      tableOutput("table")),
                 col_widths = c(8, 4, 4, 4, 4, 12),
                 row_heights = c(4, 1.5, 3))
)

# Define the Shiny server function
server <- function(input, output) {

  # Provide default values for industry, propensity, and contract selections
  selected_industries <- reactive({
    if (is.null(input$industry)) industries else input$industry
  })

  selected_propensities <- reactive({
    if (is.null(input$propensity)) propensities else input$propensity
  })

  selected_contracts <- reactive({
    if (is.null(input$contract)) contracts else input$contract
  })

  # Filter data against selections
  filtered_expansions <- reactive({
    expansions |>
      filter(industry %in% selected_industries(),
             propensity %in% selected_propensities(),
             contract %in% selected_contracts())
  })

  # Compute conversions by month
  conversions <- reactive({
    filtered_expansions() |>
      mutate(date = floor_date(date, unit = "month")) |>
      group_by(date, evaluation) |>
      summarize(n = sum(outcome == "Won")) |>
      ungroup()
  })

  # Retrieve conversion rates for selected groups
  groups <- reactive({
    expansion_groups |>
      filter(industry %in% selected_industries(),
             propensity %in% selected_propensities(),
             contract %in% selected_contracts())
  })

  # Render text for recommended trial
  output$recommended_eval <- renderText({
    recommendation <-
      filtered_expansions() |>
      group_by(evaluation) |>
      summarise(rate = mean(outcome == "Won")) |>
      filter(rate == max(rate)) |>
      pull(evaluation)

    as.character(recommendation[1])
  })

  # Render text for number of customers
  output$number_of_customers <- renderText({
    sum(filtered_expansions()$outcome == "Won") |>
      format(big.mark = ",")
  })

  # Render text for average spend
  output$average_spend <- renderText({
      x <-
        filtered_expansions() |>
        filter(outcome == "Won") |>
        summarise(spend = round(mean(amount))) |>
        pull(spend)

      str_glue("${x}")
  })

  # Render line plot for conversions over time
  output$line <- renderPlot({
    ggplot(conversions(), aes(x = date, y = n, color = evaluation)) +
      geom_line() +
      theme(axis.title = element_blank()) +
      labs(color = "Trial Type")
  })

  # Render bar plot for conversion rates by subgroup
  output$bar <- renderPlot({
    groups() |>
      group_by(evaluation) |>
      summarise(rate = round(sum(n * success_rate) / sum(n), 2)) |>
      ggplot(aes(x = evaluation, y = rate, fill = evaluation)) +
        geom_col() +
        guides(fill = "none") +
        theme(axis.title = element_blank()) +
        scale_y_continuous(limits = c(0, 100))
  })

  # Render table for conversion rates by subgroup
  output$table <- renderTable({
    groups() |>
      select(industry, propensity, contract, evaluation, success_rate) |>
      pivot_wider(names_from = evaluation, values_from = success_rate)
  },
  digits = 0)
}

# Create the Shiny app
shinyApp(ui = ui, server = server)

Get data/expansions.csv and www/logo.png from the sample repository.

To preview the app before you deploy, open app.R in RStudio and click Run App, or run this from the folder that contains app.R:

shiny::runApp()

Choose a deployment method

Method When to use it Jump to
GitHub Your code is on GitHub and you want it to update when you push (public repositories only on free accounts) Deploy from GitHub
Posit Publisher You work in Positron or VS Code and do not use GitHub Deploy from Positron or VS Code
Push-button publishing You work in RStudio and do not use GitHub Deploy from RStudio
rsconnect You want to deploy from the R console or automate deployments Deploy from the command line

For more on how these methods differ, see Publishing.

Deploy from GitHub

Use this method when your app’s code is in a GitHub repository and you want changes you push to publish automatically. On a free account, your repository must be public, so anyone can see your code.

  1. Create a manifest.json file by running this in the R console from the folder that contains app.R:

    rsconnect::writeManifest()
  2. Commit app.R, manifest.json, data/expansions.csv, and www/logo.png, then push your app to a GitHub repository.

  3. Sign in to Connect Cloud.

  4. Click Publish at the top of your Home page. If this is your first time publishing, Connect Cloud prompts you to install the GitHub App, which lets Connect Cloud read your repository.

  5. Select Shiny.

  6. Select your repository.

  7. Check that Branch is the branch that has your content, usually main.

  8. Select app.R as the Primary file.

  9. Leave Automatically publish on push on if you want Connect Cloud to update what you published each time you push to this branch. Turn it off to update only when you choose.

  10. Click Publish.

  • Result: Connect Cloud shows a log of the install steps while it deploys. When it finishes, your app is live at an address like https://[content-id].share.connect.posit.cloud, where [content-id] is a unique identifier that Connect Cloud picks. You can set a custom name to change the URL.
  • Update or redeploy: Commit and push to the connected branch and Connect Cloud republishes automatically. If you turned off automatic publishing, click Republish on the content’s card on your Home page. See Republishing.

For all options, see Publish from GitHub.

Deploy from Positron or VS Code

Use this method when you work in Positron or VS Code and want to publish your app directly. Positron includes the Posit Publisher extension. In VS Code, install it from the marketplace first.

  1. Open your project folder in Positron, or in VS Code with Posit Publisher installed.
  2. Click the Posit Publisher icon in the Activity Bar.
  3. Next to CREDENTIALS, click + and select Posit Connect Cloud. A credential is a saved connection to your Connect Cloud account. Your browser opens.
  4. In your browser, check that the Authorize Access code matches the code in Positron or VS Code. Then click Continue and Authorize.
  5. Return to Positron or VS Code and accept or change the name for this credential. Do this once.
  6. Click the + next to Deployments. If Posit Publisher asks which file to publish, select app.R.
  7. Enter a title for your content, then select the credential you just added.
  8. Under PROJECT FILES, check that the list includes app.R, data/expansions.csv, and www/logo.png. Connect Cloud uploads only the files in this list.
  9. Click Deploy Your Project.
  • Result: A success message appears in the lower right of Positron or VS Code. Click View Content in the extension to open the app on Connect Cloud. Its address has the form https://[content-id].share.connect.posit.cloud, where [content-id] is unique to your app.
  • Update or redeploy: Save your changes, open Posit Publisher, select the previous deployment, and click Deploy Your Project again. Keep the .posit folder in your project. It remembers which Connect Cloud content this project publishes to, so the next deploy updates the same content instead of creating a new one.

For the configuration file, multiple deployments per project, and troubleshooting, see Publish from your IDE.

Deploy from RStudio

Use this method when you work in RStudio and want to publish your app directly with push-button publishing.

  1. Open app.R in RStudio.
  2. Click Publish in the editor toolbar.
  3. Follow the prompts to connect your Connect Cloud account and publish.
  • Result: RStudio opens your app on Connect Cloud.
  • Update or redeploy: Save your changes and click Publish again.

For setup instructions and supported content types, see Publish from your IDE and the RStudio IDE publishing documentation.

Deploy from the command line

Use this method to script deployments from the R console with the rsconnect package. You do not need a manifest.json or renv.lock file for this method.

  1. Install the package.

    install.packages("rsconnect")
  2. Add your Connect Cloud account. Your browser opens for you to authorize access.

    rsconnect::connectCloudUser()
  3. Deploy the folder that contains app.R, data/expansions.csv, and www/logo.png.

    rsconnect::deployApp()

If your app is in a subdirectory, pass it with appDir, for example rsconnect::deployApp(appDir = "Dashboard"). If you have several accounts configured, rsconnect asks which one to use.

  • Result: A successful deployment opens your browser to your content’s page on Connect Cloud.
  • Update or redeploy: Run rsconnect::deployApp() again.

For noninteractive credentials and other options, see Publish from your console or terminal.

After you deploy

Common things to set up next. They work the same however you deployed your app:

  • Passwords and API keys: Store them as secret variables, not in your code. Add them under Variables in content settings, or during publishing. Read them with Sys.getenv("NAME").
  • Access and sharing: Control who can view your app in Sharing.
  • Scheduling: On paid plans, schedule your app to republish at set times, for example, to refresh data each day.
  • Custom name and domain: Change the app’s URL with a custom name or a custom domain.
  • Upload size limit: The upload size limit applies when you upload your project files from your editor, the R console, or the command line.

Troubleshooting

  • Missing package error: Re-run rsconnect::writeManifest() so manifest.json lists every package your app loads, then commit it and republish.
  • Missing data file: Commit the files to the repository, or include them in the upload. Keep expansions.csv in the data folder and logo.png in www.
  • Repository not listed: Confirm that the GitHub App has access to it.

If your deployment still fails, read the log that Connect Cloud shows while it deploys and look for the first error. You can also try Error Assist, which gives guidance based on your log output.

Next steps