Essence UI Primitives compose through concrete HEEx parts rather than cloning props onto alternate child elements. This approach fits LiveView and keeps markup explicit.
What Essence does instead
Primitive parts render a concrete element (button, div, …). You compose by:
- Wrapping parts in your own function components (most common)
- Passing attributes (
class,aria-*,phx-*) throughrest/ explicit attrs - Using
Slotwhen you need a polymorphic tag helper - Using Themes
as_childon Themes components (not on Primitives parts)
Essence does not support asChild on primitive parts; use wrapping or Themes as_child instead.
Wrapping a part
Encode your design-system button once, then use it as the dialog trigger content (or wrap Dialog.trigger in a styled function component):
<Dialog.trigger id="edit-trigger" class="DemoButton violet">
Edit profile
</Dialog.trigger>
Or wrap the primitive:
def my_dialog_trigger(assigns) do
~H"""
<Dialog.trigger id={@id} class={["Button", @class]} {@rest}>
{render_slot(@inner_block)}
</Dialog.trigger>
"""
end
Changing the element type with Slot
Slot renders a chosen HTML tag with merged attributes. Use it for polymorphic leaves—not as a drop-in for asChild on compound triggers.
<Slot.slot as="a" href="/docs" class="link">
Docs
</Slot.slot>
If you change the underlying element of a trigger-like control, you are responsible for keeping it focusable and operable with pointer and keyboard.
Themes as_child
Themes components (for example Button, Card) support as_child to merge Themes styles onto a child element. That API lives in Themes, not Primitives. Prefer Themes when you want that composition model with styled defaults.