GitHub
LinkedIn
Twitter
YouTube
RSS

Top 5 Shiny UI Add-On Packages

Author: Mandy Norrbo

Published: October 27, 2022

There are a growing number of Shiny users across the world, and with many users comes an increasing number of open-source “add-on” packages that extend the functionality of Shiny, both in terms of the front end and the back end of an app.

This blog will highlight 5 UI add-on packages that can massively improve your user experience and also just add a bit of flair to your app. Each package will have an associated example app (some more inspired than others) that I’ve created where you can actually see the UI component in action. All code for example apps can be found on our GitHub.


Do you require help building a Shiny app? Would you like someone to take over the maintenance burden? If so, check out our Shiny and Dash services.


{shinycssloaders}

If you have graphs or other outputs in your app that are slow to render and re-render, it can be frustrating for the user if it looks like nothing is changing after they’ve changed an input or pressed a button to re-render. Adding a spinner to indicate the plot is re-rendering makes it clear to the user what is going on and can make that waiting time a bit more bearable!

While the {shinycssloaders} package does not speed up your plot rendering (although if you do need help with speeding up a Shiny app, we can help), it does let you very easily add loading spinners to any specific output in your Shiny app.

All you need to do is wrap your Shiny output in the withSpinner() function! You can also modify the spinner with the type, color, and size arguments.

# in the UI
withSpinner(plotOutput("my_plot"))

In my example app:

A gif of a plot being re-generated with a shinycssloader showingduring rendering.

{waiter}

{shinycssloaders} is very useful if you have one or more slow outputs that need to be rendered or re-rendered, but what if just loading up your app at start up is slow? For example, if you’re pulling new data from an API or a database every time you load the app. This is where the {waiter} package comes in handy!

{waiter} allows you to create a loading screen that covers the entire app until some specific bit of code has finished running. You just need to tell it when to show (waiter_show()) and when to hide (waiter_hide()) the waiter loading screen. Note that you’ll need to activate the waiter functionality by including the useWaiter() command in your UI.

The basic syntax is:

# in the UI
useWaiter()

# in the server
waiter_show()

  # code that takes a while to run

waiter_hide()

In my example app:

A gif of a an app being refreshed with a loading screen created by thewaiter package.

{rclipboard} & {tippy}

If your app needs to generate any kind of text that would then need to be used elsewhere, e.g. a URL or a uniquely identifying string, then a “Copy to clipboard” button can easily be added with the {rclipboard} package. The main function from {rclipboard} is rclipButton() which creates a clickable button that lets you copy whatever is passed to the clipText argument. There are no associated render*() or *Output() functions, so you can create your clipboard button and then render it with renderUI() and display it with uiOutput(). You will also need to activate the copy to clipboard functionality by including the rclipboardSetup() command in your UI.

A basic button could be created like this:

# in the UI
rclipboardSetup(),
uiOutput("clip_button")

# in the server
renderUI({
  rclipButton(
    "clip_button",
    "Copy to clipboard",
    clipText = "Text to be copied",
    icon = icon("clipboard")
  )
})

However, when you press this button there is no feedback to the user that anything has actually been copied. This is where the {tippy} package comes in. {tippy} allows you to add tooltips to UI elements in your app that appear at a certain trigger such as hover or click. Tooltips are an effective way to provide help or feedback to the user and improve their experience using the app.

All you need to do is call the tippy_this() function on the input ID of your UI component. If we have a clipboard button with ID clip_button as above, we could add a tooltip that says “String copied!” whenever the button is clicked.

# in the UI
tippy_this(
  "clip_button",
  tooltip = "String copied!",
  trigger = "click"
)

In my example app:

A gif of a random string being copied to clipboard with tooltip pop-upand then being pasted.

{shinyglide}

{shinyglide} lets you create a carousel-like window that allows the user to move between different screens in a chronological order using “Next” and “Back” buttons. It’s a nice way to add a pop-up to guide your user through some action, either for collecting inputs for your app, or demoing how to use the app, or just as an embedded presentation.

There is a lot of flexibility when using {shinyglide}, but a basic series of screens could be constructed like this:

# in the UI
glide(
  screen(
    # content on the first screen
  ),
  screen(
    # content on second screen
  )
)

A nice feature is that you can easily add a condition that needs to be met until the user can move on to the next page, e.g., the user needs to provide some input. This is done with the next_condition argument in the screen() function.

In my example app:

A gif of three shinyglide screens being moved between using Next andBack buttons.

{sortable}

Last but not least is {sortable}, which is a package that enables drag-and-drop behaviour in your Shiny apps. This means that the user can perform actions such as rearranging plots in a grid, or include/exclude elements by dragging them in and out of a specific panel.

The basic syntax of {sortable} is:

sortable_js("element_id")

where "element_id" is the ID of the div containing the elements you wish to be drag-and-drop-able.

In my example app:

A gif of four plots in a grid where two are swapped by using drag anddrop with the cursor.

Further resources

If you want to explore even more packages that extend the Shiny framework, I would highly recommend checking out this curated list of Shiny extension packages:


Jumping Rivers Logo