Guides
Component state

Component State

Learn how to manage component state using Context and Provider components.

Need to access a component's state? You have three options:

ApproachWhen to use it
Component.ContextQuick inline access via named children
use*Context hooksBuild custom child components that read state
useComponent + RootProviderControl the component from outside

Context Components

Use Component.Context to access state inline via named children. Here, Avatar.Fallback reads the loaded state to show different content:

import { Avatar } from 'ark-ripple/avatar';
import styles from './index.module.css';

export component Context() {
  <Avatar.Root class={styles.Root}>
    <Avatar.Context>
      component children({ context }) {
        <Avatar.Fallback class={styles.Fallback}>
          {@context.loaded ? 'PA' : 'Loading'}
        </Avatar.Fallback>
      }
    </Avatar.Context>
    <Avatar.Image class={styles.Image} src="https://i.pravatar.cc/300?u=a" alt="avatar" />
  </Avatar.Root>
}

Context Hooks

Every component exports a use*Context hook (like useDialogContext or useMenuContext). Call it from any child component to access state and methods—no render props needed.

import { Dialog, useDialogContext } from 'ark-ripple/dialog'

component CustomCloseButton() {
  const dialog = useDialogContext()
  
  <button onClick={() => dialog.setOpen(false)}>
    {"Close "} {dialog.open ? 'open' : 'closed'}
  </button>
}

export component Demo() {
  <Dialog.Root>
    <Dialog.Trigger>Open</Dialog.Trigger>
    <Dialog.Content>
      <CustomCloseButton />
    </Dialog.Content>
  </Dialog.Root>
}

The hook returns the same API as Component.Context, just without the nesting.

Provider Components

Need to control a component from outside its tree? Use a useComponent hook (like useDialog) with RootProvider.

When you use RootProvider, skip the Root component—you don't need both.

import { Accordion } from 'ark-ripple/accordion';
import { useAccordion } from 'ark-ripple/accordion';
import { ChevronDown } from 'lucide-ripple';
import styles from './index.module.css';

export component RootProvider() {
  const accordion = useAccordion({ multiple: true, defaultValue: ['ark-ui'] });

  <div class="stack">
    <output>
      {'Value: '}
      {JSON.stringify(@accordion.value)}
    </output>

    <Accordion.RootProvider class={styles.Root} value={accordion}>
      for (const item of items; key item.value) {
        <Accordion.Item class={styles.Item} value={item.value}>
          <Accordion.ItemTrigger class={styles.ItemTrigger}>
            {item.title}
            <Accordion.ItemIndicator class={styles.ItemIndicator}>
              <ChevronDown />
            </Accordion.ItemIndicator>
          </Accordion.ItemTrigger>
          <Accordion.ItemContent class={styles.ItemContent}>
            <div class={styles.ItemBody}>{item.content}</div>
          </Accordion.ItemContent>
        </Accordion.Item>
      }
    </Accordion.RootProvider>
  </div>
}

const items = [
  {
    value: 'ark-ui',
    title: 'What is Ark UI?',
    content: 'A headless component library for building accessible web apps.',
  },
  {
    value: 'getting-started',
    title: 'How to get started?',
    content: 'Install the package and import the components you need.',
  },
  {
    value: 'maintainers',
    title: 'Who maintains this project?',
    content: 'Ark UI is maintained by the Chakra UI team.',
  },
];

Choosing the Right Approach

  • Component.Context — Quick inline access for conditional rendering
  • use*Context hooks — Build reusable child components that need parent state
  • useComponent + RootProvider — Trigger actions from outside (like opening a dialog from a menu item)