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 message type variable is used to represent the potential for messages to be produced, not a guarantee.

The most basic ways to create elements are:

  • The element function to construct arbitrary HTML elements. You can also use this render Custom Elements (like those registered as Lustre components).

  • The text function to turn a Gleam string into a text node.

  • The none function to render nothing - useful for conditional rendering.

If you have more complex needs, there is one additional way to construct HTML elements:

  • The namespaced function 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 fragment function lets you wrap a list of Elements up as a single Element, making it useful to avoid wrapping elements in a <div/> or other container when you don’t want to.

  • The unsafe_raw_html function 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)

Refs are used as dependencies for memoised elements created using the memo function. They wrap arbitrary Gleam values and are used by Lustre to perform reference equality checks to determine whether a memoised element needs to be re-rendered or not.

pub type Ref =
  ref.Ref

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 memo increases 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.

Search Document