How can we pass (compile-time R-generated) HTML to an htmlwidget?

(This isn't strictly speaking a shiny question, just seemed like the closest thing).

We want to build an htmlwidget foo, which works on the basis of already prepared HTML.
This HTML is prepared at "compile" time (say, rmarkdown::render_...(), or even shinyapps::deploy...()), and will never need to change dynamically.
We prepare this HTML via htmltools, and it's an htmltools::tagList().

At runtime (be that shiny, or just a static html from rmarkdown), we do want the usual htmlwidget bells and whistles, say, some js action on some element, and this action should be programmatically defined by our foo htmlwidget. (So we're thinking this really is an htmlwidget use case).

The problem is that we can't seem to find a good way to pass the HTML on to our foo widget, on which the js can then work.

From perusing the docs, we find a couple of alternatives, most of which seem not to apply here:

  1. foo.yaml dependencies: accepts arbitrary HTML in via htmltools::htmlDependency(), but this is static throughout any release of the package, so cannot be altered at compile time (of an rmarkdown document).
  2. htmlwidgets::createWidget(..., dependencies = , ...) (also via htmltools::htmlDependency()) can be altered at compile time, but being limited to htmltools::htmlDependency(..., head =, ...) it only allows us to place HTML into the document head (or, apparently, the beginning of the body), which is nonsensical.
  3. Then there's the "magically named" foo_html.R (for any foo htmlwidget), but upon inspection of the source does not pass on ... arguments, and so we can't "reach" it with any compile-time arguments. It seems like a hack-fix anyway, and is clearly meant for a different, simpler purpose (replacing the widget parent, say <span> for <div>).
  4. htmlwidget::prependContent() can, well, prepend/append content to a widget, but a) pre/append position isn't really what we're looking for and b) the docs warn that this wouldn't work from inside shiny (which is a deal-breaker).
  5. I guess that, theoretically, we could pass HTML as a string (htmltools::doRender()) of our htmltools::tagList() result) to htmlwidget::createWidget(x = list(foo_string = foo_string), ...), have that transformed to JSON, and then write it out again via some simple JS. We haven't tried this, but this sounds kind of gross.
  6. Obviously, we could do it the way of most JS libraries which are wrapped by htmlwidgets and just write our custom HTML in JavaScript, and pass any necessary arguments to htmlwidgets::createWidget(x = list(...), ...), but we're not great in js, and have our cool htmltools::tagList() already.

We've tried 1-3 (and failed).
4 is too limited.
5 seems weird.
6 would duplicate existing work.

The concrete use case here is a custom table (including hex tesselation and other special sauce), to which we'd like to add some JS interaction.

How are we doing this wrong? :slight_smile:

2 Likes

It looks like you've thoroughly investigated the possible ways of dealing with this issue! To me it looks like (3) would be an elegant solution, if it worked. I think that you would need to file an issue on the htmlwidgets package and ask for changes there.

That said, you could just do (5). It's not the most elegant approach, but people do far worse things in JavaScript all the time.

2 Likes

Thanks @winston for the helpful pointer.

We ended up re-implementing the HTML-generation in JS, trying to stick closer to how htmlwidgets is / ought to be typically used.

We were in the weird situation of building an htmlwidget for a "js library" (really just a couple of line of code) that we were also building at the same time, and only for this use.

If other people are in a similar situation (using htmlwidgets for your own mini-js-libraries), I'd be happy to compare notes.

1 Like