Component State
Learn how to manage component state using Context and Provider components.
Context Components
Context components expose state and functions to child components. In this example, Avatar.Fallback renders based on
loaded state.
import { Avatar } from '@ark-ui/react/avatar'
import styles from 'styles/avatar.module.css'
export const Context = () => (
<Avatar.Root className={styles.Root}>
<Avatar.Context>
{(avatar) => <Avatar.Fallback className={styles.Fallback}>{avatar.loaded ? 'PA' : 'Loading'}</Avatar.Fallback>}
</Avatar.Context>
<Avatar.Image className={styles.Image} src="https://i.pravatar.cc/300" alt="avatar" />
</Avatar.Root>
)
import { Avatar } from '@ark-ui/solid/avatar'
import styles from 'styles/avatar.module.css'
export const Context = () => (
<Avatar.Root class={styles.Root}>
<Avatar.Context>
{(avatar) => <Avatar.Fallback class={styles.Fallback}>{avatar().loaded ? 'PA' : 'Loading'}</Avatar.Fallback>}
</Avatar.Context>
<Avatar.Image class={styles.Image} src="https://i.pravatar.cc/300" alt="avatar" />
</Avatar.Root>
)
<script setup lang="ts">
import { Avatar } from '@ark-ui/vue/avatar'
import styles from 'styles/avatar.module.css'
</script>
<template>
<Avatar.Root :class="styles.Root">
<Avatar.Context v-slot="avatar">
<Avatar.Fallback :class="styles.Fallback">
{{ avatar.loaded ? 'PA' : 'Loading' }}
</Avatar.Fallback>
</Avatar.Context>
<Avatar.Image :class="styles.Image" src="https://i.pravatar.cc/300" alt="avatar" />
</Avatar.Root>
</template>
<script lang="ts">
import { Avatar } from '@ark-ui/svelte/avatar'
import styles from 'styles/avatar.module.css'
</script>
<Avatar.Root class={styles.Root}>
<Avatar.Context>
{#snippet api(avatar)}
<Avatar.Fallback class={styles.Fallback}>
{#if avatar().loaded}
<p>PA</p>
{:else}
<p>Loading</p>
{/if}
</Avatar.Fallback>
{/snippet}
</Avatar.Context>
<Avatar.Image class={styles.Image} src="https://i.pravatar.cc/3000?u=b" alt="avatar" />
</Avatar.Root>
Good to know (RSC): Due to the usage of render prop, you might need to add the
'use client'directive at the top of your file when using React Server Components.
Provider Components
Provider components can help coordinate state and behavior between multiple components, enabling interactions that
aren't possible with Context components alone. They are used alongside component hooks.
import { Accordion, useAccordion } from '@ark-ui/react/accordion'
import { ChevronDownIcon } from 'lucide-react'
import styles from 'styles/accordion.module.css'
export const RootProvider = () => {
const accordion = useAccordion({
multiple: true,
defaultValue: ['ark-ui'],
})
return (
<>
<button onClick={() => accordion.setValue(['maintainers'])}>Set to Maintainers</button>
<Accordion.RootProvider className={styles.Root} value={accordion}>
{items.map((item) => (
<Accordion.Item className={styles.Item} key={item.value} value={item.value}>
<Accordion.ItemTrigger className={styles.ItemTrigger}>
{item.title}
<Accordion.ItemIndicator className={styles.ItemIndicator}>
<ChevronDownIcon />
</Accordion.ItemIndicator>
</Accordion.ItemTrigger>
<Accordion.ItemContent className={styles.ItemContent}>
<div className={styles.ItemBody}>{item.content}</div>
</Accordion.ItemContent>
</Accordion.Item>
))}
</Accordion.RootProvider>
</>
)
}
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.',
},
]
import { Accordion, useAccordion } from '@ark-ui/solid/accordion'
import { ChevronDownIcon } from 'lucide-solid'
import { Index } from 'solid-js'
import styles from 'styles/accordion.module.css'
export const RootProvider = () => {
const accordion = useAccordion({
multiple: true,
defaultValue: ['ark-ui'],
})
return (
<>
<button onClick={() => accordion().setValue(['maintainers'])}>Set to Maintainers</button>
<Accordion.RootProvider class={styles.Root} value={accordion}>
<Index each={items}>
{(item) => (
<Accordion.Item class={styles.Item} value={item().value}>
<Accordion.ItemTrigger class={styles.ItemTrigger}>
{item().title}
<Accordion.ItemIndicator class={styles.ItemIndicator}>
<ChevronDownIcon />
</Accordion.ItemIndicator>
</Accordion.ItemTrigger>
<Accordion.ItemContent class={styles.ItemContent}>
<div class={styles.ItemBody}>{item().content}</div>
</Accordion.ItemContent>
</Accordion.Item>
)}
</Index>
</Accordion.RootProvider>
</>
)
}
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.',
},
]
<script setup lang="ts">
import { Accordion, useAccordion } from '@ark-ui/vue/accordion'
import { ChevronDownIcon } from 'lucide-vue-next'
import styles from 'styles/accordion.module.css'
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.',
},
]
const accordion = useAccordion({
multiple: true,
defaultValue: ['ark-ui'],
})
</script>
<template>
<button @click="accordion.setValue(['maintainers'])">Set to Maintainers</button>
<Accordion.RootProvider :class="styles.Root" :value="accordion">
<Accordion.Item v-for="item in items" :key="item.value" :class="styles.Item" :value="item.value">
<Accordion.ItemTrigger :class="styles.ItemTrigger">
{{ item.title }}
<Accordion.ItemIndicator :class="styles.ItemIndicator">
<ChevronDownIcon />
</Accordion.ItemIndicator>
</Accordion.ItemTrigger>
<Accordion.ItemContent :class="styles.ItemContent">
<div :class="styles.ItemBody">{{ item.content }}</div>
</Accordion.ItemContent>
</Accordion.Item>
</Accordion.RootProvider>
</template>
<script lang="ts">
import { Accordion, useAccordion } from '@ark-ui/svelte/accordion'
import { ChevronDownIcon } from 'lucide-svelte'
import styles from 'styles/accordion.module.css'
const id = $props.id()
const accordion = useAccordion({
id,
defaultValue: ['ark-ui'],
})
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.',
},
]
</script>
<div>
<button onclick={() => accordion().setValue(['maintainers'])}>Set to Maintainers</button>
<Accordion.RootProvider class={styles.Root} value={accordion}>
{#each items as item (item.value)}
<Accordion.Item class={styles.Item} value={item.value}>
<Accordion.ItemTrigger class={styles.ItemTrigger}>
{item.title}
<Accordion.ItemIndicator class={styles.ItemIndicator}>
<ChevronDownIcon />
</Accordion.ItemIndicator>
</Accordion.ItemTrigger>
<Accordion.ItemContent class={styles.ItemContent}>
<div class={styles.ItemBody}>{item.content}</div>
</Accordion.ItemContent>
</Accordion.Item>
{/each}
</Accordion.RootProvider>
</div>
When using the
RootProvidercomponent, you don't need to use theRootcomponent.
See more in Examples.