How to create multiple tabs in a shiny dashboard

Hi,
I am trying to create a basic multi tabs dashboard but the below code is not working well. It brings always the second tab's graph but there is nothing appearing in the first tab. Can someone guide me.

library(shiny)
library(shinydashboard)
library(dplyr)
library(kableExtra)
query_detail <- read_excel("Query Detail_ACT16404.xls")
ARMADA_Detail <- read_excel("ACT16404_DVS.xls")

Define UI for application that draws a histogram

ui <- dashboardPage(
header = dashboardHeader(title = "Queries Dashboard"),
sidebar =
dashboardSidebar(width=150, sidebarMenu(
menuItem("RAVE Query",tabName = "RAVEQueryDetail",
sliderInput("bins",
"Number of bins:",
min = 1,
max = 20,
value = 10
)),
menuItem("ARMADA",tabName = "ARMADAEditChecks")

    )),
dashboardBody(
    tabItems(
        tabItem(
            tabName = "RAVEQueryDetail",
            fluidRow(box(width=4, plotOutput("Marking_gr", width="200px",height="200px")), box(width=8, plotOutput("distPlot", width="500px",height="300px"))),
            fluidRow(box(tableOutput("QueryDetail")))
        ),
        tabItem(title = "ARMADA Query Distribution", tabName = "ARMADAEditChecks",
                fluidRow(box(width=4, plotOutput("ARMADA_gr", width="300px",height="200px"))))
    )
)

)

Define server logic required to draw a histogram

server <- function(input, output) {
output$distPlot <- renderPlot({
query_detail %>% filter(Query Text != "The field is required. Please complete.") %>%
group_by(Form, Field, Query Text) %>%
summarise(number_of_times = n()) %>% filter(number_of_times != 1) %>%
arrange(desc(number_of_times)) %>% ungroup() %>%
mutate(Check_order_ID = row_number()) %>%
filter(number_of_times >= input$bins) %>%
ggplot(aes(x= Check_order_ID, y=number_of_times, fill=Field), height = 50, width = 200) +
geom_bar(stat="identity", position=position_dodge()) + facet_wrap(~Form) +
theme(axis.text = element_text(size = 5))+
ggtitle("Top n query firing edit checks")
})
output$QueryDetail <- function(){
query_detail %>% filter(Query Text != "The field is required. Please complete.") %>%
group_by(Form, Field, Query Text) %>%
summarise(number_of_times = n()) %>% filter(number_of_times != 1) %>%
arrange(desc(number_of_times)) %>% ungroup() %>%
mutate(Check_order_ID = row_number()) %>%
filter(number_of_times >= input$bins) %>% kable() %>% kable_styling() %>% scroll_box(height = "300px", width = "700px")
}
output$Marking_gr <- renderPlot({
query_detail %>% filter(Query Text != "The field is required. Please complete.") %>%
group_by(Marking Gr. Name) %>%
summarise(number_of_times = n()) %>% ungroup() %>% mutate(total = sum(number_of_times), percent_Query = round((number_of_times/total)*100)) %>%
ggplot(aes(x="", y=number_of_times, fill=Marking Gr. Name)) +
geom_bar(stat="identity", width=1) +
coord_polar("y", start=0) + theme_void() +
geom_text(aes(label = paste0(number_of_times, " ", percent_Query, "%")), position = position_stack(vjust=0.5)) +
ggtitle("Query distribution between marking groups")
})
output$ARMADA_gr <- renderPlot({
ARMADA_Detail %>%
group_by(Marking Group) %>%
summarise(number_of_times = n()) %>% ungroup() %>% mutate(total = sum(number_of_times), percent_Query = round((number_of_times/total)*100, 2))%>%
ggplot(aes(x=Marking Group, y=number_of_times, fill=Marking Group)) +
geom_bar(stat="identity", width=1) +
geom_text(aes(label = paste(percent_Query, "%")), position = position_stack(vjust = .5, reverse = FALSE), size=3) + theme_classic() +
theme(plot.title = element_text(hjust=0.5),
axis.text = element_blank())

    })
    
}

Run the application

shinyApp(ui = ui, server = server)

If this is a tableOutput then it needs renderTable

Hi Nir,
Thanks for your reply. I changed to rendertable but in the first tab nothing is appearing, in the second tab the graph is appearing well. please find the code

library(shiny)
library(shinydashboard)
library(dplyr)
library(kableExtra)
query_detail <- read_excel("Query Detail_ACT16404.xls")
ARMADA_Detail <- read_excel("ACT16404_DVS.xls")
# Define UI for application that draws a histogram
ui <- dashboardPage(
    header = dashboardHeader(title = "Queries Dashboard"),
    sidebar =
        dashboardSidebar(width=150, sidebarMenu(
            menuItem("RAVE Query",tabName = "RAVEQueryDetail", 
                     sliderInput("bins",  
                                 "Number of bins:",
                                 min = 1,
                                 max = 20,
                                 value = 10                                                                                                                    
                     )),
            menuItem("ARMADA",tabName = "ARMADAEditChecks")
            
        )),
    dashboardBody(
        tabItems(
            tabItem(
                tabName = "RAVEQueryDetail",
                fluidRow(box(width=4, plotOutput("Marking_gr", width="200px",height="200px")), box(width=8, plotOutput("distPlot", width="500px",height="300px"))),
                fluidRow(box(tableOutput("QueryDetail")))
            ),
            tabItem(tabName = "ARMADAEditChecks",
                    fluidRow(box(width=4, plotOutput("ARMADA_gr", width="300px",height="200px"))))
        )
    )
)
# Define server logic required to draw a histogram
server <- function(input, output) {
    output$distPlot <- renderPlot({
        query_detail %>% filter(`Query Text` != "The field is required. Please complete.") %>% 
            group_by(Form, Field, `Query Text`) %>% 
            summarise(number_of_times = n()) %>% filter(number_of_times != 1) %>%
            arrange(desc(number_of_times)) %>% ungroup() %>% 
            mutate(Check_order_ID = row_number()) %>%
            filter(number_of_times >= input$bins) %>%
            ggplot(aes(x= Check_order_ID, y=number_of_times, fill=Field), height = 50, width = 200) +
            geom_bar(stat="identity", position=position_dodge()) + facet_wrap(~Form) +
            theme(axis.text = element_text(size = 5))+
            ggtitle("Top n query firing edit checks")
        })
    
        output$QueryDetail <- renderTable({
            query_detail %>% filter(`Query Text` != "The field is required. Please complete.") %>% 
                group_by(Form, Field, `Query Text`) %>% 
                summarise(number_of_times = n()) %>% filter(number_of_times != 1) %>%
                arrange(desc(number_of_times)) %>% ungroup() %>% 
                mutate(Check_order_ID = row_number()) %>%
                filter(number_of_times >= input$bins) %>% kable() %>% kable_styling() %>% scroll_box(height = "300px", width = "700px")
            
        })
        
        output$Marking_gr <- renderPlot({
            query_detail %>% filter(`Query Text` != "The field is required. Please complete.") %>% 
                group_by(`Marking Gr. Name`) %>% 
                summarise(number_of_times = n()) %>% ungroup() %>% mutate(total = sum(number_of_times), percent_Query = round((number_of_times/total)*100)) %>%
                ggplot(aes(x="", y=number_of_times, fill=`Marking Gr. Name`)) +
                geom_bar(stat="identity", width=1) +
                coord_polar("y", start=0) + theme_void() + 
                geom_text(aes(label = paste0(number_of_times, "  ", percent_Query, "%")), position = position_stack(vjust=0.5)) +
                ggtitle("Query distribution between marking groups")
            
            })
        output$ARMADA_gr <- renderPlot({
            ARMADA_Detail  %>% 
                group_by(`Marking Group`) %>% 
                summarise(number_of_times = n()) %>% ungroup() %>% mutate(total = sum(number_of_times), percent_Query = round((number_of_times/total)*100, 2))%>%
                ggplot(aes(x=`Marking Group`, y=number_of_times, fill=`Marking Group`)) +
                geom_bar(stat="identity", width=1) + 
                geom_text(aes(label = paste(percent_Query, "%")), position = position_stack(vjust = .5, reverse = FALSE), size=3) + theme_classic() + 
                theme(plot.title = element_text(hjust=0.5),
                      axis.text = element_blank())
            
        })
        
    }

# Run the application 
shinyApp(ui = ui, server = server)

we dont have your excel files so it would be impossible for this code to run for us.
However, I think I understand your issue.
When you place a subitem under a menuitem, the menuitem stops acting like a toggle to the tab, but the sub item can be that toggle, so you can do...

sidebarMenu(
      menuItem(
        "RAVE Query",
        menuSubItem(
          tabName = "RAVEQueryDetail",
          sliderInput("bins",
            "Number of bins:",
            min = 1,
            max = 20,
            value = 10
          )
        )
      ),
      menuItem("ARMADA", tabName = "ARMADAEditChecks")

but then user needs to click on RAVE Query to get the slider to show and then click on the slider to change tab.
Its possible something can be done with shinyjs here, but this is the default behaviours.

Thanks a lot Nir. I got it.