Please ignore the title of this post. It’s an exaggeration. In this post, I want to show an example that has the feel of a Shiny app, but it doesn’t really use Shiny or R as the backend when it runs. This example is from an answer of mine on a forum, and mainly intended for beginners who are interested in learning a little bit JavaScript, although I’m not a JavaScript expert. Below is the full R Markdown document:
---
title: "Change the number of bins of a histogram"
---
```{r, setup, echo=FALSE}
n_breaks = c(5, 10, 15, 20)
shiny::selectInput(
'n_breaks',
label = 'Number of bins',
choices = n_breaks,
selected = 5)
```
Draw all histograms and put them in a container with
the class `hist-all`.
```{r, class.chunk='hist-all'}
x = faithful$eruptions
for (i in n_breaks) {
hist(x, breaks = seq(min(x), max(x), length.out = i + 1),
main = paste('n = ', i))
}
```
But we hide all of those images initially with CSS.
```{css}
/* hide all images */
.hist-all img { display: none; }
/* but show the image with the class 'show' */
.hist-all .show { display: block; }
```
When the choice in the select input is changed, show
the histogram with the selected number of bins.
```{js}
(d => {
const s = d.getElementById('n_breaks'), // the select input
imgs = d.querySelectorAll('.hist-all img'); // all histograms
// add the class 'show' to the i-th image, where i is the
// selected number in the select input
function showImg() {
const i = s.selectedIndex;
imgs.forEach((el, k) => {
// k is the index of the image; i is the index of the choice
// in the select input; add the class 'show' if the two
// indices are equal
el.classList.toggle('show', k == i);
});
}
// show the initial image
showImg();
// show a different image when select input is changed
s.onchange = showImg;
})(document);
```
You can knit the document to HTML to play with the select input, or just visit
my demo. Personally I liked this example
because it’s a combination of my favorite technologies: R, CSS, and JavaScript.
R for drawing plots, CSS for appearance, and JavaScript for interaction. I have
tried to comment the code in detail, and I hope it’s not too hard to understand.
The key piece of JavaScript is el.classList.toggle().
This example could be useful when you want to present several plots one by one on a static HTML page that has no R or Shiny running behind it. But guess what? Someday you may be able to run real Shiny apps in your web browser without R or Shiny—WASM for the win!
Donate
As a freelancer (currently working as a contractor) and a dad of three kids, I truly appreciate your donation to support my writing and open-source software development! Your contribution helps me cope with financial uncertainty better, so I can spend more time on producing high-quality content and software. You can make a donation through methods below.
-
Venmo:
@yihui_xie, or Zelle:xie@yihui.name -
Paypal
-
If you have a Paypal account, you can follow the link https://paypal.me/YihuiXie or find me on Paypal via my email
xie@yihui.name. Please choose the payment type as “Family and Friends” (instead of “Goods and Services”) to avoid extra fees. -
If you don’t have Paypal, you may donate through this link via your debit or credit card. Paypal will charge a fee on my side.
-
-
Other ways:
WeChat Pay (微信支付:谢益辉) Alipay (支付宝:谢益辉) 

When sending money, please be sure to add a note “gift” or “donation” if possible, so it won’t be treated as my taxable income but a genuine gift. Needless to say, donation is completely voluntary and I appreciate any amount you can give.
Please feel free to email me if you prefer a different way to give. Thank you very much!
I’ll give back a significant portion of the donations to the open-source community and charities. For the record, I received about $30,000 in total (before tax) in 2024-25, and gave back about $15,000 (after tax).