# Composition

URL: https://ark-ui.com/docs/guides/composition
Source: https://raw.githubusercontent.com/chakra-ui/ark/refs/heads/main/website/src/content/pages/guides/composition.mdx

Learn how to compose default components with custom elements

---

## The asChild Prop

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

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

**Example: as-child**

#### React

```tsx
import { Popover } from '@ark-ui/react/popover'

export const AsChild = () => (
  <Popover.Root>
    <Popover.Trigger asChild>
      <button>Open</button>
    </Popover.Trigger>
  </Popover.Root>
)

```

#### Solid

```tsx
import { Popover } from '@ark-ui/solid/popover'

export const Basic = () => (
  <Popover.Root>
    <Popover.Trigger asChild={(props) => <button {...props()} />}>Open</Popover.Trigger>
  </Popover.Root>
)

```

#### Vue

```vue
<script setup lang="ts">
import { Popover } from '@ark-ui/vue/popover'
</script>

<template>
  <Popover.Root>
    <Popover.Trigger asChild>
      <button>Open</button>
    </Popover.Trigger>
  </Popover.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Popover } from '@ark-ui/svelte/popover'
</script>

<Popover.Root>
  <Popover.Trigger>
    {#snippet asChild(props)}
      <button {...props()}>Open</button>
    {/snippet}
  </Popover.Trigger>
</Popover.Root>

```

In this example, the `asChild` prop 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 UI components.

**Example: factory**

#### React

```tsx
import { ark } from '@ark-ui/react/factory'

export const ArkFactory = () => (
  <ark.div id="parent" className="parent" style={{ background: 'red' }} asChild>
    <ark.span id="child" className="child" style={{ color: 'blue' }}>
      Ark UI
    </ark.span>
  </ark.div>
)

```

#### Solid

```tsx
import { ark } from '@ark-ui/solid/factory'

export const ArkFactory = () => (
  <ark.div
    id="parent"
    class="parent"
    style={{ background: 'red' }}
    asChild={(props) => <ark.span {...props({ id: 'child', class: 'child', style: { color: 'blue' } })} />}
  >
    Ark UI
  </ark.div>
)

```

#### Vue

```vue
<script setup lang="ts">
import { ark } from '@ark-ui/vue/factory'
</script>

<template>
  <ark.div id="parent" className="parent" :style="{ background: 'red' }" :as-child="true">
    <ark.span id="child" className="child" :style="{ color: 'blue' }">Ark UI</ark.span>
  </ark.div>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Ark } from '@ark-ui/svelte/factory'
</script>

<Ark as="div" id="parent" class="parent" style="background: red;">
  {#snippet asChild(props)}
    <Ark as="span" {...props({ id: 'child', class: 'child', style: 'color: blue' })}>Ark UI</Ark>
  {/snippet}
</Ark>

```

This will produce the following HTML:

```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.

```tsx
import { Avatar } from '@ark-ui/react/avatar'
import { Tooltip } from '@ark-ui/react/tooltip'
import { useId } from 'react'

export const TooltipWithAvatar = () => {
  const id = useId()

  return (
    <Tooltip.Root ids={{ trigger: id }}>
      <Tooltip.Trigger asChild>
        <Avatar.Root ids={{ root: id }}>
          <Avatar.Image src="https://bit.ly/sage-adebayo" />
          <Avatar.Fallback>SA</Avatar.Fallback>
        </Avatar.Root>
      </Tooltip.Trigger>
      <Tooltip.Positioner>
        <Tooltip.Content>Segun Adebayo 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

When using the `asChild` prop, ensure you pass only a single child element. Passing multiple children may cause
rendering issues.

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.