html element construction for R.
hypertext provides a deterministic, framework-agnostic DSL for building
html nodes and rendering them to a string.
it does not implement templating, dependency management, widgets or framework integrations.
heavily inspired by {htmltools}.
install the stable version from CRAN:
install.packages("hypertext")or get the development version from github:
devtools::install_github("sigflux/hypertext")library(hypertext)
page <- tag_list(
doctype(),
tags$html(
tags$head(
tags$title("hypertext")
),
tags$body(
tags$h1("Hello"),
tags$p(
class = c("lead", "mb-2"),
"Server-side HTML."
),
tags$input(
type = "text",
placeholder = "enter your nickname"
),
tags$button(
"Click"
)
)
)
)
render(page)render() takes a tag tree and returns a single HTML string:
x <- tags$p(
class = "lead",
"hello"
)
# `x` contains the tag tree
class(x)
#> [1] "hypertext.tag"
# rendering produces an HTML string:
render(x)
#> [1] "<p class=\"lead\">hello</p>"you can render() directly to an html file by supplying
the file parameter:
library(hypertext)
page <- tag_list(
doctype(),
tags$html(
tags$head(
tags$title("hypertext")
),
tags$body(
tags$h1("Hello"),
tags$p(
class = c("lead", "mb-2"),
"Server-side HTML."
),
tags$input(
type = "text",
placeholder = "enter your nickname"
),
tags$button(
"Click"
)
)
)
)
render(x = page, file = "index.html")tag_list() groups sibling nodes without wrapping them in a parent element.
library(hypertext)
header <- tag_list(
tags$h1("hello"),
tags$p(
class = "lead",
"welcome aboard."
)
)
render(header)
#> [1] "<h1>hello</h1><p class=\"lead\">welcome aboard.</p>"raw_html() marks a string as pre-rendered HTML so that render() outputs
it verbatim, without escaping. useful for injecting inline scripts, styles,
SVG markup, or any content that is already valid HTML.
library(hypertext)
page <- tags$div(
raw_html("<svg viewBox='0 0 100 100'><circle cx='50' cy='50' r='40'/></svg>")
)
render(page)
#> [1] "<div><svg viewBox='0 0 100 100'><circle cx='50' cy='50' r='40'/></svg></div>"doctype() is a convenience wrapper around raw_html("<!DOCTYPE html>").
tag() creates elements for any tag name, including web components and
custom elements not in the built-in tags list. it takes 3 arguments:
tag_name: name of the html element....: attributes (named) & children (unnamed).tag_type: either "normal" (default) for the standard html elements, or "void" for the self-closing elements.
library(hypertext)
content <- tag(
tag_name = "calcite-action-bar",
layout = "horizontal"
)
render(content)
#> [1] "<calcite-action-bar layout=\"horizontal\"></calcite-action-bar>"nest them freely with each other and with built-in tags:
page <- tags$div(
class = "app",
tag(
tag_name = "calcite-shell",
tag(
tag_name = "calcite-shell-panel",
slot = "panel-start",
tag(
tag_name = "calcite-action-bar",
tag(
tag_name = "calcite-action",
text = "Layers",
icon = "layers"
),
tag(
tag_name = "calcite-action",
text = "Basemaps",
icon = "basemap"
)
)
),
tags$div(id = "map")
)
)
render(page)for self-closing elements, set tag_type = "void":
content <- tag(
tag_name = "my-icon",
name = "home",
tag_type = "void"
)
render(content)
#> [1] "<my-icon name=\"home\" />"-
ambiorix is the perfect example of a web framework where you will find {hypertext} useful:
library(ambiorix) library(hypertext) app <- Ambiorix$new(port = 3000L) app$get("/", function(req, res) { html <- tags$h1("hello, world!") |> render() res$send(html) }) app$get("/about", function(req, res) { html <- tag_list( tags$h1("about us"), tags$p( "minimal ", tags$strong("html construction"), " for R." ) ) |> render() res$send(html) }) app$get("/team", function(req, res) { teammates <- c("you", "me", "other") html <- tags$div( class = "team", tags$p("meet the team:"), tags$ul( lapply(teammates, tags$li) ) ) |> render() res$send(html) }) app$start()
-
shiny already has {htmltools} tags internally, so you do not need {hypertext} in your shiny apps, but in case you do:
library(shiny) library(bslib) library(hypertext) # use `hypertext::tags` explicitly to avoid clashing with `shiny::tags`. ht <- hypertext::tags card <- function(title, body) { ht$div( class = "card mt-3", ht$div( class = "card-header", title ), ht$div( class = "card-body", ht$p( class = "card-text", body ) ) ) } content <- ht$div( class = "container py-4", card("First card", "Some quick example text."), card("Second card", "Another body of text.") ) |> render() ui <- page( theme = bs_theme(version = 5L), # hypertext renders an HTML string, so wrap in shiny::HTML() HTML(content) ) server <- function(input, output, session) {} shinyApp(ui, server)