html element construction for R.
hypertext provides a deterministic, framework-agnostic
DSL for building html nodes and rendering them to a string.
install the stable version from CRAN:
install.packages("hypertext")or get the development version from github:
devtools::install_github("sigflux/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"
)
)
)
)
page
#> <!DOCTYPE html>
#> <html>
#> <head>
#> <title>hypertext</title>
#> </head>
#> <body>
#> <h1>Hello</h1>
#> <p class="lead mb-2">Server-side HTML.</p>
#> <input type="text" placeholder="enter your nickname" />
#> <button>Click</button>
#> </body>
#> </html>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"
x
#> <p class="lead">hello</p>
# 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:
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.
header <- tag_list(
tags$h1("hello"),
tags$p(
class = "lead",
"welcome aboard."
)
)
# `header` is a "hypertext.tag.list" object:
class(header)
#> [1] "hypertext.tag.list" "list"
header
#> <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.
tags$div(
raw_html("<svg viewBox='0 0 100 100'><circle cx='50' cy='50' r='40'/></svg>")
)
#> <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.content <- tag(
tag_name = "calcite-action-bar",
layout = "horizontal"
)
content
#> <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")
)
)
page
#> <div class="app">
#> <calcite-shell>
#> <calcite-shell-panel slot="panel-start">
#> <calcite-action-bar>
#> <calcite-action text="Layers" icon="layers"></calcite-action>
#> <calcite-action text="Basemaps" icon="basemap"></calcite-action>
#> </calcite-action-bar>
#> </calcite-shell-panel>
#> <div id="map"></div>
#> </calcite-shell>
#> </div>for self-closing elements, set tag_type = "void":
content <- tag(
tag_name = "my-icon",
name = "home",
tag_type = "void"
)
content
#> <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)