Polymorphism
Render components as alternative HTML elements or custom SolidJS components with full type safety via the as prop.
Moraine components and parts that expose as can render a different element or Solid component while preserving their styles and typed props. State-only roots and native form controls such as Input do not expose as. When changing an element, ensure the result still provides an accessible name and appropriate semantics.
Rendering as an HTML Element (as="tag")#
By default, interactive components render sensible semantic elements (such as <button> for Button or <div> for Card). Use as="tag" to change the underlying HTML element:
TypeScript Attribute Narrowing#
When you specify as="tag", Moraine automatically narrows the allowed props, event types, and ref callback parameter to match that specific HTML tag:
Rendering as a Solid Component (as={Component})#
You can pass another SolidJS component to as. Moraine preserves the target component’s custom props, providing full IDE autocompletion, type validation, and direct props forwarding:
Custom components receive ordinary event props and can wrap, conditionally forward, or invoke them. Forward the root ref to let Moraine resolve native element semantics and manage focus. Ordinary event callbacks remain available even when the target does not forward its ref.
Overlay Triggers and Composition#
Overlay triggers (Dialog.Trigger, Popover.Trigger, Sheet.Trigger, DropdownMenu.Trigger, ContextMenu.Trigger) are designed to be composed with as={Button}:
- State Injection: The trigger automatically injects
aria-haspopup,aria-expanded, and required click or pointer handlers into the rendered element. - Event Merging: If you provide your own
onClickhandler on<Dialog.Trigger as={Button} onClick={...}>, Moraine calls your handler first. If you callevent.preventDefault(), the disclosure action is canceled. - Ref Forwarding: Refs point directly to the underlying DOM button, allowing easy focus management.
Modal, Dialog, Sheet, Popover, Tooltip, and DropdownMenu triggers handle composed clicks after a custom root’s own click handler. For these triggers, the custom target does not receive the composed onClick prop; its own DOM click handler can call preventDefault() to cancel activation. Other polymorphic roots, including BaseSelect and ContextMenu triggers, forward onClick normally. Keyboard handlers run through the target’s forwarding path in both the main document and iframe documents.
Global Type Configuration#
If you want to restrict as autocompletion to standard HTML element tags (excluding SVG and MathML elements), enable simpleHtmlTags via module augmentation. See the TypeScript Guide for details.