R Shiny and ChatGPT: Can ChatGPT Help You Make Better R Shiny Apps?
ChatGPT - It's here to reshape the future of technology and programming, and some even argue it's going to<a href="https://www.businessinsider.com/chatgpt-jobs-at-risk-replacement-artificial-intelligence-ai-labor-trends-2023-02"> steal our jobs</a>. The truth? It's a tool. It’s highly unlikely, at least in the near future, to replace your data science team. But the question now is: “How can this tool bolster your productivity?” This tool, and others like it, got the R community wondering: “How good is ChatGPT for R Shiny?” Can you make R Shiny apps with ChatGPT? Can ChatGPT replace R developers? The short answer is again, <b>no</b>. But there is a lot of potential here. Continue reading for more in-depth explanations and some intriguing examples. <blockquote>Want to build your own Large Language Model (LLM), but are completely new to deep learning? <a href="https://appsilon.com/pytorch-neural-network-tutorial/" target="_blank" rel="noopener">You're not alone. Start with this PyTorch guide for beginners</a>.</blockquote> Table of contents: <ul><li><a href="#what-does-it-know">What does ChatGPT Know about R Shiny?</a></li><li><a href="#minimal-example">R Shiny ChatGPT Minimal Example</a></li><li><a href="#in-depth">R Shiny ChatGPT In-Depth Overview</a></li><li><a href="#summary">Summing Up R Shiny ChatGPT</a></li></ul> <hr /> <h2 id="what-does-it-know">What does ChatGPT Know about R Shiny?</h2> First, things first, what actually is ChatGPT? Put simply, it's a Natural Language Processing (NLP) tool that allows you to have human-like conversations, and much, much more. It was made by OpenAI in late November 2022, and it had more than 1 million users in the first 5 days after launch, which makes it the fastest-growing app of all time. You can use ChatGPT for free, and ask it the same questions you would normally type into Google. Most of the time, it will give you an accurate answer out of the box, but other times it might be entirely wrong. For example, it knows everything about math but doesn't know what day is it. Now, <b>what does it know about R Shiny?</b> Let's ask it - here's the prompt I've entered: <blockquote><i>> What do you know about R Shiny?</i></blockquote> And here's the answer: <img class="size-full wp-image-18650" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7d785dbea942dfb095cac_e63baddf_1-3.webp" alt="Image 1 - What GPT knows about R Shiny" width="3286" height="2306" /> Image 1 - What GPT knows about R Shiny Nothing groundbreaking, sure, as you can easily Google this stuff out. Nevertheless, ChatGPT is well aware of R Shiny as a technology, so maybe we can even ask it to write some Shiny apps from scratch. Let's see if that's the case. <h2 id="minimal-example">R Shiny ChatGPT Minimal Example</h2> In this section, we'll ask ChatGPT to provide us with a minimal working example of an R Shiny application. We don't care much about the app's functionality or looks, the only thing that matters is that it works. Here's the R Shiny ChatGPT prompt I've entered: <blockquote><i>> Can you provide me with a minimal boilerplate code for an R Shiny application?</i></blockquote> This is what ChatGPT returned: <img class="size-full wp-image-18652" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7d785ebc881cc38346b86_22e56998_2-3.webp" alt="Image 2 - ChatGPT output" width="3286" height="2560" /> Image 2 - ChatGPT output The results look promising, but let's copy the code and paste it into an R script to verify: <pre><code class="language-r"># Load the shiny package library(shiny) <br># Define the UI ui <- fluidPage( # Application title titlePanel("My Shiny App"), # Sidebar with a slider input sidebarLayout( sidebarPanel( sliderInput("n", "Number of observations:", min = 1, max = 1000, value = 500) ), # Show a plot of the generated distribution mainPanel( plotOutput("distPlot") ) ) ) <br># Define the server server <- function(input, output) { # Generate a histogram of random data output$distPlot <- renderPlot({ hist(rnorm(input$n)) }) } <br># Run the application shinyApp(ui = ui, server = server)</code></pre> Here's the resulting app: <img class="size-full wp-image-18654" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7d78649b91708eb342934_af622970_3-3.webp" alt="Image 3 - Resulting Shiny dashboard" width="2362" height="2086" /> Image 3 - Resulting Shiny dashboard The app looks almost identical (if not entirely) to the one you can find on the official documentation, but hey, it works. ChatGPT had no trouble writing this code for us, but will the same be the case as the app complexity increases? Let's find out in the following section. <h2 id="in-depth">R Shiny ChatGPT In-Depth Overview</h2> In this section, we'll try to build a slightly more complicated R Shiny app with ChatGPT. The idea is to start simple and see where GPT will break. We'll be brutally explicit with the prompts because it will give ChatGPT the best chance to return a meaningful output. <h3>Basic App</h3> The idea is as follows - we want an R Shiny application based on the <code>gapminder</code> dataset. We want a select input in the left panel allowing us to change the continent, and two charts in the main panel showing a summarized version of life expectancy and population over time on the currently selected continent. Sounds simple enough, but a Shiny app of this kind might throw you off if you're just starting out. Let's see what ChatGPT has to say. Here's the prompt I've entered: <blockquote><i>> I want to create an R Shiny application based on the Gapminder dataset, which is available in the R gapminder package. The idea is to have a select input in the left panel that allows me to select a continent, and then based on the selection, the main panel is updated. The main panel should contain a line chart showing life expecancy over time (dataset columns <code>lifeExp</code> and <code>year</code>), and a line chart showing population over time (dataset columns <code>pop</code> and <code>year</code>).</i></blockquote> And here's the output: <img class="size-full wp-image-18656" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7d78654840e14c9c666fb_64e04355_4-3.webp" alt="Image 4 - ChatGPT output (2)" width="3286" height="2560" /> Image 4 - ChatGPT output (2) Everything looks alright at first, but let's paste the code into an R script to see if the app works: <pre><code class="language-r"># Load the required packages library(shiny) library(gapminder) library(dplyr) <br># Define the UI ui <- fluidPage( # Application title titlePanel("Gapminder Data Explorer"), # Sidebar with a select input for choosing the continent sidebarLayout( sidebarPanel( selectInput("continent", "Select a continent:", choices = unique(gapminder$continent)) ), # Show line charts for life expectancy and population mainPanel( plotOutput("lifeExpChart"), plotOutput("popChart") ) ) ) <br># Define the server server <- function(input, output) { # Filter the gapminder dataset based on the selected continent continent_data <- reactive({ gapminder %>% filter(continent == input$continent) }) # Create a line chart for life expectancy over time output$lifeExpChart <- renderPlot({ ggplot(continent_data(), aes(x = year, y = lifeExp)) + geom_line() + xlab("Year") + ylab("Life Expectancy") + ggtitle("Life Expectancy over Time") }) # Create a line chart for population over time output$popChart <- renderPlot({ ggplot(continent_data(), aes(x = year, y = pop)) + geom_line() + xlab("Year") + ylab("Population") + ggtitle("Population over Time") }) } <br># Run the application shinyApp(ui = ui, server = server)</code></pre> It looks like we have an error: <img class="size-full wp-image-18658" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7d7876115b77c55a19db9_da2c0fbd_5-3.webp" alt="Image 5 - Resulting Shiny dashboard (2)" width="2690" height="2266" /> Image 5 - Resulting Shiny dashboard (2) It's an easy one to fix - ChatGPT just missed a single import (<code>ggplot2</code>), so we'll have to import it manually. Paste the following line of code at the top of the R script: <pre><code class="language-r">library(ggplot2)</code></pre> The app will now work if you re-run it: <img class="size-full wp-image-18660" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7d788c039958680cc242c_36e0e65d_6-3.webp" alt="Image 6 - Resulting Shiny dashboard (3)" width="2690" height="2266" /> Image 6 - Resulting Shiny dashboard (3) It kind of works, but something is wrong with the data. Let's address this next. <h3>Data Preprocessing Operations</h3> The app technically works, but it's far from what we want. The data shown on the charts isn't adequately prepared, which is a mistake made on our end. We've failed to tell ChatGPT to summarize the data before plotting it. The charts should show an <i>average</i> life expectancy/population per continent, but currently, they show the data for all countries within a continent group. Our next prompt will take care of data summarization, and also tell ChatGPT to import the missing <code>ggplot2</code> module. <blockquote><i>> That's an excellent start, but you're missing the ggplot2 package import. Also, please also group data for both charts in a way that only an average (mean) for that given year is shown.</i></blockquote> Here's the output returned by ChatGPT: <img class="size-full wp-image-18662" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7d78887994b7930bc21c1_e502ab1a_7-3.webp" alt="Image 7 - ChatGPT output (3)" width="3286" height="2560" /> Image 7 - ChatGPT output (3) It looks like it aggregated the data and stored it inside a <b>reactive value</b>, which is the correct practice in R Shiny. Let's copy the code into an R script and see if the app runs: <pre><code class="language-r"># Load the required packages library(shiny) library(gapminder) library(dplyr) library(ggplot2) <br># Define the UI ui <- fluidPage( # Application title titlePanel("Gapminder Data Explorer"), # Sidebar with a select input for choosing the continent sidebarLayout( sidebarPanel( selectInput("continent", "Select a continent:", choices = unique(gapminder$continent)) ), # Show line charts for life expectancy and population mainPanel( plotOutput("lifeExpChart"), plotOutput("popChart") ) ) ) <br># Define the server server <- function(input, output) { # Filter the gapminder dataset based on the selected continent continent_data <- reactive({ gapminder %>% filter(continent == input$continent) %>% group_by(year) %>% summarise(lifeExp = mean(lifeExp), pop = mean(pop)) }) # Create a line chart for life expectancy over time output$lifeExpChart <- renderPlot({ ggplot(continent_data(), aes(x = year, y = lifeExp)) + geom_line() + xlab("Year") + ylab("Life Expectancy") + ggtitle("Life Expectancy over Time") }) # Create a line chart for population over time output$popChart <- renderPlot({ ggplot(continent_data(), aes(x = year, y = pop)) + geom_line() + xlab("Year") + ylab("Population") + ggtitle("Population over Time") }) } <br># Run the application shinyApp(ui = ui, server = server)</code></pre> Here's the Shiny app: <img class="size-full wp-image-18664" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b01db771463a7a55840d98_8-3.webp" alt="Image 8 - Resulting Shiny dashboard (4)" width="2502" height="2254" /> Image 8 - Resulting Shiny dashboard (4) Much better. ChatGPT has corrected its previous mistakes (and ours) and is now showing the data in the format we wanted. The charts look a bit plain, so let's explore how good GPT is at styling them. <h3>Styling R Shiny charts with ChatGPT</h3> You can use R's <code>ggplot2</code> package to make some <a href="https://appsilon.com/ggplot2-line-charts/" target="_blank" rel="noopener">stunning charts</a>, but tweaking the visuals will require some manual labor. In this section, we'll ask ChatGPT to style the R Shiny charts slightly to hopefully produce a better-looking output. Here's a prompt with everything we want - removing the chart background color, increasing the font size, and changing the line color: <blockquote><i>> Now everything is working as expected, great work! Can you also show me how to style these charts? I don't like how plain they look. It would be good if you could remove the chart background color, increase the font size, and change the line color to #0099f9.</i></blockquote> <img class="size-full wp-image-18666" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b020f85ff524b5d1fc0ddb_9-3.webp" alt="Image 9 - ChatGPT output (4)" width="3286" height="2560" /> Image 9 - ChatGPT output (4) And here's the code snippet returned by ChatGPT: <pre><code class="language-r"># Load the required packages library(shiny) library(gapminder) library(dplyr) library(ggplot2) <br># Define the UI ui <- fluidPage( # Application title titlePanel("Gapminder Data Explorer"), # Sidebar with a select input for choosing the continent sidebarLayout( sidebarPanel( selectInput("continent", "Select a continent:", choices = unique(gapminder$continent)) ), # Show line charts for life expectancy and population mainPanel( plotOutput("lifeExpChart"), plotOutput("popChart") ) ) ) <br># Define the server server <- function(input, output) { # Filter the gapminder dataset based on the selected continent continent_data <- reactive({ gapminder %>% filter(continent == input$continent) %>% group_by(year) %>% summarise(lifeExp = mean(lifeExp), pop = mean(pop)) }) # Create a line chart for life expectancy over time output$lifeExpChart <- renderPlot({ ggplot(continent_data(), aes(x = year, y = lifeExp)) + geom_line(color = "#0099f9", size = 1.5) + xlab("Year") + ylab("Life Expectancy") + ggtitle("Life Expectancy over Time") + theme(panel.background = element_blank(), panel.grid.major = element_blank(), panel.grid.minor = element_blank(), axis.line = element_line(color = "black"), axis.text = element_text(size = 12), axis.title = element_text(size = 14, face = "bold")) }) # Create a line chart for population over time output$popChart <- renderPlot({ ggplot(continent_data(), aes(x = year, y = pop)) + geom_line(color = "#0099f9", size = 1.5) + xlab("Year") + ylab("Population") + ggtitle("Population over Time") + theme(panel.background = element_blank(), panel.grid.major = element_blank(), panel.grid.minor = element_blank(), axis.line = element_line(color = "black"), axis.text = element_text(size = 12), axis.title = element_text(size = 14, face = "bold")) }) } <br># Run the application shinyApp(ui = ui, server = server)</code></pre> We've pasted it in our R script, so let's see if everything still works: <img class="size-full wp-image-18668" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b020fae9c00d5eac14cbaa_10-3.webp" alt="Image 10 - Resulting Shiny dashboard (5)" width="2682" height="2266" /> Image 10 - Resulting Shiny dashboard (5) ChatGPT did a great job styling our charts. They're now much more minimal-looking, and the line color matches nicely with Appsilon's color scheme. Up next, let's see what GPT knows about <a href="https://appsilon.com/howto-css-and-shiny/" target="_blank" rel="noopener">CSS in Shiny</a>. <h3>Adding CSS to R Shiny with ChatGPT</h3> We can complicate things slightly for ChatGPT by introducing yet another technology - CSS. It is used for styling websites and applications, and Shiny developers use either plain CSS or SCSS to customize the looks of their dashboards. The question is - How well can ChatGPT balance between R Shiny and CSS? There's only one way to find out. I've asked ChatGPT the following: <blockquote><i>> Just what I wanted! I would also like to include custom CSS to this app. Can you show me how? I would really love to have a dark theme. Also, please use the Poppins font.</i></blockquote> It's a really simple prompt since we only want a dark theme and a custom font. Here's a response: <img class="size-full wp-image-18670" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b020fa0279bfd45037810d_11-3.webp" alt="Image 11 - ChatGPT output (5)" width="3286" height="2560" /> Image 11 - ChatGPT output (5) It's quite a lengthy one, and this is where we run into the first issue - <b>ChatGPT has stopped generating code and we had to manually instruct it to continue</b>. It's not a dealbreaker, but GPT failed to recognize it was generating R code in the previous message, and continued to format the code as Markdown. Again, not a huge issue if you're familiar with R, but it might throw some beginners off. Below you'll see the second portion of the output: <img class="size-full wp-image-18672" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b020fbab40efae1ca9ccb2_12-3.webp" alt="Image 12 - ChatGPT output (6)" width="3286" height="2560" /> Image 12 - ChatGPT output (6) Anyhow, let's copy the code into the R script to see what ChatGPT came up with: <pre><code class="language-r"># Load the required packages library(shiny) library(gapminder) library(dplyr) library(ggplot2) <br># Define the UI ui <- fluidPage( # Application title titlePanel("Gapminder Data Explorer"), <br> # Custom CSS for a dark theme and Poppins font tags$head(tags$style(HTML(" body { background-color: #222222; font-family: 'Poppins', sans-serif; color: #ffffff; } <br> .navbar { background-color: #1a1a1a !important; border-color: #1a1a1a !important; color: #ffffff; font-weight: bold; font-size: 24px; } <br> .well { background-color: #1a1a1a !important; border-color: #1a1a1a !important; color: #ffffff; } <br> .selectize-control.single .selectize-input { color: #ffffff; background-color: #333333; border-color: #333333; } <br> .selectize-dropdown { background-color: #333333; border-color: #333333; } <br> .selectize-dropdown-content .active { background-color: #555555; color: #ffffff; } <br> .selectize-dropdown-content .highlighted { background-color: #666666; color: #ffffff; } <br> .plot-container { background-color: #222222 !important; } <br> .ggtitle { color: #ffffff !important; font-size: 20px; font-weight: bold; } <br> .axis line, .axis path { color: #ffffff; } <br> .axis text { font-size: 16px; font-weight: bold; } <br> .legend.text { color: #ffffff; font-size: 16px; font-weight: bold; } <br> .legend.title { color: #ffffff; font-size: 20px; font-weight: bold; } "))), <br> # Sidebar with a select input for choosing the continent sidebarLayout( sidebarPanel( selectInput("continent", "Select a continent:", choices = unique(gapminder$continent) ) ), <br> # Show line charts for life expectancy and population mainPanel( plotOutput("lifeExpChart"), plotOutput("popChart") ) ) ) <br># Define the server server <- function(input, output) { # Filter the gapminder dataset based on the selected continent continent_data <- reactive({ gapminder %>% filter(continent == input$continent) %>% group_by(year) %>% summarise(lifeExp = mean(lifeExp), pop = mean(pop)) }) <br> # Create a line chart for life expectancy over time output$lifeExpChart <- renderPlot({ ggplot(continent_data(), aes(x = year, y = lifeExp)) + geom_line(color = "#0099f9", size = 1.5) + xlab("Year") + ylab("Life Expectancy") + ggtitle("Life Expectancy over Time") + theme( panel.background = element_blank(), panel.grid.major = element_blank(), panel.grid.minor = element_blank(), axis.line = element_line(size = 1, color = "white"), axis.text = element_text(size = 14), legend.text = element_text(size = 14), legend.title = element_text(size = 16), plot.title = element_text(hjust = 0.5) ) }) <br> # Create a line chart for population over time output$popChart <- renderPlot({ ggplot(continent_data(), aes(x = year, y = pop)) + geom_line(color = "#0099f9", size = 1.5) + xlab("Year") + ylab("Population") + ggtitle("Population over Time") + theme( panel.background = element_blank(), panel.grid.major = element_blank(), panel.grid.minor = element_blank(), axis.line = element_line(size = 1, color = "white"), axis.text = element_text(size = 14), legend.text = element_text(size = 14), legend.title = element_text(size = 16), plot.title = element_text(hjust = 0.5) ) }) } <br># Run the application shinyApp(ui = ui, server = server)</code></pre> If anything, the dashboard looks worse now: <img class="size-full wp-image-18674" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b02136f8f7f11a90a39431_13-3.webp" alt="Image 13 - Resulting Shiny dashboard (6)" width="2794" height="2266" /> Image 13 - Resulting Shiny dashboard (6) But wait until you click on the select input - it's truly horrifying: <img class="size-full wp-image-18676" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b023eac79554d89c1deabb_14-3.webp" alt="Image 14 - Resulting Shiny dashboard (7)" width="2794" height="2266" /> Image 14 - Resulting Shiny dashboard (7) Overall, the results are not what we expected. Did we run into ChatGPT limitations for R Shiny? Is it too much code to handle at once? Or have we failed to provide instructions that are detailed enough? Let's find out next. <h3>Adding CSS to R Shiny with ChatGPT (2nd Attempt)</h3> ChatGPT has included Poppins as our font of choice and it has added a dark theme to the app, but the visuals still leave a lot to be desired. We'll now instruct it to keep working on the stylings by providing additional inputs. Here's the prompt: <blockquote><i>> Thank you, you're on a right track. Just a couple of tweaks: - Make sure the chart background color also reflects the dark theme. It's white now, and it's not very pleasant to look at - Select input component elements text is not visible for some reason.</i> Can you fix these?</blockquote> Let's take a look at the results: <img class="size-full wp-image-18678" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b0243d98693685815ffddc_15-3.webp" alt="Image 15 - ChatGPT output (7)" width="3286" height="2560" /> Image 15 - ChatGPT output (7) This is the generated code snippet: <pre><code class="language-r">library(shiny) library(gapminder) library(dplyr) library(ggplot2) <br># Define custom CSS css <- " body { background-color: #2b2b2b; font-family: 'Poppins', sans-serif; font-size: 16px; color: #fff; } select { color: #000; background-color: #fff; } .selectize-dropdown-content { background-color: #fff; color: #000; } .plot-container { background-color: #2b2b2b; } form.well { background-color: #2b2b2b; } " <br># Define UI ui <- fluidPage( # Add custom CSS to the head of the document tags$head(tags$style(HTML(css))), # Add a title titlePanel("Gapminder Data"), # Define the sidebar with the continent selector sidebarLayout( sidebarPanel( selectInput(inputId = "continent", label = "Select a continent:", choices = unique(gapminder$continent), selected = "Europe") ), # Define the main panel with the plots mainPanel( div(class = "plot-container", plotOutput(outputId = "lifeExpChart", height = 400)), div(class = "plot-container", plotOutput(outputId = "popChart", height = 400)) ) ) ) <br># Define server server <- function(input, output) { # Define a reactive function to filter the data based on the selected continent continent_data <- reactive({ gapminder %>% filter(continent == input$continent) %>% group_by(year) %>% summarise(lifeExp = mean(lifeExp), pop = mean(pop)) }) # Create a line chart for life expectancy over time output$lifeExpChart <- renderPlot({ ggplot(continent_data(), aes(x = year, y = lifeExp)) + geom_line(color = "#0099f9", size = 1.5) + xlab("Year") + ylab("Life Expectancy") + ggtitle("Life Expectancy over Time") + theme(panel.background = element_rect(fill = "#2b2b2b"), panel.grid.major = element_line(size = 0.2, color = "white"), panel.grid.minor = element_blank(), axis.line = element_line(size = 1, color = "white"), axis.text = element_text(size = 14, color = "white"), legend.text = element_text(size = 14, color = "white"), legend.title = element_text(size = 16, color = "white"), plot.title = element_text(hjust = 0.5, color = "white")) }) # Create a line chart for population over time output$popChart <- renderPlot({ ggplot(continent_data(), aes(x = year, y = pop)) + geom_line(color = "#0099f9", size = 1.5) + xlab("Year") + ylab("Population") + ggtitle("Population over Time") + theme(panel.background = element_rect(fill = "#2b2b2b"), panel.grid.major = element_line(size = 0.2, color = "white"), panel.grid.minor = element_blank(), axis.line = element_line(size = 1, color = "white"), axis.text = element_text(size = 14, color = "white"), legend.text = element_text(size = 14, color = "white"), legend.title = element_text(size = 16, color = "white"), plot.title = element_text(hjust = 0.5, color = "white")) }) } <br># Run the app shinyApp(ui, server)</code></pre> The dashboard looks worse if that's even possible: <img class="size-full wp-image-18680" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b0243fa24514c234bc7748_16-3.webp" alt="Image 16 - Resulting Shiny dashboard (8)" width="2412" height="2266" /> Image 16 - Resulting Shiny dashboard (8) <b>What went wrong here?</b> It's hard to say, but it looks like we'll have to tell ChatGPT explicitly what to do with the styles. In addition, the way ChatGPT integrates CSS into R Shiny is different every time, which some developers won't appreciate. It doesn't matter much for the resulting app, but we would like to see ChatGPT being consistent with the optimal coding practices. Let's try adding explicit styling instructions next. <h3>Adding CSS to R Shiny with ChatGPT (3rd Attempt)</h3> After some manual inspection, it looks like the HTML <code>form</code> tag with a CSS class of <code>well</code> is responsible for the overall styles of our inputs. We'll tell that explicitly to ChatGPT, and also instruct it to somehow solve the issue of white color surrounding the charts. Here's the entire prompt: <blockquote><i>> Add form.well { background-color: #2b2b2b; } to styles.</i> Also, the background color surrounding the chart content is still white. Can you change it to #2b2b2b?</blockquote> And here's the entire generated code (It was once again split into multiple responses): <img class="size-full wp-image-18682" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b0239f7e876a3f78c78793_17-2.webp" alt="Image 17 - ChatGPT output (8)" width="3286" height="2560" /> Image 17 - ChatGPT output (8) Let's copy this code and test it out: <pre><code class="language-r">library(shiny) library(gapminder) library(dplyr) library(ggplot2) <br># Define UI ui <- fluidPage( tags$head( # Import Google Fonts tags$link( rel = "stylesheet", href = "https://fonts.googleapis.com/css?family=Poppins&display=swap" ), # Add custom styles tags$style( " body { background-color: #2b2b2b; color: white; font-family: 'Poppins', sans-serif; } select { background-color: #2b2b2b; color: white; } form.well { background-color: #2b2b2b; } .plot-container { background-color: #2b2b2b; } " ) ), # Sidebar layout sidebarLayout( # Sidebar panel sidebarPanel( selectInput(inputId = "continent", label = "Select a continent", choices = c("Asia", "Africa", "Americas", "Europe", "Oceania"), selected = "Asia") ), # Main panel mainPanel( plotOutput(outputId = "lifeExp_plot", height = "400px"), plotOutput(outputId = "pop_plot", height = "400px") ) ) ) <br># Define server logic server <- function(input, output) { # Filter data based on continent selection continent_data <- reactive({ gapminder %>% filter(continent == input$continent) }) # Compute mean life expectancy by year lifeExp_by_year <- reactive({ continent_data() %>% group_by(year) %>% summarize(mean_lifeExp = mean(lifeExp)) }) # Compute mean population by year pop_by_year <- reactive({ continent_data() %>% group_by(year) %>% summarize(mean_pop = mean(pop)) }) # Render life expectancy plot output$lifeExp_plot <- renderPlot({ ggplot(lifeExp_by_year(), aes(x = year, y = mean_lifeExp)) + geom_line(color = "#0099f9", size = 2) + labs(title = "Life Expectancy over Time", x = "Year", y = "Mean Life Expectancy") + theme_minimal() + theme(plot.background = element_rect(fill = "#2b2b2b"), panel.background = element_rect(fill = "#2b2b2b"), axis.text = element_text(size = 14, color = "white"), axis.title = element_text(size = 16, color = "white"), legend.background = element_rect(fill = "#2b2b2b"), legend.text = element_text(size = 14, color = "white"), legend.title = element_text(size = 16, color = "white"), plot.title = element_text(hjust = 0.5, color = "white")) }) <br> # Create a line chart for population over time output$pop_plot <- renderPlot({ ggplot(continent_data(), aes(x = year, y = pop)) + geom_line(color = "#0099f9", size = 1.5) + xlab("Year") + ylab("Population") + ggtitle("Population over Time") + theme(plot.background = element_rect(fill = "#2b2b2b"), panel.background = element_rect(fill = "#2b2b2b"), axis.text = element_text(size = 14, color = "white"), axis.title = element_text(size = 16, color = "white"), legend.background = element_rect(fill = "#2b2b2b"), legend.text = element_text(size = 14, color = "white"), legend.title = element_text(size = 16, color = "white"), plot.title = element_text(hjust = 0.5, color = "white")) }) } <br># Run the app shinyApp(ui, server)</code></pre> It's a win, but a bittersweet one: <img class="size-full wp-image-18684" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7d791917cd6e112228b6c_01b1ba36_18-1.webp" alt="Image 18 - Resulting Shiny dashboard (9)" width="3016" height="2298" /> Image 18 - Resulting Shiny dashboard (9) ChatGPT managed to solve the chart styling issue but has for some reason modified the code for the second chart. This was unnecessary, but something we obviously have to live with at the moment. Currently, CSS styles are written inside the R script, which isn't a recommended practice. Let's see if GPT knows how to separate these into two files. <h3>Separating CSS Styles from the R Shiny App</h3> This is the final section - we promise. Now we want to separate CSS styles from the dashboard code. Can ChatGPT do this for us? There's only one way to find out. Here's the prompt: <blockquote><i>> Still needs a bit work, but I can manage from here, thanks! Last question - can you show me how to extract the CSS styles into a separate file for R Shiny? The app code looks messy currently.</i></blockquote> ChatGPT has returned this output (over multiple responses): <img class="size-full wp-image-18686" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b02442f904062e7c9990fc_19-2.webp" alt="Image 19 - ChatGPT output (9)" width="3286" height="2560" /> Image 19 - ChatGPT output (9) <pre><code class="language-r">library(shiny) <br>ui <- fluidPage( includeCSS("styles.css"), sidebarLayout( sidebarPanel( selectInput( inputId = "continent", label = "Select a continent:", choices = unique(gapminder$continent), selected = "Asia" ) ), mainPanel( plotOutput(outputId = "lifeExpPlot"), plotOutput(outputId = "popPlot") ) ) ) <br>server <- function(input, output) { data <- reactive({ gapminder %>% filter(continent == input$continent) %>% group_by(year) %>% summarise(mean_lifeExp = mean(lifeExp), mean_pop = mean(pop)) }) output$lifeExpPlot <- renderPlot({ ggplot(data(), aes(x = year, y = mean_lifeExp)) + geom_line(color = "#0099f9", size = 1.5) + labs(title = "Life Expectancy Over Time", x = "Year", y = "Life Expectancy") + theme_minimal() + theme(plot.background = element_rect(fill = "#2b2b2b"), panel.background = element_rect(fill = "#2b2b2b"), axis.text = element_text(size = 16, color = "#f5f5f5"), axis.title = element_text(size = 18, color = "#f5f5f5")) }) output$popPlot <- renderPlot({ ggplot(data(), aes(x = year, y = mean_pop)) + geom_line(color = "#0099f9", size = 1.5) + labs(title = "Population Over Time", x = "Year", y = "Population") + theme_minimal() + theme(plot.background = element_rect(fill = "#2b2b2b"), panel.background = element_rect(fill = "#2b2b2b"), axis.text = element_text(size = 16, color = "#f5f5f5"), axis.title = element_text(size = 18, color = "#f5f5f5")) }) } <br>shinyApp(ui, server)</code></pre> It has also instructed us to create a new file named <code>styles.css</code> right where the R script is and add the followign code: <pre><code class="language-css">body { background-color: #2b2b2b; color: white; font-family: 'Poppins', sans-serif; } select { background-color: #2b2b2b; color: white; } form.well { background-color: #2b2b2b; } .plot-container { background-color: #2b2b2b; }</code></pre> The CSS file is included in the R script via the <code>includeCSS()</code> function. Here's what the resulting dashboard looks like: <img class="size-full wp-image-18688" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b02444f904062e7c999201_20-2.webp" alt="Image 20 - Resulting Shiny dashboard (10)" width="2802" height="2228" /> Image 20 - Resulting Shiny dashboard (10) GPT has magically retweaked the code for the second chart, who knows why. The CSS styles still work correctly, but a recommended practice with R Shiny is to put CSS files (and other static assets) into the <code>www</code> folder. Everything still works with GPT's approach, but we thought you should know that. So, should you fear for your job? Read the conclusion section to read our thoughts. <hr /> <h2 id="summary">Summing up R Shiny ChatGPT</h2> In a nutshell, yes, ChatGPT can write R Shiny code. Is it any good? Well, that depends on the task. We've managed to semi break it with an utterly simple dashboard example, so we can only imagine what would happen on a more complex example that involves multiple technologies and dependencies. We currently see ChatGPT as a playground when it comes to R Shiny. It can write some basic code for you, which means you don't have to search for the answer manually to see what works. It's faster than Googling, but nowhere near as fast as a seasoned R Shiny professional. Generating this example with GPT took us a while, and our team of Shiny experts could have easily produced the same output in a fraction of the time. If you're a beginner, maybe ChatGPT is a better (certainly faster) solution than Stack Overflow. Truth be told, the R Shiny community isn't as big as let's say Python Django one, so you have to manage your expectations when it comes to finding step-by-step materials online. And that's where <b>Appsilon</b> comes in. If you're stuck building an R Shiny application for your business and ChatGPT can't help you, don't hesitate to reach out. We have a team of R Shiny professionals ready to help you out. Feel free to <a href="https://appsilon.com/#contact" target="_blank" rel="noopener">leave us a message</a>, and we'll make sure to respond within 24 hours.