agnostic/element
Lustre wouldn’t be much use as a frontend library if it didn’t provide a way to create HTML elements. This module contains the basic functions necessary to construct and manipulate different HTML elements.
It is also possible to use Lustre as a HTML templating library, without
using its runtime features, by passing elements to functions like
to_string_tree or to_document_string.
Types
The Element type is how Lustre represents chunks of HTML. The message type
variable is used to represent the types of messages that can be produced from
events on the element or its children.
Note: Just because an element can produces messages of a given type, doesn’t mean that it will! The
messagetype variable is used to represent the potential for messages to be produced, not a guarantee.
The most basic ways to create elements are:
-
The
elementfunction to construct arbitrary HTML elements. You can also use this render Custom Elements (like those registered as Lustre components). -
The
textfunction to turn a Gleam string into a text node. -
The
nonefunction to render nothing - useful for conditional rendering.
If you have more complex needs, there is one additional way to construct HTML elements:
- The
namespacedfunction to create elements in a specific XML namespace. This is useful for SVG or MathML elements, for example.
For controlling how elements are rendered to strings (void elements,
self-closing tags), use a custom SerializerConfig
with dom.serialize.
Finally, for other niche use cases there are two additional functions:
-
The
fragmentfunction lets you wrap a list ofElements up as a singleElement, making it useful to avoid wrapping elements in a<div/>or other container when you don’t want to. -
The
unsafe_raw_htmlfunction lets you render raw HTML directly into an element. This function is primarily useful in cases where you have pre-sanitised HTML or are working with libraries outside of Lustre that produce plain HTML strings.Lustre will not escape the HTML string provided to this function, meaning inappropriate use can expose your application to XSS attacks. Make sure you never take untrusted user input and pass it to this function!
pub type Element(message) =
vnode.Element(message)
Values
pub fn element(
tag: String,
attributes: List(vattr.Attribute(message)),
children: List(vnode.Element(message)),
) -> vnode.Element(message)
A general function for constructing any kind of element. In most cases you
will want to use the agnostic/element/html instead but this
function is particularly handy when constructing custom elements, either
from your own Lustre components or from external JavaScript libraries.
When rendering elements to strings with to_string, the default
HTML serializer will render standard HTML void elements (like <br>, <img>,
<input>) without closing tags. To customize which elements are treated as
void or self-closing, use dom.serialize
with a custom SerializerConfig.
pub fn fragment(
children: List(vnode.Element(message)),
) -> vnode.Element(message)
A function for constructing a wrapper element with no tag name. This is
useful for wrapping a list of elements together without adding an extra
<div> or other container element, or returning multiple elements in places
where only one Element is expected.
pub fn map(
element: vnode.Element(a),
f: fn(a) -> b,
) -> vnode.Element(b)
The Element type is parameterised by the type of messages it can produce
from events. Sometimes you might end up with a fragment of HTML from another
library or module that produces a different type of message: this function lets
you map the messages produced from one type to another.
Think of it like list.map or result.map but for HTML events!
pub fn memo(
dependencies: List(ref.Ref),
view: fn() -> vnode.Element(message),
) -> vnode.Element(message)
A function for creating “memoised” or “lazy” elements. Lustre will use the dependencies list to skip calling the provided view function if all of the dependencies are reference equal to their previous values.
memo can be used to optimise performance-critical parts of your application,
for example in cases where many instances of the same element are rendered but
only one may change at a time, or cases where a part of your view may update
very frequently but other parts remain largely static. When Lustre can tell
that the dependencies haven’t changed, almost all the work typically done to
update the DOM can be skipped.
In many cases memo will not be necessary, so think twice before considering
its use! Lustre is designed to handle rerenders and large vdom trees efficiently,
so in most cases the naive approach of re-rendering everything will be perfectly
fine.
Note: reference equality is not the same as Gleam’s normal equality. Two custom types with the same values are not reference equal unless they are the exact same instance in memory! Because of this, it’s important to avoid list literals or constructing custom types in the dependencies list.
Note: memoisation comes with its own trade-offs and can cause performance regressions in two ways. First, every use of
memoincreases your application’s memory usage slightly, as Lustre needs to keep dependencies around to compare them on subsequent renders. Second, if dependencies change regularly, the overhead of comparing dependencies and managing memoisation may be more than the naive cost of re-rendering the element each time.
pub fn namespaced(
namespace: String,
tag: String,
attributes: List(vattr.Attribute(message)),
children: List(vnode.Element(message)),
) -> vnode.Element(message)
A function for constructing elements in a specific XML namespace. This can be used to construct SVG or MathML elements, for example.
pub fn none() -> vnode.Element(message)
A function for rendering nothing. This is mostly useful for conditional rendering, where you might want to render something only if a certain condition is met.
pub fn ref(value: a) -> ref.Ref
Create a Ref dependency value used for memo elements.
Lustre uses reference equality to compare dependencies. On JavaScript, values
are compared using same-value-zero
semantics. This means Lustre will treat +0 and -0 as equal, and any errant
NaN values (which are not typically producible in Gleam code) as equal. On
Erlang, there is no difference between reference equality and value equality,
so all values are compared using normal equality semantics.
pub fn text(content: String) -> vnode.Element(message)
A function for turning a Gleam string into a text node. Gleam doesn’t have
union types like some other languages you may be familiar with, like TypeScript.
Instead, we need a way to take a String and turn it into an Element somehow:
this function is exactly that!
pub fn to_document_string(el: vnode.Element(message)) -> String
Deprecated: Use agnostic/platform/dom.to_document_string instead
DEPRECATED: Use agnostic/platform/dom.to_document_string instead.
This shim exists only for lustre_dev_tools compatibility.
pub fn to_readable_string(el: vnode.Element(message)) -> String
Deprecated: Use agnostic/platform/dom.to_readable_string instead
DEPRECATED: Use agnostic/platform/dom.to_readable_string instead.
This shim exists only for lustre_dev_tools compatibility.
pub fn to_string(el: vnode.Element(message)) -> String
Deprecated: Use agnostic/platform/dom.to_string instead
DEPRECATED: Use agnostic/platform/dom.to_string instead.
This shim exists only for lustre_dev_tools compatibility.
pub fn unsafe_raw(
key key: String,
content content: a,
compare compare: option.Option(fn(a, a) -> Bool),
) -> vnode.Element(message)
A function for inserting a raw platform node directly into the virtual DOM
tree without a wrapper element. The content is treated as an opaque platform
node and inserted directly using the platform’s insert_before method.
For inserting raw HTML strings, use html.unsafe_raw
instead, which wraps the content in a container element (required for innerHTML).
The optional compare parameter allows custom equality checking for the content.
When provided, the diff algorithm uses this comparator instead of == to
determine if the content has changed.
pub fn unsafe_raw_content(
key key: String,
namespace namespace: String,
tag tag: String,
attributes attributes: List(vattr.Attribute(message)),
content content: a,
compare compare: option.Option(fn(a, a) -> Bool),
) -> vnode.Element(message)
A function for constructing a wrapper element with platform-specific raw
content. This is the generic version of unsafe_raw_html that accepts any
content type, allowing platforms to inject their own node types.
For DOM platforms, use unsafe_raw_html with a String instead.
For other platforms (like OpenTUI), this allows inserting raw platform nodes.
The platform’s set_raw_content function receives this content and decides
how to handle it.
The optional compare parameter allows custom equality checking for the content.
When provided, the diff algorithm uses this comparator instead of == to
determine if the content has changed. This is useful for content types like
closures where reference equality isn’t appropriate.
pub fn unsafe_raw_html(
namespace: String,
tag: String,
attributes: List(vattr.Attribute(message)),
content: String,
) -> vnode.Element(message)
Deprecated: Use element.unsafe_raw_content instead
DEPRECATED: Use element.unsafe_raw_content instead.
This shim exists only for lustre_dev_tools compatibility.