TextValue
Inline label primitive for selection items.
The <TextValue> component is a small inline label designed for selection items such as <Select.Option>, <Menu.Item>, <ListBox.Item>, and <ComboBox.Item>. By marking the label explicitly, screen readers announce the right text and keyboard typeahead lands on the right item without any extra wiring.
Anatomy
A <TextValue> is a single inline label with no parts of its own. It sits inside a selection item and marks the text that names the option.
Appearance
<TextValue> carries no styling of its own. Its appearance comes entirely from the surrounding selection item, which keeps the look consistent across menus, selects, and lists. Pair it with <Description> when the item also needs a help line.
Styling is inferred automatically from the surrounding context. See Slot-Driven Composition for how this works.
Usage
<TextValue> exists to mark the label inside a selection item when the item's content is more than a plain string. As soon as an option contains an avatar, an icon, a badge, or any non-text node, there is no longer a single unambiguous text node the option can use as its accessible name. <TextValue> marks that text explicitly, so screen readers announce the right name and keyboard typeahead matches the visible label.
Reach for <TextValue> when:
- the option's children include images, icons, or layout components like
<Inline>or<Stack>, - the option pairs a label with a
<Description>help line, or - you want the label to drive typeahead independently from supporting copy.
Do
Keep <TextValue> content to a short label that names the option, like
'Admin', 'Archived', or 'Newest first'. Screen readers announce it and
keyboard typeahead matches against it.
Don't
Don't reach for <TextValue> when:
- an option's children are a single string, the selection item already uses that as the accessible name, or
- you need paragraph text or arbitrary inline strings, use
<Text>instead.
With a description
Place a <Description> next to the <TextValue> inside a selection item to add a one-line help line below the label. The label drives the option's accessible name and typeahead. The description sits below it.
import { Description, Select, TextValue } from '@marigold/components';export default () => ( <Select label="Region" defaultValue="eu" width={64}> <Select.Option id="eu"> <TextValue>Europe</TextValue> <Description>Frankfurt, Dublin, Stockholm</Description> </Select.Option> <Select.Option id="us"> <TextValue>North America</TextValue> <Description>Virginia, Oregon</Description> </Select.Option> <Select.Option id="ap"> <TextValue>Asia Pacific</TextValue> <Description>Tokyo, Singapore, Sydney</Description> </Select.Option> </Select>);With a complex layout
When option content includes images, icons, or layout components like <Inline>, keep <TextValue> around the label text only and let the layout wrap around it. Set the option's textValue prop so typeahead has a plain string to match even when the label sits behind layout.
Pass renderValue to <Select> to control what the trigger shows once an option is selected. Without it the trigger renders the option's full layout, which rarely looks right inside a single-line button.
import type { Venue } from '@/lib/data/venues';import { venues } from '@/lib/data/venues';import { Inline, NumericFormat, Select, Stack, Text, TextValue,} from '@marigold/components';export default () => ( <Select label="Choose a venue" placeholder="Select a venue" defaultValue="6" width={96} items={venues} renderValue={([venue]: Venue[]) => ( <Inline space={2} alignY="center"> <img src={venue.image} alt="" className="size-5 rounded-sm object-cover" /> <Text>{venue.name}</Text> </Inline> )} > {(venue: Venue) => ( <Select.Option id={venue.id}> <Inline space={4} alignY="center" noWrap> <img src={venue.image} alt="" className="size-16 rounded-md object-cover" /> <Stack space="tight"> <TextValue>{venue.name}</TextValue> <Text size="xs" variant="muted"> {venue.city}, {venue.country} · Capacity {venue.capacity} ·{' '} {venue.indoor ? 'Indoor' : 'Outdoor'} </Text> <Inline space={2} alignY="center"> <Text size="xs" weight="medium"> <NumericFormat style="currency" currency="USD" value={venue.price.from} maximumFractionDigits={0} /> {' – '} <NumericFormat style="currency" currency="USD" value={venue.price.to} maximumFractionDigits={0} /> </Text> <Text size="xs" variant="muted"> ★ {venue.rating.toFixed(1)} </Text> </Inline> </Stack> </Inline> </Select.Option> )} </Select>);Driving typeahead
Filtering inside a <ComboBox> follows the option's label, not its supporting copy. <TextValue> marks that label, so typing a fragment of a description won't match. Only the label is searched.
import { ComboBox, Description, TextValue } from '@marigold/components';export default () => ( <ComboBox label="Framework" placeholder="Search frameworks" width={72}> <ComboBox.Option id="react" textValue="React"> <TextValue>React</TextValue> <Description>Component-based UI library</Description> </ComboBox.Option> <ComboBox.Option id="vue" textValue="Vue"> <TextValue>Vue</TextValue> <Description>Progressive web framework</Description> </ComboBox.Option> <ComboBox.Option id="svelte" textValue="Svelte"> <TextValue>Svelte</TextValue> <Description>Compiles to vanilla JavaScript</Description> </ComboBox.Option> <ComboBox.Option id="solid" textValue="Solid"> <TextValue>Solid</TextValue> <Description>Fine-grained reactivity</Description> </ComboBox.Option> </ComboBox>);Set `textValue` on each ComboBox option
A <ComboBox> shows the selected value inside a text input, which can only display a string. When the option's children are more than plain text, set the textValue prop on the option to the label string. Without it, the input falls back to stringifying the children and renders [object Object]. The textValue prop also drives keyboard typeahead.
Custom styling
<TextValue> carries no styling of its own. It picks up its look from the surrounding selection item, which keeps menus, selects, and lists feeling consistent. That works for most cases, but it also means the label can't be made larger, bolder, or visually distinct from the rest of the option content.
When an option grows into a richer layout where the title should be the loudest element, set the textValue prop on the option instead. It accepts a plain string that becomes the option's accessible name and the source for keyboard typeahead, while leaving the children free to render any layout and typography. Pair it with renderValue on the parent to bring the name back into the trigger once selected.
import type { Venue } from '@/lib/data/venues';import { venueTypes, venues } from '@/lib/data/venues';import { Badge, Inline, Inset, NumericFormat, Select, Stack, Text,} from '@marigold/components';export default () => ( <Select label="Choose a venue" placeholder="Browse venues" defaultValue="6" width="full" items={venues} renderValue={([venue]: Venue[]) => ( <Inline space={2} alignY="center" noWrap> <img src={venue.image} alt="" className="size-5 rounded-sm object-cover" /> <Inline space={1} alignY="center" noWrap> <Text>{venue.name}</Text> <Text variant="muted"> ({venue.city}, {venue.country}) </Text> </Inline> </Inline> )} > {(venue: Venue) => ( <Select.Option id={venue.id} textValue={venue.name}> <Inset p="square-regular"> <Inline space={4} alignY="top" noWrap> <img src={venue.image} alt="" className="aspect-video w-40 shrink-0 rounded-md object-cover" /> <Stack space={2}> <Stack space={1}> <Inline space={2} alignY="center"> <Badge variant="info">{venueTypes[venue.type]}</Badge> <Text size="xs" variant="muted"> {venue.city}, {venue.country} </Text> </Inline> <Text size="xl" weight="bold" lineHeight="tight"> {venue.name} </Text> </Stack> <Text size="sm" variant="muted"> {venue.description} </Text> <Inline space={3} alignY="center" alignX="between"> <Inline space={3} alignY="center"> <Text size="xs" variant="muted"> Capacity {venue.capacity} </Text> <Text size="xs" variant="muted"> {venue.indoor ? 'Indoor' : 'Outdoor'} </Text> <Text size="xs" variant="muted"> ★ {venue.rating.toFixed(1)} </Text> </Inline> <Text size="sm" weight="semibold"> <NumericFormat style="currency" currency="USD" value={venue.price.from} maximumFractionDigits={0} /> {' – '} <NumericFormat style="currency" currency="USD" value={venue.price.to} maximumFractionDigits={0} /> </Text> </Inline> </Stack> </Inline> </Inset> </Select.Option> )} </Select>);Props
TextValue
Prop
Type
Accessibility props (54)
Prop
Type
DOM event handlers (164)
Prop
Type
Alternative components
- Text: Use for paragraph text and body copy.
- Description: The help-line partner inside selection items.