{"id":19721759,"url":"https://github.com/rinterface/shinyheatmap","last_synced_at":"2025-10-11T05:34:31.142Z","repository":{"id":37593640,"uuid":"505916067","full_name":"RinteRface/shinyHeatmap","owner":"RinteRface","description":"Usage Heatmap for Shiny with heatmap.js","archived":false,"fork":false,"pushed_at":"2025-01-25T21:56:15.000Z","size":14714,"stargazers_count":22,"open_issues_count":7,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-07-20T04:55:42.816Z","etag":null,"topics":["heatmap","shiny-apps","usage-data"],"latest_commit_sha":null,"homepage":"https://rinterface.github.io/shinyHeatmap/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/RinteRface.png","metadata":{"files":{"readme":"README.md","changelog":"NEWS.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-06-21T16:11:16.000Z","updated_at":"2025-03-21T23:55:17.000Z","dependencies_parsed_at":"2025-04-29T21:43:39.184Z","dependency_job_id":null,"html_url":"https://github.com/RinteRface/shinyHeatmap","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/RinteRface/shinyHeatmap","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RinteRface%2FshinyHeatmap","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RinteRface%2FshinyHeatmap/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RinteRface%2FshinyHeatmap/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RinteRface%2FshinyHeatmap/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RinteRface","download_url":"https://codeload.github.com/RinteRface/shinyHeatmap/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RinteRface%2FshinyHeatmap/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279006322,"owners_count":26084085,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-11T02:00:06.511Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["heatmap","shiny-apps","usage-data"],"created_at":"2024-11-11T23:15:26.110Z","updated_at":"2025-10-11T05:34:31.137Z","avatar_url":"https://github.com/RinteRface.png","language":"JavaScript","readme":"\n# shinyHeatmap\n\n\u003c!-- badges: start --\u003e\n[![R-CMD-check](https://github.com/RinteRface/shinyHeatmap/workflows/R-CMD-check/badge.svg)](https://github.com/RinteRface/shinyHeatmap/actions)\n[![Lifecycle: experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://lifecycle.r-lib.org/articles/stages.html#experimental)\n\u003c!-- badges: end --\u003e\n\nThe goal of `{shinyHeatmap}` is to provide a __free__ and __local__ alternative to more advanced user tracking platform such as [Hotjar](https://www.google.com/search?q=hotjar\u0026oq=hotjar\u0026aqs=chrome.0.69i59j0i512j69i60l6.1063j0j7\u0026sourceid=chrome\u0026ie=UTF-8).\n\n`{shinyHeatmap}` generates beautiful and persistent visual heatmaps, representing the app usage across many user sessions. \n\n\u003cfigure\u003e\n  \u003cimg src=\"man/figures/shinyHeatmap-demo.png\"\u003e\n  \u003cfigcaption\u003eCommute explorer Shiny \u003ca href=\"https://community.rstudio.com/t/commute-explorer-shiny-contest-submission/104651\"\u003eapp\u003c/a\u003e (2021 Shiny Contest winner).\u003c/figcaption\u003e\n\u003c/figure\u003e\n\n\u003cbr\u003e\nIf you ever wondered:\n\n- Is the left action button used?\n- Did people notice the new tab?\n- Is the top left checkbox still useful?\n\nYou should give it a try! If you're concerned about data privacy, `{shinyHeatmap}` only records x and y clicks coordinates on the window.\n\n\u003cstyle\u003e\n.center {\n  display: block;\n  margin-left: auto;\n  margin-right: auto;\n  width: 50%;\n}\n\u003c/style\u003e\n\u003cimg src=\"man/figures/shinyHeatmap-ui.gif\" class=\"center\"\u003e\n\n## Note about shinyapps.io\n\nAt the moment, shinyHeatmap writes on local files. As a result, whenever the app is deployed on shinyapps.io, \nthe data storage is ephemeral (not persistent: data will be reset each time the apps\nbecome idle, or redeployed ...). See [here](https://docs.posit.co/shinyapps.io/Storage.html) for more details.\n\n## Examples\n\n### {shiny}\n\u003cimg src=\"man/figures/shinyHeatmap-basic.png\"\u003e\n\n### {bs4Dash}\n\u003cimg src=\"man/figures/shinyHeatmap-bs4Dash.png\"\u003e\n\n### {shinydashboard}\n\u003cimg src=\"man/figures/shinyHeatmap-shinydashboard.png\"\u003e\n\n### {shiny.fluent}\n\u003cimg src=\"man/figures/shinyHeatmap-shinyFluent.png\"\u003e\n\n## Installation\n\nYou can install the development version of `{shinyHeatmap}` from [GitHub](https://github.com/) with:\n\n``` r\n# install.packages(\"devtools\")\ndevtools::install_github(\"RinteRface/shinyHeatmap\")\n```\n\n## Getting started\n\n`{shinyHeatmap}` only requires 2 functions:\n\n- `with_heatmap()`, on the UI side.\n- `process_heatmap()`, on the server side.\n\nIn the following part, we provide more instructions.\n\n### How to use it\n\nThe app must have a `www` folder since \nheatmap data are stored in `www/heatmap-data.json` by default.\n\n1. In `ui.R`, wrap the UI inside `with_heatmap()`. This contains all necessary dependencies (JavaScript code, ...) to record and display\nthe heatmap.\n\n2. In `server.R`, call `process_heatmap()`. Overall, this recovers the\ncoordinates of each click on the JS side and store them in \n`www/heatmap-\u003cUSER_AGENT\u003e-\u003cDATE\u003e.json`. \nThis may be used later to preview the heatmap by aggregating all compatible user sessions. \nFor instance, mobile platforms are not aggregated with desktop since coordinates would be\nincorrect. With vanilla `{shiny}` templates like `fluidPage`, \nyou don't need to change anything. However, with more complex \ntemplates, you can pass the heatmap container CSS selector with the __target__ parameter such as `process_heatmap(target = \".wrapper\")`. \nIf the app takes time to load, a __timeout__ parameters is available. \nThis could be the case when you rely on packages\nsuch as [{waiter}](https://github.com/JohnCoene/waiter). For more\ncomplex patterns, you may want to use __trigger__ parameter to control\nwhen to record the heatmap. See the navbar example below.\n\n3. Locally, you can test your heatmap recording by using browsing to \n`http://127.0.0.1:\u003cPORT\u003e?get_heatmap`, `?get_heatmap` ensuring to run the app in preview mode for the heatmap. Under the hoods, `process_heatmap()` will call `download_heatmap()`, which will read data stored in the JSON files, generate the heatmap and save it as a png file. By default, `download_heatmap()` will show a tiny UI below your app. It allows to see a timeline of the app usage as shown below. To disable the UI, you can call `download_heatmap(show_ui = FALSE)`, which will show all the aggregated data as well as take a screenshot of the heatmap area.\n\n4. Deploy the app on the server of your choice (Rstudio Connect, Shiny server, ...) and let the end-users interact with it. \n\n5. To preview the heatmap from __deployed__ app, there are 2 solutions:\n\n  - Browse to the app url with a query parameter such as `\u003cAPP_URL\u003e?get_heatmap`, which will run the heatmap\nin __display__ mode so that you don't record extra actions by \ninteracting with it.\n  - Dump the deployed app `www` folder and copy it locally. Run\n  the local app with `\u003cAPP_URL\u003e?get_heatmap` as in 3.\n\nNote: Since `process_heatmap()` is clever enough to switch between\nrecording and display, we don't recommand using `record_heatmap()` or `download_heatmap()` directly. If you do, don't forget to remove `record_heatmap()` so that you don't generate extra logs when inspecting the heatmap.\n\nBelow shows an example to record the heatmap:\n\n```r\nlibrary(shiny)\nlibrary(shinyHeatmap)\n\n# Define UI for application that draws a histogram\nui \u003c- with_heatmap(\n  fluidPage(\n    # Application title\n    titlePanel(\"Old Faithful Geyser Data\"),\n    # Sidebar with a slider input for number of bins \n    sidebarLayout(\n      sidebarPanel(\n        sliderInput(\n          \"bins\",\n          \"Number of bins:\",\n          min = 1,\n          max = 50,\n          value = 30\n        )\n      ),\n      # Show a plot of the generated distribution\n      mainPanel(plotOutput(\"distPlot\"))\n    )\n  )\n)\n\n# Define server logic required to draw a histogram\nserver \u003c- function(input, output, session) {\n  \n  process_heatmap()\n  \n  output$distPlot \u003c- renderPlot({\n    # generate bins based on input$bins from ui.R\n    x    \u003c- faithful[, 2]\n    bins \u003c- seq(min(x), max(x), length.out = input$bins + 1)\n    \n    # draw the histogram with the specified number of bins\n    hist(x, breaks = bins, col = 'darkgray', border = 'white')\n  })\n}\n\n# Run the application \nshinyApp(ui = ui, server = server)\n```\n\n### App with navbar\nFor app with navbar like with `shiny::navbarPage()` or dashboard\nwith sidebar items, you'll need to record one heatmap per tab. This\ncan be achieve since `{shinyHeatmap}` __0.2.0.9000__ like below:\n\n- Give an __id__ to the navbar menu.\n- Pass it in the __trigger__ parameter of `process_heatmap()` (or `record_heatmap()` and `download_heatmap()`).\n\nTo browse between multiple pages, you'll have to toggle\nthe heatmap visibility thanks to the new toggle button. This is necessary because the heatmap z-index is set to the maximum and you can't click anywhere else after, except the toggle heatmap button.\n\n```r\nlibrary(shiny)\nlibrary(shinyHeatmap)\n\n# Define UI for application that draws a histogram\nui \u003c- with_heatmap(\n  navbarPage(\n    id = \"navbar\",\n    \"Navbar!\",\n    tabPanel(\n      \"Plot\",\n      sidebarLayout(\n        sidebarPanel(radioButtons(\n          \"plotType\", \"Plot type\",\n          c(\"Scatter\" = \"p\", \"Line\" = \"l\")\n        )),\n        mainPanel(plotOutput(\"plot\"))\n      )\n    ),\n    tabPanel(\"Summary\", verbatimTextOutput(\"summary\")),\n    navbarMenu(\n      \"More\",\n      tabPanel(\"Table\", DT::dataTableOutput(\"table\")),\n      tabPanel(\n        \"About\",\n        fluidRow(\n          column(6, \"Blabla\"),\n          column(\n            3,\n            img(\n              class = \"img-polaroid\",\n              src = paste0(\n                \"http://upload.wikimedia.org/\",\n                \"wikipedia/commons/9/92/\",\n                \"1919_Ford_Model_T_Highboy_Coupe.jpg\"\n              )\n            ),\n            tags$small(\n              \"Source: Photographed at the Bay State Antique \",\n              \"Automobile Club's July 10, 2005 show at the \",\n              \"Endicott Estate in Dedham, MA by \",\n              a(href = \"http://commons.wikimedia.org/wiki/User:Sfoskett\",\n                \"User:Sfoskett\")\n            )\n          )\n        )\n      )\n    )\n  ))\n\n# Define server logic required to draw a histogram\nserver \u003c- function(input, output, session) {\n  #record_heatmap(\n  #  trigger = reactive(input$navbar),\n  #  target = \"body\"\n  #)\n  #download_heatmap(trigger = reactive(input$navbar))\n  \n  process_heatmap(trigger = reactive(input$navbar), target = \"body\")\n  \n  output$plot \u003c- renderPlot({\n    plot(cars, type=input$plotType)\n  })\n  \n  output$summary \u003c- renderPrint({\n    summary(cars)\n  })\n  \n  output$table \u003c- DT::renderDataTable({\n    DT::datatable(cars)\n  })\n}\n\n# Run the application\nshinyApp(ui = ui, server = server)\n\n```\n\n### Options\n\n`{shinyHeatmap}` allows to tweak the heatmap style with few lines of code. This may\nbe achieved with the __options__ parameter that expects a list of properties available \nin the heatmap.js [documentation](https://www.patrick-wied.at/static/heatmapjs/docs.html). \nFor instance, below we change the points radius and color:\n\n```r\nprocess_heatmap(\n  options = list(\n    radius = 10,\n    maxOpacity = .5,\n    minOpacity = 0,\n    blur = .75,\n    gradient =  list(\n      \".5\" = \"blue\",\n      \".8\" = \"red\",\n      \".95\" = \"white\"\n    )\n  )\n)\n```\n\nThis is ideal if your app contains custom design like in the following example.\n\n\u003cimg src=\"man/figures/shinyHeatmap-theming-1.png\"\u003e\n\n## Acknowledgement\n\n`{shinyHeatmap}` is proudly powered by the excellent and free [heatmap.js](https://github.com/pa7/heatmap.js) library. Thanks [@pa7](https://github.com/pa7) for making this possible.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frinterface%2Fshinyheatmap","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frinterface%2Fshinyheatmap","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frinterface%2Fshinyheatmap/lists"}