Skip to content

KeyValueList ​

The facts of one thing as names and values side by side: the headers of a message, the details of a connection, the properties of a record. A compound component on a description list (<dl>). For a setting a person changes, use a SettingsRow instead.

tsx
import { KeyValueList } from '@adecore/ui';
tsx
import { KeyValueList } from '@adecore/ui';

export default function KeyValueListDemo() {
    return (
        <KeyValueList.Root className="w-full max-w-sm">
            <KeyValueList.Item>
                <KeyValueList.Name>Length</KeyValueList.Name>
                <KeyValueList.Value>3 min 45 s</KeyValueList.Value>
            </KeyValueList.Item>
            <KeyValueList.Item>
                <KeyValueList.Name>Tempo</KeyValueList.Name>
                <KeyValueList.Value>118 BPM</KeyValueList.Value>
            </KeyValueList.Item>
            <KeyValueList.Item>
                <KeyValueList.Name>Mood</KeyValueList.Name>
                <KeyValueList.Value>Warm, unhurried, with a lift in the last chorus</KeyValueList.Value>
            </KeyValueList.Item>
        </KeyValueList.Root>
    );
}
tsx
<KeyValueList.Root>
    <KeyValueList.Item>
        <KeyValueList.Name>Tempo</KeyValueList.Name>
        <KeyValueList.Value>118 BPM</KeyValueList.Value>
    </KeyValueList.Item>
</KeyValueList.Root>

Parts ​

PartWhat it is
KeyValueList.RootThe list, a <dl>. divided puts a hairline between rows.
KeyValueList.ItemOne row, which holds a name and its value.
KeyValueList.NameThe name, a <dt>, in the faint text color.
KeyValueList.ValueThe value, a <dd>. mono sets it in the monospace face.

Every part takes className, ref and a render prop to draw another element.

Alignment ​

The names take the width of the widest one, so every value starts on the same line. A long name stops at two fifths of the list and wraps there, so a list of message headers never squeezes its values into a sliver. A value wraps too, also in the middle of a long token or URL, instead of running out of its column.

A long list ​

divided draws a hairline between rows and gives each a little air, for a list that runs to dozens of rows. Without it the rows sit close, which suits three or four facts in a card. mono on a value sets it in the monospace face, for a header, an id or a path that a person reads back letter by letter.

tsx
import { KeyValueList } from '@adecore/ui';

const HEADERS: [string, string][] = [
    ['From', 'Billing <billing@example.com>'],
    ['To', 'ada@example.com'],
    ['Subject', 'Your invoice for September'],
    ['Message-ID', '<20261005134412.4f8a2c91e7b3@mail.example.com>'],
    ['DKIM-Signature', 'v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.com; s=mail; h=from:to:subject:date; bh=47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU='],
    ['X-Mailer-Campaign-Identifier', 'autumn-billing-2026']
];

export default function KeyValueListDividedDemo() {
    return (
        <KeyValueList.Root divided className="w-full max-w-xl">
            {HEADERS.map(([name, value]) => (
                <KeyValueList.Item key={name}>
                    <KeyValueList.Name>{name}</KeyValueList.Name>
                    <KeyValueList.Value mono>{value}</KeyValueList.Value>
                </KeyValueList.Item>
            ))}
        </KeyValueList.Root>
    );
}

Rich content ​

A name and a value take any content: an Icon before a name, a status icon before a value, or a row of Pills. An icon sits on the middle of the line of text beside it.

tsx
import { CircleCheck, Globe, KeyRound, Server } from 'lucide-react';
import { Icon, KeyValueList, Pill } from '@adecore/ui';

export default function KeyValueListRichDemo() {
    return (
        <KeyValueList.Root divided className="w-full max-w-md">
            <KeyValueList.Item>
                <KeyValueList.Name>
                    <Icon icon={Server} size={14} /> Host
                </KeyValueList.Name>
                <KeyValueList.Value mono>smtp.example.com</KeyValueList.Value>
            </KeyValueList.Item>
            <KeyValueList.Item>
                <KeyValueList.Name>
                    <Icon icon={Globe} size={14} /> Port
                </KeyValueList.Name>
                <KeyValueList.Value mono>587</KeyValueList.Value>
            </KeyValueList.Item>
            <KeyValueList.Item>
                <KeyValueList.Name>
                    <Icon icon={KeyRound} size={14} /> Security
                </KeyValueList.Name>
                <KeyValueList.Value>
                    <Icon icon={CircleCheck} size={14} className="text-positive" /> STARTTLS
                </KeyValueList.Value>
            </KeyValueList.Item>
            <KeyValueList.Item>
                <KeyValueList.Name>Tags</KeyValueList.Name>
                <KeyValueList.Value className="flex flex-wrap gap-1">
                    <Pill>billing</Pill>
                    <Pill>autumn</Pill>
                </KeyValueList.Value>
            </KeyValueList.Item>
        </KeyValueList.Root>
    );
}

Copying ​

The interface around it cannot be selected, but a value can: a person selects a header or a token and copies it with the platform's shortcut. The names stay out of the selection.

KeyValueListRootProps, KeyValueListItemProps, KeyValueListNameProps and KeyValueListValueProps are exported types.