Build custom input objects
Design the Component
Shiny input components should try to adhere to the following principles, if possible:
- Designed to be used from HTML and R: Shiny user interfaces can either be written using R code (that generates HTML), or by writing the HTML directly. A well-designed Shiny input component will take both styles into account: offer an R function for creating the component, but also have thoughtfully designed and documented HTML markup.
<head>. For R-based interface code, you can use the functions
tags$head together to ensure these tags appear once and only once, in the head. (See the full example below.)
Write an Input Binding
Each custom input component also needs an input binding, an object you create that tells Shiny how to identify instances of your component and how to interact with them. (Note that each instance of the input component doesn’t need its own input binding object; rather, all instances of a particular type of input component share a single input binding object.)
An input binding object needs to have the following methods:
Given an HTML document or element (
scope), find any descendant elements that are an instance of your component and return them as an array (or array-like object). The other input binding methods all take an
elargument; that value will always be an element that was returned from
A very common implementation is to use jQuery’s
findmethod to identify elements with a specific class, for example:
Return the Shiny input ID for the element
nullif the element doesn’t have an ID and should therefore be ignored. The default implementation in
data-input-idattribute and falls back to the element’s
idif not present.
Return the Shiny value for the element
el. This can be any JSON-compatible value.
Set the element to the specified value. (This is not currently used, but in the future we anticipate adding features that will require the server to push input values to the client.)
Subscribe to DOM events on the element
elthat indicate the value has changed. When the DOM events fire, call
callback(a function) which will tell Shiny to retrieve the value.
We recommend using jQuery’s event namespacing feature when subscribing, as unsubscribing becomes very easy (see
unsubscribe, below). In this example,
exampleComponentNameis used as a namespace:
Later on, we can unsubscribe
".exampleComponentName"which will remove all of our handlers without touching anyone else’s.
callbackfunction optionally takes an argument: a boolean value that indicates whether the component’s rate policy should apply (
truemeans the rate policy should apply). See
getRatePolicybelow for more details.
Unsubscribe DOM event listeners that were bound in
Return an object that describes the rate policy of this component (or
Rate policies are helpful for slowing down the rate at which input events get sent to the server. For example, as the user drags a slider from value A to value B, dozens of change events may occur. It would be wasteful to send all of those events to the server, where each event would potentially cause expensive computations to occur.
A rate policy slows down the rate of events using one of two algorithms (so far). Throttling means no more than one event will be sent per X milliseconds. Debouncing means all of the events will be ignored until no events have been received for X milliseconds, at which time the most recent event will be sent. This blog post goes into more detail about the difference between throttle and debounce.
A rate policy object has two members:
policy- Valid values are the strings
"direct"means that all events are sent immediately.
delay- Number indicating the number of milliseconds that should be used when debouncing or throttling. Has no effect if the policy is
Rate policies are only applied when the
subscribeis called with
trueas the first parameter. It’s important that input components be able to control which events are rate-limited and which are not, as different events may have different expectations to the user. For example, for a textbox, it would make sense to rate-limit events while the user is typing, but if the user hits Enter or focus leaves the textbox, then the input should always be sent immediately.
Register Input Binding
Once you’ve created an input binding object, you need to tell Shiny to use it:
The second argument is a name the user can use to change the priority of the binding. On the off chance that the user has multiple bindings that all want to claim the same HTML element as their own, this call can be used to control the priority of the bindings:
Higher numbers indicate a higher priority; the default priority is 0. All of Shiny’s built-in input component bindings default to a priority of 0.
If two bindings have the same priority value, then the more recently registered binding has the higher priority.
For this example, we’ll create a button that displays a number, whose value increases by one each time the button is clicked. Here’s what the end result will look like (click it!):
To start, let’s design the HTML markup for this component:
The CSS class
increment is what will differentiate our buttons from any other kind of buttons. (The
btn btn-default classes are there to make the button look decent in Bootstrap.)
This code uses jQuery’s delegated events feature to bind all increment buttons at once.
Now we’ll create the Shiny binding object for our component, and register it:
./www/js/increment.js, which we can then access as
If you’re using an
index.html style user interface, you’ll just need to add this line to your
<head> (make sure it comes after the script tag that loads
On the other hand, if you’re using
ui.R, then you can define this function before the call to
Then in your
shinyUI page definition you can call
incrementButton wherever you want an increment button rendered. Notice the line that begins with
singleton will ensure that the
increment.js file will be included just one time, in the
<head>, no matter how many buttons you insert into the page or where you place them.
If you have questions about this article or would like to discuss ideas presented here, please post on RStudio Community. Our developers monitor these forums and answer questions periodically. See help for more help with all things Shiny.