class: inverse background-image: url("images/title.jpg") background-size: cover <style type="text/css"> /* custom.css */ .left-code { color: #777; width: 40%; height: 92%; float: left; } .right-plot { width: 58%; float: right; padding-left: 1%; } </style> <ul class="hextile clr"> <li> <div> <h1>03</h1> <p style = "padding-top: 30%; font-size: 1.2em">Publishing apps</p> </div> </li> <li> <img src="images/shiny.svg" alt=""/> </li> <li></li> </li> <li> <div> <p style = "padding-top: 20%; font-size: 1.33em"> Mitchell O'Hara‑Wild (Nectric) </p> </div> </li> </ul> .footnote[ Materials at [workshop.nectric.com.au/shiny-wombat24/](https://workshop.nectric.com.au/shiny-wombat24/) ] --- # Recap: A multi-file shiny app ``` r fs::dir_tree("../apps/bob_ross/01_bob_ross") ``` ``` ## ../apps/bob_ross/01_bob_ross ## ├── data ## │ └── elements-by-episode.csv ## ├── global.R ## ├── server.R ## ├── ui.R ## └── www ## └── joy_of_painting.jpg ``` -- Most shiny apps are organised into several files. * `ui.R`: The specification of the user interface * `server.R`: The R code to define behaviour * `global.R`: Objects creation accessible to both `ui.R` and `server.R` * `data/`: Folder for your data * `www/`: Folder for your web data (images, css, javascript, etc.) --- # Recap: A multi-file shiny app ``` r fs::dir_tree("../apps/bob_ross/01_bob_ross") ``` ``` ## ../apps/bob_ross/01_bob_ross ## ├── data ## │ └── elements-by-episode.csv ## ├── global.R ## ├── server.R ## ├── ui.R ## └── www ## └── joy_of_painting.jpg ``` Most shiny apps are organised into several files. * `ui.R`: The specification of the user interface * `server.R`: The R code to define behaviour * `global.R`: Objects creation accessible to both `ui.R` and `server.R` * `data/`: Folder for your data * **`www/`: Folder for your web data (images, css, javascript, etc.)** --- class: center .box-12.bg-blond[ # 🚧 This app has room for improvement! 🚧 ] # How could we make it better? Remove code duplication ✅ Add comments to server code ✅ Make it more useful ✅ Improve layout of outputs ✅ **Make it look better** --- # 🎨 Change colour swatches with `shinythemes` This package provides colour schemes from http://bootswatch.com/. -- The fastest and easiest way to change the appearance of your app. To use it, specify the `theme` argument in your **UI's page function**. ``` r fluidPage( theme = shinythemes::shinytheme("superhero"), # The rest of your UI code goes here. ) ``` You can also provide your own css file to this `theme` argument for a custom style - but remember, it must be in the `www/` folder! --- class: feature # 🌠 Your turn! .box-12[ ## Pick a style for your app 🎨 Use `shinythemes` to change the theme for your gallery. 1. Go to http://bootswatch.com/ and choose a theme you like. 1. Open `bob_ross/04_bob_ross` and use `shinythemes` to set the app's theme by name. Hint: Code for setting the theme to "superhero" is shown on the previous page. <br> ]
−
+
02
:
00
--- # 🪟 Transparent plot backgrounds The plot looks out of place, with some plot styling it can look better. ``` r output$plot_proportion <- renderPlot({ elements_prop() %>% ggplot(aes(x = proportion, y = element)) + * geom_col(fill = "white") + scale_x_continuous(labels = scales::percent, limits = c(0,1)) + * theme( * panel.background = element_rect(fill='transparent'), * plot.background = element_rect(fill='transparent', color=NA), * legend.background = element_rect(fill='transparent'), * legend.box.background = element_rect(fill='transparent'), * text = element_text(colour = "white"), * axis.text = element_text(colour = "white") * ) *}, bg = "transparent") ``` Try it out by running the `bob_ross/06_bob_ross` app. --- # ✨ Alternatively, use a JS plotting library Replacing ggplot2 with ECharts v5 via the echarts4r package ``` r # global.R *library(echarts4r) # ui.R tabPanel("Plot", "Frequency of elements in paintings", * echarts4rOutput("plot_proportion", height = "800px") ) # server.R *output$plot_proportion <- renderEcharts4r({ e_charts(data = elements_prop(), x = element) %>% e_bar(serie = proportion, legend = FALSE) %>% e_flip_coords() %>% e_y_axis(inverse = TRUE) }) ``` Try it out by running the `bob_ross/07_bob_ross` app. --- background-image: url("images/awesome-shiny-extensions.jpg") background-size: cover <br> .box-12.bg-blond.bottom-margin.center[ Curated list of extensions packages by Nan Xiao (肖楠) https://github.com/nanxstats/awesome-shiny-extensions ] --- class: center .box-12.bg-blond[ # 🎉 This app is ready to publish! 🎉 ] Remove code duplication ✅ Add comments to server code ✅ Make it more useful ✅ Improve layout of outputs ✅ Make it look better ✅ --- # 🤝 Some publishing options 1. Share the source code fow users to run locally (GitHub) 2. Host on a server with shinyapps.io (SaaS) 3. Host on your own server (docker / shinyproxy) 4. Host on a static website and run shiny in the browser (shinylive) --- # 🐙 Sharing apps with GitHub (or just a ZIP!) This is the easiest method for sharing your app, but it could be a bit tricky for end-users to run. * You simply provide the app's code * They need to know how to run R code * They need to install the app's package dependencies (`renv` helps) * Requires server code and data to be shared in full This is how I've shared the demo apps with you today! You can run an app directly from GitHub with one line of code: ``` r shiny::runGitHub("workshop-shiny-wombat24", "mitchelloharawild", subdir = "apps/bob_ross/07_bob_ross") ``` --- # 🌥 Hosting with shinyapps.io Pros: 1. Very easy to deploy and update (usually just one button) 2. Support comes included from Posit 3. Lots of extra features included, like authentication and metrics Cons: 1. SaaS is a service (cost, off-site hosting) 2. Performance tends to be limited, especially for the free tier. --- class: feature # 🌠 Our turn! <br> .box-12[ ## Deploy an app to shinyapps.io ☁️ Select your favourite app from today and publish it to [shinyapps.io](https://www.shinyapps.io/) 1. When viewing your selected app, click the publish button. 1. Set up your <shinyapps.io> access token in R. 1. Select all relevant app files and click 'Publish' ] --- # 🐳 Hosting with docker Pros: 1. Data and app is kept on-site 2. Can be better value for performance 3. Often cheaper, especially if you already run a server Cons: 1. Need to manage your own deployment 2. Can be tricky to install the right dependencies (renv helps) 3. Not as many helpful tools built-in as shinyapps.io (authentication, management, etc.) Best approach is to use one docker container per app. --- # 🐳 Hosting with docker: Dockerfile 1. Create the Dockerfile (update dependencies) ```bash FROM rocker/shiny:4.3.1 # Install system dependencies if needed # RUN apt-get update && apt-get install -y [DEPENDENCES...] # Set the working directory in the container WORKDIR /srv/shiny-server # Copy the app files and renv.lock to the container COPY . . # Install renv RUN R -e "install.packages('renv')" # Restore R packages from renv.lock RUN R -e "renv::restore()" # Change permissions for the Shiny app folder (shiny needs proper permissions) RUN chown -R shiny:shiny /srv/shiny-server # Expose the port for Shiny EXPOSE 3838 # Run the Shiny app as a service #CMD ["R", "-e", "shiny::runApp('/srv/shiny-server', port = 3838)"] CMD ["/usr/bin/shiny-server"] ``` --- # 🐳 Hosting with docker: Dockerfile 2\. Build the docker image ```bash docker build -t "image-name" . ``` 3\. Create and run the docker container ```bash docker run --rm -ti -p 3838:3838 image-name ``` --- # 🐳 Hosting with docker: docker-compose ```yaml shiny: image: rocker/shiny container_name: shiny ports: - "3838:3838" networks: - proxied volumes: - ${BASE_DIR}/shiny/apps:/srv/shiny-server/ - ${BASE_DIR}/shiny/logs:/var/log/shiny-server/ restart: unless-stopped ``` Install the apps you need by starting an R session within the container using `docker exec -it shiny R`. This shares packages across multiple apps - while it's good for saving space it's terrible for breaking apps! --- # 🐳 Exposing your docker container to the web Port forwarding and reverse proxies - I use `jc21/nginx-proxy-manager`. ```yaml nginx-manager: image: 'jc21/nginx-proxy-manager:latest' restart: always ports: - '80:80' - '443:443' - '81:81' environment: # These are the settings to access your db DB_MYSQL_HOST: "nginx-db" DB_MYSQL_PORT: 3306 DB_MYSQL_USER: "nginx-admin" DB_MYSQL_PASSWORD: "changeme" DB_MYSQL_NAME: "nginx" volumes: - ${BASE_DIR}/nginx/data:/data - ${BASE_DIR}/nginx/letsencrypt:/etc/letsencrypt depends_on: - nginx-db ``` --- # 🐳 Exposing your docker container to the web ```yaml nginx-db: image: 'jc21/mariadb-aria:latest' restart: always environment: MYSQL_ROOT_PASSWORD: 'changeme' MYSQL_DATABASE: 'nginx' MYSQL_USER: 'nginx-admin' MYSQL_PASSWORD: 'changeme' volumes: - ./data/mysql:/var/lib/mysql ``` -- Tech infrastructure teams are usually familiar with docker, and might be more receptive to running a docker container rather than maintaining a shiny server for your app. --- # ✨ Or try shinylive on a static website Pros: 1. Can use free static web hosts like Github Pages 2. Serverless and safer - all code runs in the user's browser Cons: 1. Data and app is shared to users (nothing is secret!) 2. Bleeding edge and things could break 3. Can take a while to start, as R, shiny, and the packages need downloading 3. Lacks the helpful tools built-in with shinyapps.io (authentication, management, etc.) More information: https://github.com/posit-dev/shinylive --- class: feature # 🕛 That's all for today! <br> .box-12[ ## Where to go next? Lots more information (especially on non-standard evaluation!) in the Mastering Shiny online book: https://mastering-shiny.org/ Consider integrating R code in existing web apps (non-shiny) using APIs via the plumber package. Need help making your app? Check out [Shiny Assistant](https://shiny.posit.co/blog/posts/shiny-assistant/). ]