Search code examples
rshinyr-markdownmarkdownknitr

Why Shiny app layout changed when including Rmarkdown files?


I was playing around with a Shiny app that has multiple tabs using tabsetPanel and tabPanel. Everthing was fine until including a Rmarkdown file into one of the tabs. The whole app was reduced in size after the .rmd file was rendered to HTML. Also, the option number_sections: true did not do anything. Does anyone know how to work around this? Thanks!

Video of the issue

Video

App.R

if (!require("pacman")) install.packages("pacman")
pacman::p_load(skimr)
pacman::p_load(markdown)
pacman::p_load(knitr)
pacman::p_load(shiny)

# 1) Define UI for random distribution app ----
ui <- fluidPage(
  
  # App title ----
  titlePanel("Tabsets"),
  
  # Sidebar layout with input and output definitions ----
  sidebarLayout(
    
    # Sidebar panel for inputs ----
    sidebarPanel(
      
      # Input: Select the random distribution type ----
      radioButtons("dist", "Distribution type:",
                   c("Normal" = "norm",
                     "Uniform" = "unif",
                     "Log-normal" = "lnorm",
                     "Exponential" = "exp")),
      
      br(),
      
      # * Input: Slider for the number of observations to generate ----
      sliderInput("n",
                  "Number of observations:",
                  value = 500,
                  min = 1,
                  max = 1000)
      
    ),
    
    # * Main panel for displaying outputs ----
    mainPanel(
      
      # * Output: Tabset w/ plot, summary, and table, etc. ----
      tabsetPanel(type = "tabs",
                  tabPanel("Plot", plotOutput("plot")),
                  tabPanel("Summary", verbatimTextOutput("summary")),
                  tabPanel("RMarkdown", uiOutput('rmd_summary'))
      )
      
    )
  )
)

# 2) Define server logic for random distribution app ----
server <- function(input, output) {
  
  # Reactive expression to generate the requested distribution ----
  d <- reactive({
    dist <- switch(input$dist,
                   norm = rnorm,
                   unif = runif,
                   lnorm = rlnorm,
                   exp = rexp,
                   rnorm)
    
    dist(input$n)
  })
  
  # Generate a plot of the data ----
  output$plot <- renderPlot({
    dist <- input$dist
    n <- input$n
    
    hist(d(),
         main = paste("r", dist, "(", n, ")", sep = ""),
         col = "#007bc2", border = "white")
  })
  
  # Generate a summary of the data ----
  output$summary <- renderPrint({
    summary(d())
  })
  
  # Data Summary ----
  output$rmd_summary <- renderUI({
    HTML(mark_html(knit('data_summary.Rmd', quiet = TRUE)))
  })
  
}

# Create Shiny app ----
shinyApp(ui, server)

data_summary.Rmd

---
title: "Overview of the Data"
output: 
  html_document:
    number_sections: true
---

<br> 

# Data Statistics

## Skimr

```{r, echo=FALSE, warning=FALSE}
skimr::skim(iris)
```

Update

Based on Russ' and Yihui Xie's comments, here is what's needed

HTML(mark_html(knit('data_summary.Rmd', quiet = TRUE), 
                   output = NULL, options = list(number_sections = TRUE, toc = TRUE),  
                   template = FALSE))

Solution

  • Not sure exactly how, but it looks like the mark_html() function imports a template by default and that template has CSS files associated with it that overwrite the original shiny CSS. The easiest way to turn that off is to set the template argument to template=FALSE.

    This means that there is no (or at least minimal) imported CSS for the markdown rendering, so you may have to do some CSS tweaks to the rendered markdown part of the shiny afterwards. I didn't change the markdown file. Here's what the modified UI and server looks like:

    if (!require("pacman")) install.packages("pacman")
    pacman::p_load(skimr)
    pacman::p_load(markdown)
    pacman::p_load(knitr)
    pacman::p_load(shiny)
    
    # 1) Define UI for random distribution app ----
    ui <- fluidPage(
      
    # I added CSS to add cell space the table in the markdown file and add white space at 
    # the bottom of the page. More tweaks might be needed. 
    
      tags$head(
        tags$style(
          HTML("td, th {padding: 10px;} #rmd_summary {margin-bottom: 60px;}"))),
      
    # end of UI changes, server changes below
      
      # App title ----
      titlePanel("Tabsets"),
      
      # Sidebar layout with input and output definitions ----
      sidebarLayout(
        
        # Sidebar panel for inputs ----
        sidebarPanel(
          
          # Input: Select the random distribution type ----
          radioButtons("dist", "Distribution type:",
                       c("Normal" = "norm",
                         "Uniform" = "unif",
                         "Log-normal" = "lnorm",
                         "Exponential" = "exp")),
          
          br(),
          
          # * Input: Slider for the number of observations to generate ----
          sliderInput("n",
                      "Number of observations:",
                      value = 500,
                      min = 1,
                      max = 1000)
          
        ),
        
        # * Main panel for displaying outputs ----
        mainPanel(
          
          # * Output: Tabset w/ plot, summary, and table, etc. ----
          tabsetPanel(type = "tabs",
                      tabPanel("Plot", plotOutput("plot")),
                      tabPanel("Summary", verbatimTextOutput("summary")),
                      tabPanel("RMarkdown", uiOutput('rmd_summary'))
          )
          
        )
      )
    )
    
    # 2) Define server logic for random distribution app ----
    server <- function(input, output) {
      
      # Reactive expression to generate the requested distribution ----
      d <- reactive({
        dist <- switch(input$dist,
                       norm = rnorm,
                       unif = runif,
                       lnorm = rlnorm,
                       exp = rexp,
                       rnorm)
        
        dist(input$n)
      })
      
      # Generate a plot of the data ----
      output$plot <- renderPlot({
        dist <- input$dist
        n <- input$n
        
        hist(d(),
             main = paste("r", dist, "(", n, ")", sep = ""),
             col = "#007bc2", border = "white")
      })
      
      # Generate a summary of the data ----
      output$summary <- renderPrint({
        summary(d())
      })
      
    # I added the template = FALSE argument
    
      # Data Summary ----
      output$rmd_summary <- renderUI({
        HTML(mark_html(knit('data_summary.Rmd', quiet = TRUE) , template = FALSE))
      })
      
    }
    
    # Create Shiny app ----
    shinyApp(ui, server)
    

    Here's what it looks like: unchanged shiny layout