Shiny Tutorial: Build a Full Shiny Dashboard with shiny.fluent

Estimated time:
time
min

If your goal is to build elegant, professional dashboards, you should be using shiny.fluent. In this Shiny tutorial, we'll cover how you can make the most of your UI and give your application a chance for success. <h2>Shiny UI tutorial using shiny.fluent</h2> With Appsilon’s <a href="https://appsilon.com/shiny-fluent-intro/" target="_blank" rel="noopener noreferrer">public launch</a> of two R packages -<a href="https://github.com/Appsilon/shiny.fluent" target="_blank" rel="noopener noreferrer"> shiny.fluent</a> and <a href="https://github.com/Appsilon/shiny.react" target="_blank" rel="noopener noreferrer">shiny.react</a>, the accessibility of React libraries to the Shiny dev community is now easier than ever. shiny.react opens up the rich React ecosystem (e.g., frameworks, blueprints, and components for charts and maps) to R developers, creating the opportunity to improve development speed, app functionality, and design. The ability to port entire React UI libraries is now possible through shiny.react. For professional, elegant designs consistent with enterprise-grade applications shiny.fluent joins the efficiency of Microsoft's Fluent UI and R's advanced data handling capabilities.  With a significant set of components allowing for unique functions like the '<a href="https://aniaskrzydlo.shinyapps.io/story/_w_c04eb737/#!/" target="_blank" rel="noopener noreferrer">People Picker</a>', '<a href="https://connect.appsilon.com/fluentui/#!/TeachingBubble" target="_blank" rel="noopener noreferrer">Teaching Bubble</a>', and '<a href="https://connect.appsilon.com/fluentui/#!/HoverCard" target="_blank" rel="noopener noreferrer">Hover Card</a>', and support for powerful functions like <a href="https://connect.appsilon.com/fluentui/_w_82687a2e/#!/Stack" target="_blank" rel="noopener noreferrer">Stack()</a>, users can be flexible with component placements in the dashboard. Through shiny.react, shiny.fluent makes it possible for the R community to access the power of React JavaScript library and Microsoft's Fluent UI. <ol><li><a href="#anchor-1" target="_blank" rel="noopener noreferrer">Welcome to shiny.fluent</a></li><li><a href="#anchor-2" target="_blank" rel="noopener noreferrer">Getting started</a></li><li><a href="#anchor-3" target="_blank" rel="noopener noreferrer">Creating shiny.fluent dashboards</a></li><li><a href="#anchor-4" target="_blank" rel="noopener noreferrer">Single Page Layout</a></li><li><a href="#anchor-5" target="_blank" rel="noopener noreferrer">Dashboard Layout</a></li><li><a href="#anchor-6" target="_blank" rel="noopener noreferrer">Filling All the Areas</a></li><li><a href="#anchor-7" target="_blank" rel="noopener noreferrer">Additional Pages</a></li><li><a href="#anchor-8" target="_blank" rel="noopener noreferrer">Conclusion</a></li></ol> <blockquote>Read More: <a href="https://appsilon.com/why-you-should-use-r-shiny-for-enterprise-application-development/" target="_blank" rel="noopener noreferrer">Why You Should Use Shiny for Enterprise Application Development</a></blockquote> <h2 id="anchor-1"><strong>Welcome to shiny.fluent</strong></h2> Shiny.fluent ports Fluent UI to R using shiny.react machinery, giving powerful, professional, and user-friendly interfaces to the Shiny community. In this tutorial, we will walk you through how to create a dashboard for a <a href="https://connect.appsilon.com/fluentui/" target="_blank" rel="noopener noreferrer">Fluent UI app</a> to improve upon a simple sales analysis application. If you haven't already, we recommend you start your shiny.fluent adventure <a href="https://appsilon.github.io/shiny.fluent/articles/shiny-fluent.html" target="_blank" rel="noopener noreferrer">here</a> to familiarize yourself with using Fluent components - the tutorial below will build on what you learn there.  <h2 id="anchor-2"><strong>Getting started with the Shiny tutorial</strong></h2> Shiny.react was created with ease of use in mind for R developers. Inputs are as close as possible to the Shiny API and the react package(s) documentation is provided inside the R documentation system. To begin use of shiny.fluent we will install the shiny.react and shiny.fluent packages.  <h3><strong>Installation</strong></h3> To install the packages, run: <pre>remotes::install_github("Appsilon/shiny.react") remotes::install_github("Appsilon/shiny.fluent") </pre> <h3><strong>Prerequisites</strong></h3> With shiny.react and shiny.fluent packages installed, let’s load the libraries needed for this example. <pre>library(dplyr) library(ggplot2) library(glue) library(leaflet) library(plotly) library(sass) library(shiny) library(shiny.fluent) library(shiny.router) </pre> <h3 id="anchor-3"><strong>Creating a shiny.fluent dashboard</strong></h3> In this tutorial, we will walk through how to build a dashboard UI. <h3 id="anchor-4"><strong>Single Page Layout</strong></h3> As a next step, let’s add a title and subtitle to our current app. We’ll create a helper function and call it makePage, so that it is easy to add more pages in the same fashion. <pre>makePage &lt;- function (title, subtitle, contents) {  tagList(div(    class = "page-title",    span(title, class = "ms-fontSize-32 ms-fontWeight-semibold", style =           "color: #323130"),    span(subtitle, class = "ms-fontSize-14 ms-fontWeight-regular", style =           "color: #605E5C; margin: 14px;")  ),  contents) } </pre> We can now take our entire UI built so far and put it in a “page” layout, giving it a helpful title and subtitle. <pre>analysis_page &lt;- makePage(  "Sales representatives",  "Best performing reps",  div(    Stack(      horizontal = TRUE,      tokens = list(childrenGap = 10),      makeCard("Filters", filters, size = 4, style = "max-height: 320px"),      makeCard("Deals count", plotlyOutput("plot"), size = 8, style = "max-height: 320px")    ),    uiOutput("analysis")  ) ) ui &lt;- fluentPage(  tags$style(".card { padding: 28px; margin-bottom: 28px; }"),  analysis_page ) </pre> <a href="https://appsilon.github.io/shiny.fluent/articles/shiny-fluent.html" target="_blank" rel="noopener noreferrer"><img class="aligncenter wp-image-7089 size-full" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b0206a2829666b590d22a9_sales-rep-dash.webp" alt="Simple dashboard exploring sales representatives data" width="1363" height="900" /></a> &nbsp; <h3 id="anchor-5"><strong>Dashboard Layout</strong></h3> It’s time to create a place for our header, navigation sidebar, and footer. We’ll use CSS grid for that. It’s a modern, flexible and straightforward way to achieve such a layout. We start by creating divs for each of the areas, with placeholder texts that we will later replace. <pre>header &lt;- "header" navigation &lt;- "navigation" footer &lt;- "footer" layout &lt;- function(mainUI){  div(class = "grid-container",      div(class = "header", header),      div(class = "sidenav", navigation),      div(class = "main", mainUI),      div(class = "footer", footer)  ) } </pre> Now it’s time to tell the browser using CSS how to arrange these areas. To define how our areas should be laid out on the page, let’s put the following rules in www/style.css: <pre>.grid-container {  display: grid;  grid-template-columns: 320px 1fr;  grid-template-rows: 54px 1fr 45px;  grid-template-areas: "header header" "sidenav main" "footer footer";  height: 100vh; } .header {  grid-area: header;  background-color: #fff;  padding: 6px 0px 6px 10px;  display: flex; } .main {  grid-area: main;  background-color: #faf9f8;  padding-left: 40px;  padding-right: 32px;  max-width: calc(100vw - 400px);  max-height: calc(100vh - 100px);  overflow: auto; } .footer {  grid-area: footer;  background-color: #f3f2f1;  padding: 12px 20px; } .sidenav {  grid-area: sidenav;  background-color: #fff;  padding: 25px; } </pre> We can also use this opportunity to add some additional styling for the entire page, and add the following rules to the same file: <pre>body {  background-color: rgba(225, 223, 221, 0.2);  min-height: 611px;  margin: 0; } .page-title {  padding: 52px 0px; } .card {  background: #fff;  padding: 28px;  margin-bottom: 28px;  border-radius: 2px;  background-clip: padding-box; } </pre> Now we only need to update our UI definition to load styles from www/style.css and use the new layout. <pre>ui &lt;- fluentPage(  layout(analysis_page),  tags$head(    tags$link(href = "style.css", rel = "stylesheet", type = "text/css")  )) </pre> <img class="aligncenter wp-image-7087 size-full" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b0206de8fe2e8669af003f_sales-rep-dash-2.webp" alt="Sales representative dashboard with empty header, navigation sidebar, and footer created with shiny.fluent" width="1826" height="1002" /> <h3 id="anchor-6"><strong>Filling All the Areas</strong></h3> Moving forward, we may now begin adding a Header, Footer, and Sidebar to the empty areas of our dashboard. <h4>Header</h4> Let’s replace the previous header definition with: <pre>header &lt;- tagList(  img(src = "appsilon-logo.png", class = "logo"),  div(Text(variant = "xLarge", "Sales Reps Analysis"), class = "title"),  CommandBar(    items = list(      CommandBarItem("New", "Add", subitems = list(        CommandBarItem("Email message", "Mail", key = "emailMessage", href = "mailto:me@example.com"),        CommandBarItem("Calendar event", "Calendar", key = "calendarEvent")      )),      CommandBarItem("Upload sales plan", "Upload"),      CommandBarItem("Share analysis", "Share"),      CommandBarItem("Download report", "Download")    ),    farItems = list(      CommandBarItem("Grid view", "Tiles", iconOnly = TRUE),      CommandBarItem("Info", "Info", iconOnly = TRUE)    ),    style = list(width = "100%"))) </pre> As you can see, we’re using CommandBar and CommandBarItem from shiny.fluent. We also need to add a bit of styling to our CSS file: <pre>.title {  padding: 0px 14px 0px 14px;  color: #737373;  margin: 6px 0px 6px 10px;  border-left: 1px solid darkgray;  width: 220px; } .logo {  height: 44px; } </pre> <h4>Navigation in the Sidebar</h4> Nav is a very powerful component from Fluent UI. It has very rich configuration options, but we will use it to show just a couple of links: <pre>navigation &lt;- Nav(  groups = list(    list(links = list(      list(name = 'Home', url = '#!/', key = 'home', icon = 'Home'),      list(name = 'Analysis', url = '#!/other', key = 'analysis', icon = 'AnalyticsReport'),      list(name = 'shiny.fluent', url = 'http://github.com/Appsilon/shiny.fluent', key = 'repo', icon = 'GitGraph'),      list(name = 'shiny.react', url = 'http://github.com/Appsilon/shiny.react', key = 'shinyreact', icon = 'GitGraph'),      list(name = 'Appsilon', url = 'http://appsilon.com', key = 'appsilon', icon = 'WebAppBuilderFragment')    ))  ),  initialSelectedKey = 'home',  styles = list(    root = list(      height = '100%',      boxSizing = 'border-box',      overflowY = 'auto'    )  ) ) </pre> <h4>Footer</h4> Footer is relatively straightforward - we can put anything we want there. Here we use Text for typography (setting uniform font styling). We also use Stack to arrange elements horizontally and with bigger gaps. <pre>footer &lt;- Stack(  horizontal = TRUE,  horizontalAlign = 'space-between',  tokens = list(childrenGap = 20),  Text(variant = "medium", "Built with ❤ by Appsilon", block=TRUE),  Text(variant = "medium", nowrap = FALSE, "If you'd like to learn more, reach out to us at hello@appsilon.com"),  Text(variant = "medium", nowrap = FALSE, "All rights reserved.") ) layout &lt;- function(mainUI){  div(class = "grid-container",      div(class = "header", header),      div(class = "sidenav", navigation),      div(class = "main", mainUI),      div(class = "footer", footer)  ) } # --- ui &lt;- fluentPage(  layout(analysis_page),  tags$head(    tags$link(href = "style.css", rel = "stylesheet", type = "text/css")  )) </pre> Let’s see how this looks together. <img class="aligncenter wp-image-7088 size-full" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b0206de8fe2e8669af00d2_sales-rep-dash-3-filled.webp" alt="Sales representative dashboard with filled header, navigation sidebar, and footer created with shiny.fluent" width="1855" height="976" /> <h3 id="anchor-7"><strong>Additional Pages</strong></h3> The final step is to add pages and enable navigation between them. <h4>Home Page</h4> Let’s make a home page, consisting of two cards with some welcome text. <pre>card1 &lt;- makeCard(  "Welcome to shiny.fluent demo!",  div(    Text("shiny.fluent is a package that allows you to build Shiny apps using Microsoft's Fluent UI."),    Text("Use the menu on the left to explore live demos of all available components.")  )) card2 &lt;- makeCard(  "shiny.react makes it easy to use React libraries in Shiny apps.",  div(    Text("To make a React library convenient to use from Shiny, we need to write an R package that wraps it - for example, a shiny.fluent package for Microsoft's Fluent UI, or shiny.blueprint for Palantir's Blueprint.js."),    Text("Communication and other issues in integrating Shiny and React are solved and standardized in shiny.react package."),    Text("shiny.react strives to do as much as possible automatically, but there's no free lunch here, so in all cases except trivial ones you'll need to do some amount of manual work. The more work you put into a wrapper package, the less work your users will have to do while using it.")  )) home_page &lt;- makePage(  "This is a Fluent UI app built in Shiny",  "shiny.react + Fluent UI = shiny.fluent",  div(card1, card2) ) </pre> If we replace analysis_page with home_page in our UI, we can see this page. However, there’s one problem: we don’t have a way to switch between pages! This is where so-called page routing comes into play. <h4>Adding shiny.router</h4> To enable switching between pages we will use the <a href="https://appsilon.github.io/shiny.router/" target="_blank" rel="noopener noreferrer">shiny.router</a> package. This way we will also have shareable URLs to individual pages. The first step is to define the available routes: <pre>router &lt;- make_router(  route("/", home_page),  route("other", analysis_page)) </pre> Now, we need to put router$ui in the place where we want the selected page to appear. (Currently, we also need to manually include router’s JavaScript dependencies, because shiny.fluent is not compatible with the way shiny.router loads this dependency. We expect this to be resolved in future versions of shiny.router). <pre># Add shiny.router dependencies manually: they are not picked up because they're added in a non-standard way. shiny::addResourcePath("shiny.router", system.file("www", package = "shiny.router")) shiny_router_js_src &lt;- file.path("shiny.router", "shiny.router.js") shiny_router_script_tag &lt;- shiny::tags$script(type = "text/javascript", src = shiny_router_js_src) ui &lt;- fluentPage(  layout(router$ui),  tags$head(    tags$link(href = "style.css", rel = "stylesheet", type = "text/css"),    shiny_router_script_tag  )) </pre> One final step is to add this single line to our server function, which otherwise remains untouched from the Part 1 of the Shiny tutorial. <pre>router$server(input, output, session) </pre> <h3>That's it!</h3> And there you go! We now have styled a shiny.fluent app into a solid dashboard layout. Here’s the final result: <a href="https://demo.appsilon.com/" target="_blank" rel="noopener noreferrer"><img class="aligncenter wp-image-7090 size-full" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b0206272a288e80b4e7233_tutorial-part2-final.gif" alt="Completed Fluent UI app using shiny.fluent with additional pages and routing options using shiny.router" width="1600" height="901" /></a> <h2 id="anchor-8">Summing up our Shiny tutorial with shiny.fluent</h2> The speed at which you can now add user-friendly and ubiquitous Microsoft product elements to your project is quite impressive. In a matter of minutes, we were able to create a functional, professional-looking Shiny dashboard with shiny.fluent. And if your users are already familiar with Microsoft products, adoption can be seamless.  There is more complexity over something like <a href="https://github.com/Appsilon/shiny.semantic" target="_blank" rel="noopener noreferrer">shiny.semantic</a>. However, an intermediate level of knowledge of Shiny is sufficient to begin. <a href="https://github.com/Appsilon/shiny.fluent/tree/master/inst/examples" target="_blank" rel="noopener noreferrer">Component examples</a> are provided and more comprehensive documentation can be found in the official Fluent UI docs or by typing shiny.fluent::  (e.g., ‘?shiny.fluent::MyComponentName’). Please feel free to post any comments regarding shiny.fluent on our <a href="https://github.com/Appsilon/shiny.fluent/discussions/24" target="_blank" rel="noopener noreferrer">feedback thread</a>. We love to hear feedback from our users and are happy to help should you have questions. If you like the package and want to show your support, please consider dropping a star on the <a href="https://github.com/Appsilon/shiny.fluent" target="_blank" rel="noopener noreferrer">shiny.fluent</a> package at our <a href="https://github.com/Appsilon" target="_blank" rel="noopener noreferrer">Github</a> and browse our other <a href="https://shiny.tools/" target="_blank" rel="noopener noreferrer">shiny packages</a>.  <img class="aligncenter size-full wp-image-7024" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b020493e47f6a35730bb22_Be-a-part-of-our-team.webp" alt="Be a part of our team" width="1200" height="628" /> <p style="text-align: center;"><b>Appsilon is hiring for remote roles! See our </b><a href="https://appsilon.com/careers/" target="_blank" rel="noopener noreferrer"><b>Careers</b></a><b> page for all open positions, including a </b><a href="https://appsilon.com/careers/job-offer/?job=senior-react-developer-freelancer" target="_blank" rel="noopener noreferrer"><b>React Developer</b></a><b> and </b><a href="https://appsilon.com/careers/job-offer/?job=r-shiny-developer" target="_blank" rel="noopener noreferrer"><b>R Shiny Developers</b></a>.<b> Join Appsilon and work on groundbreaking projects with the world’s most influential Fortune 500 companies.</b></p>

Contact us!
Damian's Avatar
Damian Rodziewicz
Head of Sales
shiny
shiny dashboards
r
shiny.fluent
tutorials