A Guide to Creating a Basic R Shiny App from a GitHub README (Markdown)
This was a product of boredom and curiosity, and I’m unsure if it made any sense to do this, but it’s done. Sort of.
Welcome to this step-by-step guide where we’ll transform a Markdown file (README.md) on GitHub into a simple interactive web application using R Shiny.
A bit of background, earlier this year, I worked on an AI Ethics and Policies centered project, and in the process of putting it together, I came across a lot of useful resources, so I put them all in a public GitHub repository. I update this almost regularly.
I randomly thought to myself, hmmm 🤔, can I turn this repo into a Shiny app for no reason? 🤨
Well…
Let’s break it down into simple, probably easy-to-follow steps.
There are best practices for writing technical blogs, and I’m not following any of them because…because. 😬
Let’s do it!
Step 1: Setting Up Your R Environment
Install R and RStudio
- Download and Install R: Visit the CRAN website and download R for your operating system.
- Download and Install RStudio: Go to the RStudio download page and install RStudio, a user-friendly interface for R.
Install Required Packages
- Open RStudio: Launch RStudio on your computer.
- Install Packages: In the RStudio console, type and run the following commands to install the necessary packages:
install.packages("shiny")
install.packages("httr")
install.packages("stringr")
Step 2: Create a New Shiny Project
- Start a New Project: In RStudio, go to
File > New File > Shiny Web Application
. - Name Your Project: Enter a name for your app, like “GitHubMarkdownViewer”, and choose where to save it. For me, it’s “aiethe3”, and I’m too lazy to change it because there were several iterations and I threw c̶a̶u̶t̶i̶o̶n̶ best practices to the wind. 😅
Step 3: Writing the App Code
Now, let’s break down the provided code into understandable parts.
1. Load Libraries
First, load the Shiny, httr, and stringr libraries. This is necessary to use the functions they provide.
library(shiny)
library(httr)
library(stringr)
This section loads three essential libraries:
- shiny: For building the interactive web app.
- httr: For making HTTP requests (here, to fetch the README file from GitHub).
- stringr: For string manipulation, useful in parsing text.
2. Fetching README from GitHub
The fetchReadme function fetches the README.md file from a specified GitHub repository.
fetchReadme <- function() {
url <- "https://raw.githubusercontent.com/gigikenneth/ai-ethics-resources/main/README.md"
res <- GET(url)
content <- content(res, "text")
return(content)
}
- fetchReadme: A custom function defined to fetch the README file.
- It constructs a URL pointing to the raw README.md file on GitHub.
GET(url)
fetches the file, andcontent(res, "text")
extracts its text content.
3. Parsing Markdown Content
parseMarkdown splits the markdown content into sections for easy display. Can we discuss the regex another day?
parseMarkdown <- function(mdContent) {
sections <- unlist(str_split(mdContent, "\n## "))
parsedSections <- lapply(sections[-1], function(section) {
header <- str_extract(section, "^[^\n]+")
bullets <- str_match_all(section, "- (.*)")[[1]][,2]
bullets <- sapply(bullets, markdownToHTML, USE.NAMES = FALSE)
list(header = header, bullets = bullets)
})
return(parsedSections)
}
- Splits the markdown into sections based on headers (denoted by
##
). - For each section, it extracts the header and bullet points.
- Converts markdown links in bullet points to HTML using
markdownToHTML
.
4. Converting Markdown Links to HTML
markdownToHTML converts markdown links to HTML format, making them clickable in the web app.
markdownToHTML <- function(text) {
return(gsub("\\[([^]]+)\\]\\(([^)]+)\\)", "<a href='\\2' target='_blank'>\\1</a>", text))
}
- A helper function that transforms markdown link syntax to HTML hyperlinks.
5. User Interface Design
The ui
object uses fluidPage
to create a flexible layout. It includes styling for tabs and a title panel.
ui <- fluidPage(
...
)
- Defines the layout and style of the app’s user interface.
- Uses
fluidPage
for a flexible layout. - The style within
tags$style(HTML("..."))
customizes the appearance of tabs.
ui <- fluidPage(
tags$head(
tags$style(HTML("
.nav-tabs .nav-link {
color: blue !important; /* Tab text color */
font-weight: bold !important; /* Make tab titles bold */
}
.nav-tabs .nav-link.active {
background-color: #5b5ba6 !important; /* Active tab color */
border-color: #5b5ba6 !important; /* Border color for active tab */
}
.nav-tabs .nav-link:hover {
background-color: #6d6dbf !important; /* Hover state color */
}
"))
)
6. Server Function
The server function handles the back-end operations of the app:
- Fetching and Parsing Data: It uses the
fetchReadme
andparseMarkdown
functions to get and process the GitHub markdown. - Dynamic UI: Uses
renderUI
to create tabs dynamically for each section of the markdown file.
server <- function(input, output, session) {
...
}
- The core logic of the Shiny app.
markdownContent
is a reactive value, updated with the latest README content.observe
block fetches and parses the markdown file every 5 minutes.output$tabsUI
dynamically creates tabs in the UI based on the parsed markdown.
server <- function(input, output, session) {
markdownContent <- reactiveVal(list())
observe({
content <- fetchReadme()
parsedContent <- parseMarkdown(content)
markdownContent(parsedContent)
invalidateLater(300000, session) # Refresh every 5 minutes
})
output$tabsUI <- renderUI({
content <- markdownContent()
if (is.null(content)) return(NULL)
tabs <- lapply(content, function(section) {
tabPanel(title = section$header,
HTML(paste("<ul><li>", paste(section$bullets, collapse = "</li><li>"), "</li></ul>")))
})
do.call(navbarPage, c("AI Ethics Resources", id = "nav", tabs))
})
}
7. Running the App
The shinyApp(ui, server)
call at the end of the script is what makes the app run.
shinyApp(ui, server)
- This command launches the Shiny app, combining the UI and server components.
8. Run Your App
Click the ‘Run App’ button in RStudio, and you’re done.
9. Debugging
If the app doesn’t work:
- Check for Errors: Look in the RStudio console for any error messages.
- Verify URLs: Ensure the GitHub URL in
fetchReadme
is correct and accessible.
10. Customization and Improvement
Feel free to customize the code (not sure why you’d want to try this, but okay). For example, change the GitHub URL to point to a different markdown file or modify the UI styling to fit your preferences. You could add a search bar (or maybe I’m delusional) or choose to deploy it; here’s a blog post about that.
Here’s the full code:
library(shiny)
library(httr)
library(stringr)
# Function to fetch README.md from GitHub
fetchReadme <- function() {
url <- "https://raw.githubusercontent.com/gigikenneth/ai-ethics-resources/main/README.md"
res <- GET(url)
content <- content(res, "text")
return(content)
}
# Function to parse markdown content
parseMarkdown <- function(mdContent) {
sections <- unlist(str_split(mdContent, "\n## "))
parsedSections <- lapply(sections[-1], function(section) {
header <- str_extract(section, "^[^\n]+")
# Extract bullet points and convert markdown links to HTML
bullets <- str_match_all(section, "- (.*)")[[1]][,2]
bullets <- sapply(bullets, function(bullet) {
markdownToHTML(bullet)
}, USE.NAMES = FALSE)
list(header = header, bullets = bullets)
})
return(parsedSections)
}
# Function to convert markdown links to HTML
markdownToHTML <- function(text) {
return(gsub("\\[([^]]+)\\]\\(([^)]+)\\)", "<a href='\\2' target='_blank'>\\1</a>", text))
}
ui <- fluidPage(
titlePanel("AI Ethics Resources"),
uiOutput("tabsUI")
)
server <- function(input, output, session) {
markdownContent <- reactiveVal(list())
observe({
content <- fetchReadme()
parsedContent <- parseMarkdown(content)
markdownContent(parsedContent)
invalidateLater(300000, session) # Refresh every 5 minutes
})
output$tabsUI <- renderUI({
content <- markdownContent()
if (is.null(content)) return(NULL)
tabs <- lapply(content, function(section) {
tabPanel(title = section$header,
HTML(paste("<ul><li>", paste(section$bullets, collapse = "</li><li>"), "</li></ul>")))
})
do.call(navbarPage, c("AI Ethics Resources", id = "nav", tabs))
})
}
shinyApp(ui, server)
Summary
Congratulations! You have created an interactive R Shiny app that fetches and displays a README.md file from GitHub.
Keep experimenting and exploring the vast capabilities of R and Shiny! See you in my next blog. 👋🏾