I’ve found the htmlDependency() function from htmltools to be surprsingly confusing over the years. The src field is a vector with a href and file entry, and how those values are translated and used in the resulting <script src="..."> tag has always been very confusing for me.

Here’s an example of how the function is called:

  htmlDependency(
    "jquery", "3.7.0",
    src = c(
      href = "shared",
      file = "www/shared"
    ),
    package = "shiny",
    script = "jquery.js"
  )

Test code

This UI code is used to test static and dynamic rendering of HTML dependency objects. The values in the src field are changed for different test conditions.

library(shiny)
library(htmltools)
ui <- fluidPage(
  "Hello, world",
  htmlDependency(
    "jquery", "3.7.0",
    src = c(
      # href = "shared",
      # href = "https://code.jquery.com/jquery-3.7.0",
      file = "www/shared"
    ),
    package = "shiny",
    script = "jquery.js"
  )
)

# For static rendering:
browsable(ui)

# For dynamic rendering:
runApp(shinyApp(ui, function(input, output) {}))

(Note that there’s not actually a jQuery 3.7.0. I just used that version number so this htmlDependency object would override the one that Shiny includes by default.)

When running this test code, we look at the resulting HTML for a line like this:

<script src="lib/jquery-3.7.0/jquery.js"></script>

The value of src is the result in the table below.

Results

render file href result files notes
static shared shared/jquery.js none broken
static www/shared lib/jquery-3.7.0/jquery.js copied
static www/shared shared lib/jquery-3.7.0/jquery.js copied
dynamic shared shared/jquery.js none
dynamic www/shared jquery-3.7.0/jquery.js mounted
dynamic www/shared shared shared/jquery.js none

Findings

  • When static rendering is used:
    • If file is present, it will be used, and copied to lib/<name>-<version>/<filename>. It will also copy the files to that relative path.
    • If file is not present, it will use href, as in <href>/<filename>. The page will be broken, since the files won’t actually be available there.
  • When dynamic rendering is used:
    • If href is present, it will use it, as in <href>/<filename>.
    • If href is not present, it will use file, and point to <name>-<version>/<filename>. It will also make the files available at that path (via shiny::addResourcePath()).

Additional notes

It doesn’t matter if href is a relative path, or an absolute URL. The behavior is the same in both cases. Results when href is a remote URL:

render file href result files notes
static https://code.jquery.com/jquery-3.7.0 https://code.jquery.com/jquery-3.7.0/jquery.js none
static www/shared https://code.jquery.com/jquery-3.7.0 lib/jquery-3.7.0/jquery.js copied
dynamic https://code.jquery.com/jquery-3.7.0 https://code.jquery.com/jquery-3.7.0/jquery.js none
dynamic www/shared https://code.jquery.com/jquery-3.7.0 https://code.jquery.com/jquery-3.7.0/jquery.js none