Custom Elements & Attributes
FSharp.ViewEngine covers all standard HTML elements and attributes, but you may need custom ones for web components or non-standard attributes.
On this page
Overview
FSharp.ViewEngine covers all standard HTML elements and attributes, but you may need custom ones for web components or non-standard attributes.
Trusted Content Boundaries
Text and attribute values are HTML-encoded by default. The following APIs intentionally cross that safety boundary and must only receive trusted, developer-controlled content:
Html.rawandHtml.jsemit their values without encoding.- Inline event-handler helpers such as
_onclickcontain executable JavaScript. HTML encoding preserves valid markup but does not make untrusted JavaScript safe. Html.el,Html.elVoid, and the name passed to_attrare emitted as markup names without validation.
Keep user-controlled data in normal text nodes and attribute values, where it will be encoded. Do not use user input as raw markup, JavaScript, element names, or attribute names.
Custom Elements
el
Use Html.el to create a custom element with children. This is useful for web components:
open FSharp.ViewEngine
open type Html
el "my-component" {
_class "container"
p { "Hello from a web component!" }
}Renders:
<my-component class="container">
<p>Hello from a web component!</p>
</my-component>elVoid
Use Html.elVoid to create a custom self-closing (void) element:
elVoid "my-icon" {
_attr("name", "star")
_attr("size", "24")
}Renders:
<my-icon name="star" size="24">Nested Web Components
Custom elements can be nested just like regular elements:
el "my-card" {
_attr("variant", "outlined")
el "my-card-header" {
h2 { "Card Title" }
}
el "my-card-body" {
p { "Card content goes here." }
}
el "my-card-footer" {
button { _onclick "handleClick()"; "Action" }
}
}Custom Attributes
_attr
Use Html._attr to add any attribute not covered by the built-in helpers.
Key-value attribute
div {
_attr("my-custom-attr", "value")
"Content"
}Renders:
<div my-custom-attr="value">Content</div>Boolean attribute
Pass only the name to render a valueless (boolean) attribute:
div {
_attr "my-flag"
"Content"
}Renders:
<div my-flag>Content</div>Combining with Built-in Attributes
Custom attributes work alongside all built-in attributes:
el "sl-button" {
_attr("variant", "primary")
_attr("size", "large")
_attr "pill"
_onclick "handleClick()"
_class "my-button"
"Click Me"
}Renders:
<sl-button variant="primary" size="large" pill onclick="handleClick()" class="my-button">
Click Me
</sl-button>Extending the Html Type
F# supports type extensions which let you add your own elements and attributes to the Html type. This is useful for project-specific conventions or design system components.
Adding Custom Elements
open FSharp.ViewEngine
type Html with
static member val myCard = TagBuilder("my-card") with get
static member val myIcon = VoidBuilder("my-icon") with getThen use them just like built-in elements:
open type Html
myCard {
_class "shadow-lg"
h2 { "Title" }
p { "Card content" }
}
myIcon { _attr("name", "star") }Adding Custom Attributes
open FSharp.ViewEngine
type Html with
static member inline _theme (v: string) = { Name = "data-theme"; Value = ValueSome v }
static member inline _variant (v: string) = { Name = "variant"; Value = ValueSome v }
static member inline _loading = { Name = "data-loading"; Value = ValueNone }Then use them alongside built-in attributes:
open type Html
div {
_theme "dark"
_variant "outlined"
_loading
"Content"
}Design System Example
You can build a full design system module with reusable elements and attributes:
open FSharp.ViewEngine
type Ds =
static member val alert = TagBuilder("ds-alert") with get
static member val badge = TagBuilder("ds-badge") with get
static member val tooltip = TagBuilder("ds-tooltip") with get
static member inline _severity (v: string) = { Name = "severity"; Value = ValueSome v }
static member inline _placement (v: string) = { Name = "placement"; Value = ValueSome v }
static member inline _dismissible = { Name = "dismissible"; Value = ValueNone }open type Html
open type Ds
alert {
_severity "warning"
_dismissible
"This is a warning message."
}
tooltip {
_placement "top"
button { "Hover me" }
}Shoelace Example
Here's a more complete example using Shoelace web components:
el "sl-dialog" {
_attr("label", "Confirm")
_attr "open"
p { "Are you sure?" }
div {
_slot "footer"
el "sl-button" {
_attr("variant", "primary")
_onclick "this.closest('sl-dialog').hide()"
"Confirm"
}
}
}