Mwavu
Mwavu

Mwavu

Create A "Landing Page" For Your Shiny Apps

Create A "Landing Page" For Your Shiny Apps

Mwavu's photo
Mwavu
ยทApr 2, 2022ยท

7 min read

TL;DR

Q: How?

A: tabsetPanel(..., type = "hidden", ...)

Q: So what are you going to create today?

A:

s2.png

Inspiration: dataseries.org

What happened on 4th Feb 2022?

I spent a lot of time trynna create a "page" where a user first lands before proceeding to the main content. I mysteriously landed on dataseries.org (Yes, it's pun intended). And that's exactly what I wanted. I think it's an R shiny app but I'm not ๐Ÿ’ฏ% sure.

Question was, how do I create such?

image.png

Settled For Less

You know what I always tell myself?

First pluck the low hanging fruits. Maybe along the way you'll get a ladder, then you can go for the ones higher up the tree.
~ Mwavu, everyday.

My search efforts got me to this answer on stackoverflow.

Wait... I actually now know how I landed on dataseries.org. I googled "landing page r shiny", clicked on the first stackoverflow post and the OP had linked dataseries.org in his question. Ahaa, interesting!

Reminds me, you ever be in that situation where you google something purportedly for the first time, lo and behold... Not only is the link to the stackoverflow post purple but you also find you upvoted the answer?

At the time of writing this there were only two answers to the post:

image.png

"Two roads diverged in a yellow wood,
And sorry I could not travel both
And be one traveler..."

Anyway, I took the road less traveled.

All was well, till the inline CSS in my app was unapologetically cluttering my ui.R. So I switched to www/css/styles.css. I'm not sure but I think contents in the www/ directory are rendered after ui.R and server.R, I stand to be corrected. The side effect was that the main content (in this case the dashboard) was visible for a split second on app startup. Let's just say my eyes' frame rate is higher than the split second.

I didn't like it. I knew I had a problem.

I tried to search for a different, better answer but to no avail. I put the matter on hold, after all I had much better things to focus on like adding a clickable person icon to the top right of my dashboard which would show user details.

What happened yesterday, 31st March 2022?

image.png

Pretty sure you've had those moments in life which make you question your intelligence as a human being... Like "Why am I becoming such a disgrace to the human race?"

image.png

That was what happened yesterday.

I was going through the documentation of tabsetPanel() looking into type = "hidden". Of course it's not the first time knowing of that functionality. I continued with my usual development process, yunno, just doing my thing... Then suddenly...

THE EPIPHANY!

image.png

What would happen if I used tabsetPanel() with type = "hidden" and have one of the tabPanelBody()s as my landing page, then another would contain my dashboard, then I could actually have many "pages" on my application?

Of course the devil is always looking for the slightest chance to creep in and strangle your motivation to try out new things.

"That will break the UI of the app."

"Remember when you were a beginner and wrapped dashboardPage() inside of fluidPage()? It looked pathetic. Spacing all around your dashboard, didn't fit the page as you'd have liked. This won't be any different."

I was like "Yes I trust you, but it's time to verify your claims".

By the way talking about the devil, we never really got to hear this guy's side of the story. Nothing against believers but we need to an account of events from his own handwriting. He might just turn out to be the good guy, know what I'm sayin'? Just in case anyone has his story, link it up in the comments section. ๐Ÿ˜ˆ

The devil to me rn:

image.png

It's funny how it took me so long to realize that tabsetPanel(..., type = "hidden", ...) is a solution to my 57 decade unsolved problem.

image.png

I must say today's ted talk was quite something. Shall we now get our hands dirty with some code?

Create Your Dream R Shiny Landing Page

This is quite a small application, I can do with app.R... But no... Let's just go with ui.R, server.R, global.R, www/, and maybe R/?

Step 1: global.R

I usually use global.R to load all the packages and also do any global calculations/manipulations:

library(shiny)
library(shinydashboard)
library(rversions) # to get R versions
library(shinyjqui) # for jquery animations in UI

# Get all r versions, omit the ones which didn't have nicknames:
versions <- r_versions() |> na.omit()

# we'll have a `selectInput()` on the UI whose choices will be:
choices <- c(
  "An Earlier Version", 
  paste(versions$version, versions$nickname)
)

Pretty self-explanatory stuff.

Step 2: ui.R

Nothing outlandish, just normal stuff... Let's just say I am getting a bit more creative with arranging and fitting UI components together:

# ----dashboard header----
header <- dashboardHeader(
  title = "My Dashboard"
)

# ----dashboard sidebar-----
sidebar <- dashboardSidebar(
  sidebarMenu(
    menuItem(
      text = "Menu 1", 
      tabName = "menu1", 
      selected = TRUE
    ), 

    menuItem(
      text = "Menu 2", 
      tabName = "menu2"
    ), 

    menuItem(
      text = "Menu 3", 
      tabName = "menu3"
    )
  )
)

# ----dashboard body----
body <- dashboardBody(
  tabItems(
    tabItem(
      tabName = "menu1", 

      tags$h3(
        "Menu 1 Should Appear Here!"
      )
    ), 

    tabItem(
      tabName = "menu2", 

      tags$h3(
        "Menu 2 Should Appear Here!"
      )
    ), 

    tabItem(
      tabName = "menu3", 

      tags$h3(
        "Menu 3 Should Appear Here!"
      )
    )
  )
)

# ----ui----
ui <- tabsetPanel(
  id = "panels", 
  type = "hidden", 
  selected = "landing_page", 

  tabPanelBody(
    value = "landing_page", 

    # link css stylesheet:
    includeCSS(path = "www/css/styles.css"), 

    tags$div(
      class = "landing_page_container", 

      tags$div(
        class = "landing_page_header", 

        tags$div(
          class = "landing_page_logo", 

          tags$a(
            href = "#", 

            tags$h3(
              tags$span(
                shiny::HTML(
                  text = "<strong>our</strong><span>series.org</span>"
                ), 

                icon(
                  name = "code", 
                  class = "fa-solid fa-code"
                )
              )
            )
          )
        ), 

        tags$div(
          class = "landing_page_welcome", 

          tags$h3(
            "Welcome!"
          )
        )
      ), 

      tags$div(
        class = "landing_page_body", 

        tags$div(
          class = "landing_page_content", 

          tags$div(
            class = "text_content", 

            tags$div(
              class = "some_text", 

              tags$h1(
                tags$strong(
                  "TIED UP", 

                  style = "font-size: 200%"
                )
              )
            ), 

            tags$div(
              class = "more_text", 

              tags$h3(
                "Your Dream R Shiny Landing Page"
              )
            )
          ), 

          tags$div(
            class = "select_something", 

            selectInput(
              inputId = "r_version", 
              label = "", 
              choices = c(
                "Which Was Your First R Version?" = "", choices
              ), 
              selected = NULL, 
              width = "100%"
            )
          )
        )
      )
    )
  ), 

  tabPanelBody(
    value = "dashboard", 

    # ----dashboardpage----
    tags$div(
      id = "div_dashboard", 

      dashboardPage(
        header = header, 
        sidebar = sidebar, 
        body = body, 
        title = "Landing Page"
      )
    )
  )
)

Why did you use lots and lots of div() elements? You ask.

I'm learning HTML, CSS & JS and I'm always heavy on the practice, practice, practice.

You can use whichever shiny-pure styling you wish. But in the meantime, allow me to style my div()s:

Step 3: styles.css

I always use a project oriented workflow and usually put this file inside the www/css/ folder.

/* fancy font: */

@import url('https://fonts.googleapis.com/css2?family=Quicksand&display=swap');
h1,
h2,
h3,
h4,
h5,
h6,
body {
    font-family: 'Quicksand', sans-serif;
}

.landing_page_container {
    background-image: url(/images/bg.jpg);
    background-repeat: no-repeat;
    background-position: center;
    background-size: cover;
    height: 100vh;
}

.landing_page_header {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    grid-template-rows: 1fr;
    column-gap: 25%;
    align-content: center;
    justify-content: space-evenly;
    align-items: center;
    justify-items: center;
    background-color: white;
    max-height: 50px;
    padding: 10px;
}

.landing_page_content {
    margin-top: 5%;
    color: white;
    padding: 10px;
    display: grid;
    grid-template-columns: 1fr;
    grid-template-rows: repeat(2, 1fr);
    row-gap: 15px;
    align-content: center;
    justify-content: center;
    align-items: center;
    justify-items: center;
}

.select_something {
    width: 50%;
    font-size: 150%;
}

.text_content {
    text-align: center;
}

Step 4: server.R

Have you ever seen how conditionalPanel() elements just pop out of nowhere and slap you on the face? That is one bad habit shiny has mastered, you'd think it has gone through the whole of this award winning masterpiece:

image.png

I don't want that happening to any of my applications so I did a little (re)search and {shinyjqui} came to my aid:

server <- function(input, output, session) {
  # hide dashboard by default:
  jqui_hide(
    ui = "#div_dashboard", 
    effect = "blind"
  )

  observe({
    # if user has made a selection:
    if (isTruthy(input$r_version)) {
      # first switch to tab `dashboard`:
      updateTabsetPanel(
        session = session, 
        inputId = "panels", 
        selected = "dashboard"
      )

      # then show it's contents:
      jqui_show(
        ui = "#div_dashboard", 
        effect = "blind", 
        duration = 500
      )
    }
  })
}

Finally

Run App.

image.png

I'm quite proud of the outcome:

s2.png

Hhmmmhh... What else should I add?๐Ÿค”

Let's just leave it there for now.

  • All the code can be found in this github repo.

  • You can run the app directly from R/RStudio using:

shiny::runGitHub(repo = "kennedymwavu/landing-page")

Till next time, have a good one!

ย 
Share this