Introduction to inshiny

inshiny provides a basic set of compact Shiny input widgets that are designed to be displayed within surrounding text without breaking the spacing between lines. You can use inshiny to integrate Shiny input within text paragraphs, or just to get user input for your Shiny app without taking up as much space on the page compared to standard Shiny inputs.

By default, Shiny uses Bootstrap 3 for layouts and styling. inshiny requires Bootstrap 5, which just means that you will need to use the bslib package (which enables updating to Bootstrap 5) in addition to shiny for inshiny to work.

library(shiny)
library(bslib)
library(inshiny)

To use Bootstrap 5, when building your UI, you should use one of bslib’s page layout functions instead of Shiny’s built-in page layouts. In other words, use

You should also set a theme using bslib::bs_theme(); this allows you to specify version = 5 so that your app will continue to use Bootstrap 5 even after Bootstrap 6 is released and bslib starts to support it. (We may add support for Bootstrap 6 to inshiny in the future, but better to make sure your app stays future-proof either way!)

Using inshiny

All inshiny widgets should be wrapped in a call to inline(), which creates a “paragraph” or “line” of text in which you can mix in widgets and arbitrary HTML elements. You can then use widgets such as inline_text() or inline_slider() within this element. Here is a simple demo of a Shiny UI that uses most of the widgets provided by inshiny:

ui <- page_fixed(
    theme = bs_theme(version = 5, preset = "quartz"),

    h1("Temperature plot"),

    plotOutput("plot", width = 480, height = 320),

    br(),

    inline("Start on ", inline_date("start_date", "2025-01-01"),
        " and plot for ", inline_number("num_days", 365), " days."),

    inline("Average temperature: ", inline_slider("avg_temp", 20, 0, 40),
        " °C. Range: ±", inline_select("temp_range", c(5, 10, 15), 10), " °C."),

    inline("Hemisphere: ", inline_switch("southern", FALSE,
        on = "Southern", off = "Northern")),

    inline(inline_button("colour", "Change colour"), " ",
        inline_link("reset", "Reset"))
)

The page starts with a title (h1) and a shiny::plotOutput() that we’ll use to plot average temperatures according to the settings below. There’s a line break (br) to space things out, then four inline lines containing the inline_* widgets provided by inshiny. It looks something like this:

Compare this to a similar layout with basic Shiny inputs:

ui <- page_fixed(
    theme = bs_theme(version = 5, preset = "quartz"),

    h1("Temperature plot"),

    plotOutput("plot", width = 480, height = 320),

    br(),

    dateInput("start_date", "Start date", "2025-01-01"),
    numericInput("num_days", "Number of days", 365),
    sliderInput("avg_temp", "Average temperature (°C)", 0, 40, 20),
    selectInput("temp_range", "Temperature range (± °C)", c(5, 10, 15), 10),
    checkboxInput("southern", "Southern hemisphere", FALSE),
    actionButton("colour", "Change colour"),
    actionLink("reset", "Reset"),
)

The controls for this basic Shiny version take up around 640 pixels of vertical space:

By contrast, the inshiny version takes up around 210 pixels of vertical space:

Of course, you could save some vertical space by laying out the elements in a grid, but they are still rather large compared to the inshiny widgets.

We complete the app using server code to handle the inputs:

server <- function(input, output, session)
{
    output$plot <- renderPlot({
        date <- input$start_date + seq_len(input$num_days) - 1;
        xpts <- as.POSIXlt(date)$yday; # day number, 0-365
        temperature <- cos(2 * pi * xpts / 364) *
            ifelse(input$southern, 1, -1) * as.numeric(input$temp_range) +
            input$avg_temp;
        oldpar <- par(mar = c(5, 5, 1, 2))
        plot(date, temperature, type = "l", ylim = c(-15, 55),
            col = input$colour %% 16 + 1, lwd = 3)
        abline(h = 0, col = 8, lty = 2)
        par(oldpar)
    })

    observeEvent(input$reset, {
        update_inline("start_date", value = "2025-01-01")
        update_inline("num_days", value = 365)
        update_inline("avg_temp", value = 20)
        update_inline("temp_range", value = "10")
        update_inline("southern", value = FALSE)
    })
}

This illustrates the use of update_inline() to reset the widgets to their original values. You would then launch the app with shinyApp(ui, server).

A note on themes

Most of the bslib “bootswatch” theme presets work well with inshiny. There are just a few that have some issues: "brite", "cyborg", "lumen", and "sketchy" have some minor display issues with some widgets, while "materia" has some odd spacing problems with buttons. The other 22 bslib presets and the default "shiny" preset work well.