Guides
Composition

Composition

Learn how to compose default components with custom elements

The asChild Prop

In Ark Ripple, asChild lets you integrate custom components, ensuring consistent styling and behavior while promoting flexibility and reusability. All Ark components that render a DOM element accept asChild.

Here's an example using asChild to integrate a custom Button component within a Popover:

import { Popover } from 'ark-ripple/popover';

export component AsChild() {
  <Popover.Root>
    <Popover.Trigger
      asChild={component(propsFn) {
        <button {...propsFn()}>{'Open Popover'}</button>
      }}
    />
    <Popover.Positioner>
      <Popover.Content>{'Content'}</Popover.Content>
    </Popover.Positioner>
  </Popover.Root>
}

In this example, asChild allows the Button to be used as the trigger for the Popover, inheriting its behaviors from Popover.Trigger.

The Ark Factory

You can use the ark factory to create your own elements that work just like Ark Ripple components.

import { ark } from 'ark-ripple/factory';

export component Factory() {
  <ark.span
    asChild={component(propsFn) {
      <a {...propsFn()} href="#">{'Ark UI'}</a>
    }}
  />
}

This will produce the following HTML:

<span id="child" class="parent child" style="background: red; color: blue;">Ark UI</span>

ID Composition

When composing components that need to work together, share IDs between them using the ids prop for proper accessibility and interaction.

import { Avatar } from 'ark-ripple/avatar'
import { Tooltip } from 'ark-ripple/tooltip'

export component TooltipWithAvatar() {
  const id = ':ark-avatar'

  <Tooltip.Root ids={{ trigger: id }}>
    <Tooltip.Trigger>
      component asChild({ propsFn }){
        <Avatar.Root {...propsFn({ ids: { root: id } })}>
          <Avatar.Image src="https://bit.ly/sage-adebayo" />
          <Avatar.Fallback>{"SA"}</Avatar.Fallback>
        </Avatar.Root>
      }
    </Tooltip.Trigger>
    <Tooltip.Positioner>
      <Tooltip.Content>{"Abraham Aremu is online"}</Tooltip.Content>
    </Tooltip.Positioner>
  </Tooltip.Root>
}

Both components share the same id through their ids props, creating proper accessibility bindings, aria-* attributes and interaction behavior.

Limitations

Certain components, such as Checkbox.Root or RadioGroup.Item, have specific requirements for their child elements. For instance, they may require a label element as a child. If you change the underlying element type, ensure it remains accessible and functional.