Skip to content

Ark v6 #3616

@cschroeter

Description

@cschroeter

Status: Draft
Target Release: Q2 2026 (April - Juli)
Last Updated: March 16, 2026

Overview

Ark UI v6 is all about making your life easier as a developer. We're introducing changes that give you more flexibility and consistency when building user interfaces. Here's what's coming.

Composition — asChildrender Prop

The biggest change in v6 is how you compose custom elements with Ark UI components. The asChild prop is being replaced by a render prop, giving you explicit control over what gets rendered.

Currently, asChild merges props into a single child element:

<Popover.Trigger asChild>
  <Button>Open Popover</Button>
</Popover.Trigger>

In v6, you'll use the render prop instead:

<Popover.Trigger render={<Button>Open Popover</Button>} />

The render prop also accepts a function for full control over the rendered output:

<Popover.Trigger
  render={(props) => <Button {...props}>Open Popover</Button>}
/>

This approach is more explicit, avoids the single-child limitation of asChild, and works consistently across all components - including Indicator components where it unlocks state-based rendering (see below).

Indicator

Next up, we're redesigning our Indicator components to match specific states and allow you to set fallbacks. At the moment, they look something like this:

<Menu.ItemIndicator>
    <CheckIcon />
</Menu.ItemIndicator>
<Clipboard.Indicator copied={<CheckIcon />}>
    <ClipboardCopyIcon />
</Clipboard.Indicator>
<Checkbox.Indicator>
    <CheckIcon />
</Checkbox.Indicator>
<Checkbox.Indicator indeterminate>
    <MinusIcon />
</Checkbox.Indicator>

Take a look at Menu.ItemIndicator, for example. How do you know it's meant for the selected state of an item? You don't. Additionally, if the item isn't selected, nothing is rendered, making it difficult for flexbox or grid layouts to work properly.

Altogether, these components not only lack consistency but also flexibility. In the next major version, the Indicator component will expose a render prop that gives you full control over what's rendered based on the component's state:

<Checkbox.Indicator
  render={(props, state) => {
    if (state.status === 'checked') {
      return <CheckIcon {...props} />
    } else if (state.status === 'indeterminate') {
      return <MinusIcon {...props} />
    } else {
      return <SquareIcon {...props} />
    }
  }}
/>

Triggers

This change is inspired by an upcoming HTML feature called commandForElement, which allows buttons to have multiple commands associated with them.

What does this mean for Ark UI?

Take the Carousel component, for example. Currently, we have separate trigger components for each action:

<Carousel.Control>
    <Carousel.PrevTrigger />
    <Carousel.NextTrigger />
    <Carousel.AutoplayTrigger />
    {/* ... */}
</Carousel.Control>

In the next major version, you'll have a single Trigger component that can handle multiple actions via the action prop:

<Carousel.Control>
    <Carousel.Trigger action="prev" />
    <Carousel.Trigger action="next" />
    <Carousel.Trigger action="toggle-autoplay" />
    {/* ... */}
</Carousel.Control>

With this change, we're also enabling more specific actions, such as separate play and pause controls for autoplay:

<Carousel.Control>
    <Carousel.Trigger action="play" />
    <Carousel.Trigger action="pause" />
</Carousel.Control>

Simplified Data Attributes

We're making the DOM output cleaner and more semantic. Instead of generic data attributes with scope and part information, v6 introduces component-specific data attributes that are easier to understand and work with.

Currently, components generate verbose data attributes that include scope and part information:

<Popover.Trigger>Show Popover</Popover.Trigger>

renders

<button id="popover:123:trigger" data-scope="popover" data-part="trigger">Show Popover</button>

In v6, we're simplifying this to use concise, component-specific data attributes:

<Popover.Trigger>Show Popover</Popover.Trigger>

renders

<button id="popover:123:trigger" data-popover-trigger="123">Show Popover</button>

This change is helpful when composing components with the render prop as there will no longer be a potential clash of data-part or data-scope attributes.

Anatomy

import { dialogAnatomy } from '@ark-ui/[framework]' // moved 
import { dialogAnatomy } from '@ark-ui/[framework]/dialog'
import { dialogAnatomy } from '@ark-ui/[framework]/anatomy' // new
import { dialogAnatomy } from '@ark-ui/[framework]/dialog'

Metadata

Metadata

Assignees

Labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions