Library
Module
Module type
Parameter
Class
Class type
Constructing HTML. Detailed explanation in https://github.com/yawaramin/dream-html.
Let's adapt the example from the Dream home page:
let hello who =
let open Dream_html in
let open HTML in
html [] [body [] [h1 [] [txt "Hello, %s!" who]]]
let () =
Dream.run
@@ Dream.logger
@@ Dream.router [Dream.get "/" (fun _ -> Dream_html.respond (hello "world"))]
More examples shown below.
These are the types of the final values which get rendered.
val to_string : node -> string
val pp : Stdlib.Format.formatter -> node -> unit
val respond :
?status:[< Dream.status ] ->
?code:int ->
?headers:(string * string) list ->
node ->
Dream.response Dream.promise
val set_body : Dream.response -> node -> unit
Type-safe wrapper for Dream.set_body
. Sets the body to the given node
and sets the Content-Type
header to text/html
.
val write : Dream.stream -> node -> unit Dream.promise
Type-safe wrapper for Dream.write
.
type 'a to_attr = 'a -> attr
Attributes can be created from typed values.
type 'a string_attr = ('a, unit, string, attr) Stdlib.format4 -> 'a
Special handling for string-value attributes so they can use format strings i.e. string interpolation.
A 'void element': https://developer.mozilla.org/en-US/docs/Glossary/Void_element with no children.
Tags which can have attributes but can contain only text. The text can be formatted.
val attr : string -> attr
attr name
is a new attribute which does not carry any payload. E.g.
let required = attr "required"
val string_attr : string -> ?raw:bool -> _ string_attr
string_attr name fmt
is a new string-valued attribute which allows formatting i.e. string interpolation of the value. Note, the fmt
argument is required due to the value restriction.
val uri_attr : string -> _ string_attr
Convenience for attributes whose values should be URIs. Takes care of URI- encoding.
a [href "/blog?tags=iamsafe\"></a><script>alert('Pwned')</script>"] [txt "Tags: tag1 | tag2"]
Output:
<a href="/blog?tags=iamsafe%22%3E%3C/a%3E%3Cscript%3Ealert('Pwned')%3C/script%3E">Tags: tag1 | tag2</a>
val bool_attr : string -> bool to_attr
val float_attr : string -> float to_attr
val int_attr : string -> int to_attr
val std_tag : string -> std_tag
val void_tag : string -> void_tag
val text_tag : string -> ?raw:bool -> _ text_tag
Build a tag which can contain only text.
val txt : ?raw:bool -> ('a, unit, string, node) Stdlib.format4 -> 'a
A text node inside the DOM e.g. the 'hi' in <b>hi</b>
. Allows string interpolation using the same formatting features as Printf.sprintf
:
b [] [txt "Hello, %s!" name]
Or without interpolation:
b [] [txt "Bold of you."]
HTML-escapes the text value using Dream.html_escape
. You can use the ~raw
param to bypass escaping:
let user_input = "<script>alert('I like HTML injection')</script>" in
txt ~raw:true "%s" user_input
val comment : string -> node
A comment that will be embedded in the rendered HTML, i.e. <!-- comment -->
. The text is HTML-escaped.
val csrf_tag : Dream.request -> node
Convenience to add a CSRF token generated by Dream into your form. Type-safe wrapper for Dream.csrf_tag
.
form
[action "/foo"]
[csrf_tag req; input [name "bar"]; input [type_ "submit"]]
val (.@[]) : node -> string -> string
Get the value of an existing attribute.
let toast = p [id "toast"] [txt "OK."]
let toast_id = toast.@["id"]
val is_null : node -> bool
Get whether a node is null (empty) or not. Useful for conditional rendering of UIs when you are passed in a node and you don't know if it's empty or not.
val is_null_ : attr -> bool
Get whether an attribute is null (empty) or not.
module HTML : sig ... end
All standard HTML attributes and tags. Some attributes and tags have the same name, e.g. style
. To disambiguate them, attributes have a _
(underscore) suffix.
module SVG : sig ... end
module Hx : sig ... end
htmx support https://htmx.org/reference/
In this section we show an extended example of interoperability of dream-html with other formats, e.g. the Markdown format defined by the omd
library:
open Dream_html
open HTML
let omd_attr (k, opt_v) =
string_attr k "%s" @@ Option.fold ~none:"" ~some:Fun.id opt_v
let rec omd_node elements = elements |> List.map element_node |> null
and item omd = li [] [omd_node omd]
and element_node = function
| Omd.H1 omd -> h1 [] [omd_node omd]
| H2 omd -> h2 [] [omd_node omd]
| H3 omd -> h3 [] [omd_node omd]
| H4 omd -> h4 [] [omd_node omd]
| H5 omd -> h5 [] [omd_node omd]
| H6 omd -> h6 [] [omd_node omd]
| Paragraph omd -> p [] [omd_node omd]
| Text str -> txt "%s" str
| Emph omd -> em [] [omd_node omd]
| Bold omd -> b [] [omd_node omd]
| Ul omds | Ulp omds -> ul [] @@ List.map item omds
| Ol omds | Olp omds -> ol [] @@ List.map item omds
| Code (cls, str) -> code [class_ "%s" cls] [txt "%s" str]
| Code_block (cls, str) ->
pre [class_ "%s" cls] [code [class_ "%s" cls] [txt "%s" str]]
| Br -> br []
| Hr -> hr []
| NL -> txt "\n"
| Url (hre, omd, titl) ->
a
[ href "%s" hre;
target "_blank";
class_ "after:content-['_↗️']";
title "%s" titl ]
[omd_node omd]
| Html ("details", attrs, omd) ->
details (List.map omd_attr attrs) [omd_node omd]
| Html ("summary", attrs, omd) ->
summary (List.map omd_attr attrs) [omd_node omd]
| Html_block ("details", attrs, omd) ->
details (List.map omd_attr attrs) [omd_node omd]
| Html_block ("summary", attrs, omd) ->
summary (List.map omd_attr attrs) [omd_node omd]
| Ref (_, _, _, _)
| Img_ref (_, _, _, _)
| Html (_, _, _)
| Html_block (_, _, _)
| X _ -> null []
| Html_comment str -> comment str
| Raw str -> txt ~raw:true "%s" str
| Raw_block str -> pre [] [txt ~raw:true "%s" str]
| Blockquote omd -> blockquote [] [omd_node omd]
| Img (al, sr, titl) -> img [alt "%s" al; src "%s" sr; title "%s" titl]
The entrypoint of the functionality is the omd_node
function, which converts from the Omd.t
value which can be obtained by, for example, parsing Markdown with the Omd.of_string
function, to a type-safe node
value.
In the element_node
function we traverse the Markdown structure and convert it into specific nodes–tags, comments, etc.. Text is escaped as appropriate and some specific conversions are handled specially:
<details>
and <summary>
HTML tags, but no others.This is just an example, but it shows the idea that we can take various formats and interop with them using type-sdfe HTML generated by dream-html.