An Easy Way to Customize Your Shiny App Theme

[This article was first published on Data Enthusiast's Blog, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.


There are multiple ways to style or theme a Shiny app. A high-level overview is available in the Mastering Shiny book by Hadley Wickham. Here I’ll show the easiest way to do this. If you need to build an entirely – or mostly – new Shiny theme (e.g. a corporate theme), this post is probably not for you. In that case bslib may be the best starting point. Otherwise, if you are generally happy with a pre-made theme and just want to tweak some of its elements, read on.

This post is based on an actual app, so that you can see how it all works. Here’s the app’s source.

Base Theme

The first step in styling your app is to choose a base theme. Some good pre-made options are available from the shinythemes package and can be added using shinythemes::shinytheme(). Bootswatch themes can be selected with bslib::bs_theme().

  title = "Calgary Crime Data Explorer",
  theme = shinythemes::shinytheme("readable"),

Note how the base theme is passed to the theme argument of the shiny::...Page() function, in this case navbarPage(). Other functions that create page layouts (fluidPage(), fillPage(), etc.) also allow to assign a theme.

Changes to the Base Theme

If there is anything about the base theme you’d like to change, do it by changing individual CSS elements, like this:

ui <- fluidPage(
  ## Custom style
    # CSS for navbar elements
      HTML('.navbar {font-size: 17px;}',
           '.navbar-default .navbar-brand {color: #262626; font-weight: bold}',
           '.navbar-default .navbar-brand:hover {color: #262626;}',
           '.navbar-default .navbar-nav > li > a {color: #262626;}')
    # CSS for fonts
    tags$style('h3 {font-weight: normal;}',
               'h4 {font-weight: normal;}',
               '* {font-family: Ubuntu;}'),
    # CSS for all buttons (class .btn)
    tags$style('.btn {color: #333333; 
                      background-color: #eeeeee; 
                      border-color: #cccccc;}
                .btn:hover {background-color: #e1e1e1;}'), 
    # CSS for errors and validation messages
    tags$style('.shiny-output-error-validation {
                 color: #e35300;
                 font-weight: bold;}'),
    # CSS for individual elements: note the '#id' syntax
    tags$style('#map-readme {color: #333333;
                             background-color: #ff770050;
                             border-color: #c84407;}
                #map-readme:hover {background-color: #e36a0075;}
                #trend-readme {color: #333333;
                               background-color: #ff770050;
                               border-color: #c84407;}
                #trend-readme:hover {background-color: #e36a0075;}')
  ), # end of style block
  # Note how both styling *and* navbarPage are within fluidPage.
  # Styling won't work if placed directly inside navbarPage due to
  # "Error in: Navigation containers expect a collection of ... (stuff that is not tags)"
  ## UI definition begins
    title = "Calgary Crime Data Explorer",
    theme = shinythemes::shinytheme("readable"),
  ) # UI definition ends
) # fluidPage ends

Note that the navbarPage() call contains UI definition and should not have styling inside it (except theme = shinythemes::shinytheme("readable")). Placing tags$style() inside navbarPage() will result in "Error in: Navigation containers expect a collection of ... (stuff that is not tags)" message.

How do I know which CSS selector to refer to? Right-click on the element you’d like to change and choose “Inspect”, which will open up the page’s HTML:

The element you’ve clicked on should be highlighted:

To make changes to all elements of a class, tag this class. For example, to change the appearance of all buttons, tag class .btn: tags$style('.btn {color: #333333; ...other CSS changes...}').

If you need to change just one specific element (here, one button), use the id selector instead of a class selector: tags$style('#map-readme {color: #333333; ...other CSS changes...}'). Note that referencing a class selector starts with a dot . and referencing an id starts with a hash sigh #.

If you need to change the appearance of an element in a specific state, tag the id selector followed by a pseudo-class: #map-readme:hover, where :hover is a pseudo-class.

Finally, although all CSS tags can be put inside a single tags$style() call, I broke them into separate blocks for readers’ (and my own) convenience. The comments in the code explain what each block does.

To leave a comment for the author, please follow the link and comment on their blog: Data Enthusiast's Blog. offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)