Bslib allows R developers to create modern and responsive web applications by combining the flexibility of Bootstrap with the simplicity of R. Its built-in elements like cards, value boxes, and tooltips integrate seamlessly with existing Shiny applications.
This post will provide insight into how bslib works and how to build more complex layout using it. You’ll learn to customize the look and feel of your apps using pre-made themes and custom styling, and explore advanced techniques for building sophisticated applications by combining R with JavaScript functionality. For this guide, I’ve built multiple example applications, which you’ll be able to run directly from the page. I also highly recommend opening the examples in the Editor to play with the code and understand how it works.
Core Concepts
To fully appreciate bslib’s capabilities, it’s essential to understand the architecture and key technologies that it builds upon:
1. Browser/Server Architecture:
- Server-side (R environment):
- Shiny runs on the server, handling data processing and application logic.
- R code executes here, performing computations and preparing data for display.
- Client-side (Browser):
- The user interface is rendered and interacted with in the browser.
- Responsible for displaying the UI and capturing user inputs.
2. Frontend Technology Stack:
- HTML/CSS/JavaScript:
- The core technologies interpreted by the browser.
- HTML structures the content, CSS styles it, and JavaScript adds interactivity.
- Bootstrap:
- A popular frontend framework for building responsive and mobile-first websites.
- Provides pre-designed components and a grid system for layout.
- Bootswatch:
- Offers a variety of pre-made Bootstrap themes.
- Allows quick customization of the overall look and feel.
- bslib:
- An R package that bridges Shiny with Bootstrap.
- Enables easy theme customization and component creation directly from R.
3. How bslib Enhances Shiny Development:
- Simplified Theming: Allows developers to customize the appearance of Shiny apps without writing CSS.
- Responsive Design: Leverages Bootstrap’s grid system for mobile-friendly layouts.
- Component Library: Provides a rich set of UI components that are consistent with modern web design practices.
- Real-time Updates: Enables dynamic theme changes without page reloads.
UI Components
Bslib provides a rich set of UI components that enable you to create polished, professional-looking web applications with minimal effort. Let’s explore the fundamental building blocks you’ll use to construct your Shiny apps.
Main Layout
The foundation of any Shiny application built with bslib starts with defining its main layout structure. The most common approach is using page_sidebar()
, which creates a dashboard-style layout consisting of three main parts:
- A header area for titles and navigation
- A collapsible sidebar for controls and filters
- A main content area for displaying your app’s primary content
Let’s start with a minimal example to understand these core components.
library(shiny)
library(bslib)
ui <- page_sidebar(
# Header area
title = "My Dashboard", # Appears at the top of the page
window_title = "Getting Started", # Shows in the browser tab
# Sidebar area
sidebar = sidebar(
title = "Settings",
"This sidebar can contain inputs and filters",
selectInput("example", "Choose an option:", c("A", "B", "C"))
),
# Main content area
"Your main content will go here. This area can contain plots, tables, text, or any other outputs."
)
server <- function(input, output) { }
shinyApp(ui, server)
Key aspects of this layout:
Page Title:
title
: Appears in the dashboard headerwindow_title
: Shows in the browser tab- These can be different to help distinguish between multiple open dashboards
Sidebar:
- Perfect for housing controls that affect the main content
- Can be customized with a title and various styling options
Main Content Area:
- The primary space for your app’s content
- Automatically adjusts when the sidebar is toggled
- Takes up the remaining width of the page
The layout is responsive by default, meaning it will automatically adjust to different screen sizes. On smaller screens, the sidebar collapses behind a hamburger menu button.
Adding Interactive Content
Now that we have our basic layout structure, let’s populate it with some interactive content. We’ll create a data visualization dashboard using the built-in mtcars
dataset, demonstrating how the sidebar controls can interact with the main content area.
This example demonstrates several key concepts of Shiny layouts and interactivity:
Sidebar Organization:
- Groups related controls together
- Provides clear labels and helpful descriptions
- Controls immediately affect the main content
Main Content Area:
- Centered and properly spaced content
- Responsive plot that fills available space
- Clear visual hierarchy with headers and borders
Interactivity:
- Real-time updates based on user inputs
- Multiple visualization options
- Dynamic data exploration capabilities
This foundation provides a smooth user experience while maintaining a clear separation between controls and content. In the following sections, we’ll explore how to enhance this basic structure with additional bslib components to create even more sophisticated interfaces.
Cards
Cards are versatile and fundamental components in modern web design. While they might seem simple at first (they are, it’s essentially rectangular containers with borders and spacing), their true power lies in their ability to organize and present information effectively.
card(
card_header("Content Grouping"),
p("This card demonstrates how to group related information effectively."),
card_body(
h5("Sample Data"),
p("Revenue: $50,000"),
p("Expenses: $30,000"),
p("Profit: $20,000")
)
),
Cards allow you to:
- Content Grouping: Group related information, making it easier for users to digest complex data
- Visual Hierarchy: Establish a clear visual hierarchy on your page, guiding users' attention
- Flexibility: Contain various elements like text, images, buttons, and other components
- Responsive Design: Adapt well to different screen sizes, making them ideal for responsive layouts
To see these concepts in action, take a look at the interactive demo below. Experiment with different card types and layouts to better understand their versatility:
When used strategically, cards can significantly enhance user engagement and navigation through your Shiny app. They provide a clean, organized way to present information, making your app more intuitive and user-friendly.
The demo below showcases various card types and layouts. For more advanced use cases and to interact with the code, click on “Open in Editor” within the demo:
Value Boxes
Value boxes are compact, visually appealing components used to display key metrics or important information in a dashboard-like interface. In bslib, value boxes are highly customizable and can be easily created using the value_box()
function.
A value box consists of these key components:
value
: The main piece of information or metric being displayedtitle
: (Optional) The label or description of the metricshowcase
: (Optional) An icon or image to visually represent the metric, works well withbsicon
showcase_layout
: (Optional) Determines the position ("left center"
,"top right"
, or"bottom"
) of the showcase elementtheme
: (Optional) Sets the color scheme of the value box- Additional content: You can add paragraphs, icons, or other HTML elements for extra details
Let’s break down an example:
value_boxes <- list(
value_box(
title = "Title",
value = "Value",
showcase = bs_icon("award"),
p("content", bs_icon("suit-spade")),
p("Edit me in the Ploomber Editor", bs_icon("emoji-smile"))
),
value_box(
title = "Alt",
value = "Another type of layout",
showcase = bs_icon("music-note-beamed"),
showcase_layout = "top right",
theme = "secondary",
p("From bslib", bs_icon("emoji-smile"))
),
value_box(
title = "Theme",
value = "123",
showcase = bs_icon("bar-chart"),
theme = "purple",
p("The 1st detail")
)
)
ui <- fluidPage(
theme = bs_theme(version = 5),
mainPanel(
layout_column_wrap(
width = "250px",
!!!value_boxes
)
)
)
The layout_column_wrap()
function arranges these value boxes in a responsive grid layout, with each box having a width of 250px.
As you probably thought, Value Boxes are built with Cards, allowing you to create more complex visualizations, and even dynamic ones like the following example that you can interact with - hover your mouse over the card to view it in full screen:
Tooltips & Popovers
Tooltips and popovers are useful UI elements that provide additional information or context when users interact with specific elements on your page. In bslib, these components are easy to implement and customize.
Tooltips
Tooltips are small pop-up boxes that appear when users hover over an element, providing brief, helpful information.
To add a tooltip in bslib, you can use the tooltip()
function:
tooltip(
"Hover over me",
"This is a tooltip!",
placement = "top"
)
And you can customize the placement (top
, bottom
, left
, right
) along side other attributes of the tooltip.
Popovers
Popovers are similar to tooltips but can contain more content and are typically triggered by a click rather than a hover.
To create a popover in bslib, use the popover()
function:
popover(
actionButton("btn", "Click me"),
"Popover Title",
"This is the content of the popover. It can include more detailed information.",
placement = "right"
)
Here’s an example that demonstrates both tooltips and popovers in a Shiny app:
library(shiny)
library(bslib)
ui <- fluidPage(
theme = bs_theme(version = 5),
h1("Tooltips and Popovers Demo"),
tooltip(
actionButton("tooltip_btn", "Hover over me"),
"This is a tooltip!",
placement = "top"
),
br(), br(),
popover(
actionButton("popover_btn", "Click me"),
"Popover Title",
"This is the content of the popover. It can include more detailed information.",
placement = "right"
)
)
server <- function(input, output) {}
shinyApp(ui, server)
This example creates a button with a tooltip and another button with a popover. The tooltip appears when the user hovers over the first button, while the popover is triggered by clicking the second button.
Tooltips and popovers are excellent ways to provide additional information without cluttering your UI. They can be particularly useful for:
- Explaining form inputs
- Providing definitions or context for technical terms
- Offering help or hints for complex interactions
- Displaying additional details about data points in visualizations
It’s cool, but how does it work ?
HTML tags
If you are like me, you might love to know how things work under the hood. Let’s take a quick detour to see how things work.
The web is built with HTML, CSS, and JavaScript. These are the languages that browsers understand and interpret. Even though we’re coding our Shiny app in R, it’s important to remember that ultimately, our UI code gets translated into these web technologies.
One powerful feature of Shiny is that it allows us to directly specify HTML elements in our R code. This is made possible through the htmltools package. With htmltools, we can create and specify HTML elements directly within our R code, giving us precise control over our app’s structure and appearance.
For example, instead of using high-level Shiny / bslib component, we can use HTML tags directly:
ui <- page_sidebar(
# Element at the top of the page
title = tags$h1(
"My dashboard",
class = "my-custom-class",
id = "dashboard-title",
`data-custom` = "value"
),
...
)
This creates a web page with an h1
title. In a browser, you can right-click on “My dashboard” and inspect the element to see the H1
tag with the specified id
and class
. These attributes will be useful later when applying themes to our app.
As you can imagine, we are not limited to just h1; all HTML elements are available, such as tags$h2
, tags$code
, and even tags$div
. We can nest these tag elements to create more complex structures.
ui <- page_sidebar(
# Element at the top of the page
title = tags$div(
tags$code(
"Dashboard Code:",
class = "code-class"
),
tags$h1(
"My Dashboard",
class = "title-class"
),
id = "dashboard-title-container",
`data-custom` = "value"
),
...
)
Essential CSS Concepts
CSS (Cascading Style Sheets) is a styling language used to describe the presentation of HTML documents. It allows you to control layout, colors, fonts, and other visual aspects of web pages.
CSS uses selectors to target specific elements on a page. Selectors can be based on element types (e.g., p
for paragraphs), classes (e.g., .my-class
), IDs (e.g., #my-id
), or more complex combinations. For example, div.card h2
would target all <h2>
elements inside <div>
elements with the class “card”. This selector system allows for precise styling of individual elements or groups of elements, giving developers fine-grained control over the appearance of their web pages.
Flexbox
To create a dynamic layout where elements adjust automatically when the screen resizes, we can use Flexbox. Flexbox is a CSS layout model that efficiently distributes space among items in a container, even when their sizes are unknown or dynamic. It provides powerful alignment capabilities and direction-agnostic layout, making it ideal for responsive design. For an in-depth explanation of Flexbox Layout and its various properties to achieve desired behaviors, I highly recommend this article. Understanding Flexbox will greatly enhance your ability to create flexible and responsive layouts in your Shiny applications.
Flexbox for Card
By default, the bslib
Card
is rendered as a Flexbox, allowing its content to grow based on the card’s width. We can deactivate this behavior with the fillable=FALSE
argument of the card_body
function.
ui <- fluidPage(
theme = bs_theme(version = 5),
layout_column_wrap(
width = "200px",
card(
card_body(
fillable = TRUE,
"Here's some", tags$i("inline"), "text",
actionButton("btn1", "A button")
),
),
card(
card_body(
fillable = FALSE,
"Here's some", tags$i("inline"), "text",
actionButton("btn2", "A button")
)
)
)
)
This will result in:
Padding and Margin
In CSS, padding and margin are two crucial properties for controlling spacing around elements:
- Padding: The space inside an element, between its content and border.
- Margin: The space outside an element, separating it from other elements.
Both padding and margin can be applied to all sides simultaneously or individually to top, right, bottom, and left sides.
Bslib makes it easy to add padding and margin to elements using utility classes. Here’s an example demonstrating padding and margin usage in a Shiny app with bslib:
library(shiny)
library(bslib)
ui <- fluidPage(
theme = bs_theme(version = 5),
card(
card_header("Card with different padding and margin"),
card_body(
div(class = "bg-light border p-2 m-3", "p-2 m-3: Padding 2, Margin 3"),
div(class = "bg-light border p-3 my-4", "p-3 my-4: Padding 3, Margin Y-axis 4"),
div(class = "bg-light border px-4 mt-2", "px-4 mt-2: Padding X-axis 4, Margin Top 2")
)
)
)
server <- function(input, output) {}
shinyApp(ui, server)
CSS Units: REM, PX, and Others
CSS offers various units for specifying sizes. Here are some common ones:
- REM (Root EM): Relative to the root element’s font size. 1rem equals the font size of the html element (typically 16px).
- PX (Pixels): A fixed-size unit. 1px corresponds to one device pixel.
- EM: Relative to the font size of the element (2em is twice the size of the current font).
- %: Relative to the parent element’s size.
- VW/VH: Relative to the viewport’s width/height (1vw = 1% of viewport width).
REM is often preferred for responsive design as it scales with the root font size, ensuring consistent proportions across different screen sizes.
Here’s a bslib demo showcasing these different units. I highly recommend opening it in the Editor and experimenting with the values in the style
tag:
ui <- fluidPage(
theme = bs_theme(version = 5),
tags$head(
tags$style(HTML("
.rem-example { font-size: 2rem; padding: 2rem; margin: 2rem; }
.px-example { font-size: 16px; padding: 16px; margin: 16px; }
.em-example { font-size: 0.5em; padding: 0.5em; margin: 0.5em; }
.percent-example { width: 50%; padding: 5%; margin: 5%; }
.viewport-example { width: 50vw; height: 20vh; }
"))
),
h1("CSS Units Demo"),
card(
card_header("Different CSS Units"),
card_body(
div(class = "rem-example bg-light border", "This div uses REM units"),
div(class = "px-example bg-light border", "This div uses Pixel units"),
div(class = "em-example bg-light border", "This div uses EM units"),
div(class = "percent-example bg-light border", "This div uses Percentage units"),
div(class = "viewport-example bg-light border", "This div uses Viewport units")
)
)
Theming
Pre-made Themes
You might wonder, “Why discuss CSS when I’m here to code in R?” That’s a valid question! Understanding the underlying structure helps us appreciate bslib’s power and enables us to fine-tune pre-made themes for perfect visualizations.
As you’ve likely noticed, each example imports a specific theme using theme = bs_theme(version = 5)
. However, this theme can be customized, and bslib provides multiple themes by default. Let’s explore them!
When you use bs_theme()
in your Shiny app or R Markdown document, here’s what happens behind the scenes:
- Theme Creation:
bs_theme()
creates a Sass (an extension of CSS) object with your specified theme settings. - Real-time Compilation: This Sass object is compiled into CSS when your app runs, not beforehand.
- Dynamic Updates: The theme can be updated in real-time, allowing for interactive theme customization.
- Efficient Styling: It generates only the necessary CSS, keeping your app’s styling lean and efficient.
- Compatibility: This method ensures that custom Shiny components (like sliders or date pickers) automatically adapt to your theme.
These features make it easy to create and modify themes directly in your R code, without needing to write CSS manually.
Now, let’s explore how we can leverage these benefits in our theming process.
We can easily add a theme control overlay to our Shiny app by including bs_themer()
in our server function:
server <- function(input, output) {
bs_themer()
}
Run the example below to explore all the themes bslib provides and more:
- Note: This example may take some time to load. Once it finishes, the data will appear, and a pop-up with a theme customizer will be displayed. Feel free to experiment with different theme options!
Choosing and Customizing a Theme
Once you’ve decided on a theme, you can easily apply it to your Shiny app using the bs_theme()
function. This is also where you can pass custom CSS attributes to further personalize your app’s appearance.
Here’s an example of how to configure a theme:
ui <- page_sidebar(
# Theme configuration
theme = bs_theme(
version = 5,
bootswatch = "flatly", # Try different themes like "minty", "darkly", etc.
primary = "#0d6efd", # Customize primary color
"font-size-base" = "1.1rem"
),
...
)
In this example, we’re using the “flatly” Bootswatch theme, setting a custom primary color, and adjusting the base font size.
Remember, the power of bslib lies in its flexibility. You can start with a pre-made theme and then tweak it to match your exact requirements, all without leaving your R code environment.
Advanced Usecase
Now that we’ve covered the basics of bslib and explored its core components, let’s dive into some more advanced use cases and layout. These examples will demonstrate how to leverage bslib’s flexibility to create highly customized and interactive Shiny applications.
Custom Theme
One of the most powerful features of bslib is the ability to create and apply custom themes. This allows you to tailor the look and feel of your Shiny app to match your specific design requirements or brand guidelines. Let’s start by creating a custom theme with some advanced CSS rules:
library(shiny)
library(bslib)
library(sass)
# Define custom CSS, for the bslib component
custom_css <- "
.card {
transition: transform 0.2s;
}
.card:hover {
transform: translateY(-2px);
box-shadow: var(--bs-box-shadow-sm);
}
.value-box {
background: linear-gradient(45deg, var(--bs-primary), var(--bs-info));
color: white;
}
.card-header {
background-color: rgba(var(--bs-primary-rgb), 0.1);
border-bottom: 2px solid var(--bs-primary);
}
"
# Create theme with custom CSS
my_theme <- bs_theme(
version = 5,
primary = "#2c3e50",
secondary = "#95a5a6",
success = "#18bc9c",
info = "#3498db",
warning = "#f39c12",
danger = "#e74c3c"
) |> bs_add_rules(custom_css)
ui <- fluidPage(
theme = my_theme,
layout_column_wrap(
# A layout using bslib component (Card, Value Box, ...)
)
)
server <- function(input, output, session) {
# Server logic ...
# Set the theme base on dark mode
observeEvent(input$dark_mode, {
if (input$dark_mode) {
session$setCurrentTheme(bs_theme_update(my_theme, bg = "dark", fg = "white"))
} else {
session$setCurrentTheme(my_theme)
}
})
}
shinyApp(ui = ui, server = server)
The resulting app will now have our custom CSS applied to the bslib components:
Building our Own Components
Now that we have a theme and have seen how we can build our own theme for the bslib
components, let’s create a theme for a custom layout that doesn’t use any bslib components. In a file name custom.scss
, add your styling:
// Custom variables
$primary: #2c3e50;
$secondary: #95a5a6;
$success: #18bc9c;
$info: #3498db;
$warning: #f39c12;
$danger: #e74c3c;
// Custom styles
.custom-header {
color: $primary;
padding: 1rem;
margin-bottom: 2rem;
background-color: lighten($primary, 60%);
border-radius: 0.25rem;
}
.custom-card {
border: 1px solid $secondary;
padding: 1rem;
margin-bottom: 1rem;
border-radius: 0.25rem;
&:hover {
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
}
And, in our application, we can simply import this custom.scss
file, which will apply the style base on the selectors (class, element type, id):
library(shiny)
library(bslib)
library(sass)
library(colourpicker)
library(magrittr)
ui <- fluidPage(
theme = bs_theme(version = 5),
titlePanel("bslib Theme Builder Demo"),
sidebarLayout(
sidebarPanel(
"Sidebar Panel"
),
mainPanel(
div(class = "custom-header",
h2("Theme Preview"),
p("This section shows how your theme affects various Bootstrap components.")),
div(class = "custom-card",
h4("Buttons"),
actionButton("btn_primary", "Primary", class = "btn-primary"),
actionButton("btn_secondary", "Secondary", class = "btn-secondary"),
actionButton("btn_success", "Success", class = "btn-success")),
div(class = "custom-card",
h4("Alerts"),
div(class = "alert alert-primary", "This is a primary alert"),
div(class = "alert alert-secondary", "This is a secondary alert")),
div(class = "custom-card",
h4("Cards"),
div(class = "card",
div(class = "card-body",
h5(class = "card-title", "Card Title"),
p(class = "card-text", "This is a Bootstrap card component.")))),
div(class = "custom-card",
h4("Typography"),
h1("Heading 1"),
h2("Heading 2"),
p("Regular paragraph text"),
p(class = "lead", "Lead paragraph text"))
)
)
)
# Server
server <- function(input, output, session) {
observe({
# Declare a Theme
theme <- bs_theme(version = 5)
# Load our custom theme from the custom.scss file
theme <- bs_add_rules(theme, sass::sass_file("custom.scss"))
session$setCurrentTheme(theme)
})
}
# Run the app
shinyApp(ui = ui, server = server)
Combining R and JavaScript in Shiny Apps
In this final section, let’s explore how to combine JavaScript (running in the browser) with R (running on the server) in your Shiny applications. This powerful combination, along side bslib allows you to build complex, interactive components and extend bslib’s capabilities to meet your specific needs.
The main scenarios we’ll cover are:
- Running JavaScript code in the user’s browser to update page elements
- Sending events from JavaScript to our R server for server-side processing
Resulting in:
1. Setting Up JavaScript Functions
First, we define our JavaScript functions that will handle client-side interactions. For this, we store the Javascript code that we will need be pass to ShinyJs
so it can be sent to the browser when the application load.
jsCode <- '
shinyjs.updateProgressBars = function() {
const bar = document.querySelector(".progress-bar");
let width = 0;
const interval = setInterval(function() {
if (width >= 100) {
clearInterval(interval);
Shiny.setInputValue("progressComplete", true);
} else {
width += 2;
bar.style.width = width + "%";
bar.setAttribute("aria-valuenow", width);
bar.textContent = width + "%";
}
}, 100);
};
shinyjs.initializeTooltips = function() {
const tooltipTriggerList = document.querySelectorAll("[data-bs-toggle=\'tooltip\']");
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl));
};
shinyjs.sendToShiny = function(params) {
Shiny.setInputValue("jsEvent", true);
};
'
Let’s break down what each function does:
updateProgressBars
: Creates an animated progress bar that:- Updates every 100 milliseconds
- Increases by 2% each time
- Notifies R when it reaches 100%
- Updates both the visual bar and its text content
initializeTooltips
: Sets up Bootstrap tooltips by:- Finding all elements with the tooltip attribute
- Initializing them using Bootstrap’s tooltip functionality
sendToShiny
: A simple function that:- Demonstrates how to send data back to R
- Uses Shiny’s input value system to communicate
2. Building the UI
Next, we create our Shiny UI that incorporates these JavaScript functions:
ui <- page_fluid(
# Enable JavaScript functionality
useShinyjs(),
extendShinyjs(text = jsCode, functions = c("updateProgressBars", "initializeTooltips", "sendToShiny")),
# Load Bootstrap JavaScript
tags$head(
tags$script(src = "https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js")
),
# Create two-column layout
layout_column_wrap(
width = 1/2,
# Interactive features column
card(
card_header("Interactive Features"),
card_body(
actionButton("startProgress", "Start Progress", class = "btn-primary"),
br(), br(),
# Element which the Javascript code will update directly from the Browser:
div(class = "progress",
div(class = "progress-bar",
role = "progressbar",
style = "width: 0%",
`aria-valuenow` = "0",
`aria-valuemin` = "0",
`aria-valuemax` = "100",
"0%"
)
),
br(),
actionButton("jsButton", "Trigger JS Event",
class = "btn-secondary",
`data-bs-toggle` = "tooltip",
`data-bs-placement` = "top",
title = "Click to send data to R!")
)
),
# Output display column
card(
card_header("R Output"),
card_body(
verbatimTextOutput("jsOutput"),
value_box(
title = "Data from JavaScript",
value = textOutput("valueBoxContent"),
showcase = bs_icon("activity")
)
)
)
)
)
3. Communication Flow
The magic happens in how R and JavaScript communicate:
JavaScript to R Communication:
- Uses
Shiny.setInputValue()
to send data to R - Can trigger R reactive events
- Used in both the progress bar and event button
- Uses
R to JavaScript Communication:
- Calls JavaScript functions through shinyjs
- Can update UI elements directly
- Handles user interactions
This approach enables you to:
- Create smooth, client-side animations
- Handle user interactions efficiently
- Maintain two-way communication between browser and server
- Build more dynamic and responsive Shiny applications
You can see the complete working example in the interactive editor here: Example App
Last words
Now that you have a solid understanding of bslib and its integration with Shiny apps, you’re well-equipped to take your Shiny App to the next level.
Pro Tips:
- Always specify the Bootstrap version in your theme to ensure consistency:
theme = bs_theme(version = 5)
as function names and features may evolve between version. - Need to share your app with the world? We provide hassle-free hosting services. Simply upload your code, and we’ll deploy your app to the web, making it accessible to users anywhere.