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'
export const Context = () => (
<Avatar.Root>
<Avatar.Context>{(avatar) => <Avatar.Fallback>{avatar.loaded ? 'PA' : 'Loading'}</Avatar.Fallback>}</Avatar.Context>
<Avatar.Image src="https://i.pravatar.cc/300" alt="avatar" />
</Avatar.Root>
)
import { Avatar } from '@ark-ui/solid/avatar'
export const Context = () => (
<Avatar.Root>
<Avatar.Context>
{(avatar) => <Avatar.Fallback>{avatar().loaded ? 'PA' : 'Loading'}</Avatar.Fallback>}
</Avatar.Context>
<Avatar.Image src="https://i.pravatar.cc/300" alt="avatar" />
</Avatar.Root>
)
<script setup lang="ts">
import { Avatar } from '@ark-ui/vue/avatar'
</script>
<template>
<Avatar.Root>
<Avatar.Context v-slot="avatar">
<Avatar.Fallback>
{{ avatar.loaded ? 'PA' : 'Loading' }}
</Avatar.Fallback>
</Avatar.Context>
<Avatar.Image src="https://i.pravatar.cc/300" alt="avatar" />
</Avatar.Root>
</template>
<script lang="ts">
import { Avatar } from '@ark-ui/svelte/avatar'
</script>
<Avatar.Root>
<Avatar.Context>
{#snippet api(avatar)}
<Avatar.Fallback>
{#if avatar().loaded}
<p>PA</p>
{:else}
<p>Loading</p>
{/if}
</Avatar.Fallback>
{/snippet}
</Avatar.Context>
<Avatar.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'
export const RootProvider = () => {
const accordion = useAccordion({
multiple: true,
defaultValue: ['React'],
})
return (
<>
<button onClick={() => accordion.setValue(['Vue'])}>Set to Vue</button>
<Accordion.RootProvider value={accordion}>
{['React', 'Solid', 'Vue', 'Svelte'].map((item) => (
<Accordion.Item key={item} value={item}>
<Accordion.ItemTrigger>
What is {item}?
<Accordion.ItemIndicator>
<ChevronDownIcon />
</Accordion.ItemIndicator>
</Accordion.ItemTrigger>
<Accordion.ItemContent>{item} is a JavaScript library for building user interfaces.</Accordion.ItemContent>
</Accordion.Item>
))}
</Accordion.RootProvider>
</>
)
}
import { Accordion, useAccordion } from '@ark-ui/solid/accordion'
import { ChevronDownIcon } from 'lucide-solid'
import { Index } from 'solid-js'
export const RootProvider = () => {
const accordion = useAccordion({
multiple: true,
defaultValue: ['React'],
})
return (
<>
<button onClick={() => accordion().setValue(['Vue'])}>Set to Vue</button>
<Accordion.RootProvider value={accordion}>
<Index each={['React', 'Solid', 'Vue', 'Svelte']}>
{(item) => (
<Accordion.Item value={item()}>
<Accordion.ItemTrigger>
What is {item()}?
<Accordion.ItemIndicator>
<ChevronDownIcon />
</Accordion.ItemIndicator>
</Accordion.ItemTrigger>
<Accordion.ItemContent>
{item()} is a JavaScript library for building user interfaces.
</Accordion.ItemContent>
</Accordion.Item>
)}
</Index>
</Accordion.RootProvider>
</>
)
}
<script setup lang="ts">
import { Accordion, useAccordion } from '@ark-ui/vue/accordion'
import { ChevronRightIcon } from 'lucide-vue-next'
const items = ['React', 'Solid', 'Vue', 'Svelte']
const accordion = useAccordion({
multiple: true,
defaultValue: ['React'],
})
</script>
<template>
<button @click="accordion.setValue(['Vue'])">Set to Vue</button>
<Accordion.RootProvider :value="accordion">
<Accordion.Item v-for="item in items" :key="item" :value="item">
<Accordion.ItemTrigger>
What is {{ item }}?
<Accordion.ItemIndicator>
<ChevronRightIcon />
</Accordion.ItemIndicator>
</Accordion.ItemTrigger>
<Accordion.ItemContent>{{ item }} is a JavaScript library for building user interfaces.</Accordion.ItemContent>
</Accordion.Item>
</Accordion.RootProvider>
</template>
<script>
import { Accordion, useAccordion } from '@ark-ui/svelte/accordion'
import { ChevronDownIcon } from 'lucide-svelte'
const id = $props.id()
const accordion = useAccordion({
id,
defaultValue: ['React'],
})
</script>
<div>
<div>Open items: {JSON.stringify(accordion().value)}</div>
<Accordion.RootProvider value={accordion}>
{#each ['React', 'Solid', 'Vue', 'Svelte'] as item (item)}
<Accordion.Item value={item}>
<Accordion.ItemTrigger>
What is {item}?
<Accordion.ItemIndicator>
<ChevronDownIcon />
</Accordion.ItemIndicator>
</Accordion.ItemTrigger>
<Accordion.ItemContent>
{item} is a JavaScript library for building user interfaces.
</Accordion.ItemContent>
</Accordion.Item>
{/each}
</Accordion.RootProvider>
</div>
When using the
RootProvider
component, you don't need to use theRoot
component.
See more in Examples.