Skip to content
weftGitHub

KeyValueEditor

A small table of names and values, like environment variables or headers.

SourcePackage @weft-js/warp

Preview

tsx
import { state } from '@weft-js/core'import { KeyValueEditor, Stack, Text } from '@weft-js/warp'export function Env() {  const env = state(ENV)  return (    <Stack gap="related">      <KeyValueEditor label="Environment variables" keyLabel="Name" value={env} />      <Text subtle>{env.value.filter(r => r.key).length} variables reach the next deploy.</Text>    </Stack>  )}const ENV = [{ key: 'NODE_ENV', value: 'production' }, { key: 'API_URL', value: 'https://api.weft.dev' }, { key: 'LOG_LEVEL', value: 'info' }]

Installation

npm install @weft-js/warp

Usage

tsx
import { KeyValueEditor } from '@weft-js/warp'

Form or JSON

json adds a switch. Both views edit the same value.

tsx
import { KeyValueEditor } from '@weft-js/warp'export function Json() {  return (    <KeyValueEditor label="Response headers" keyLabel="Header" json defaultValue={HEADERS} />  )}const HEADERS = [{ key: 'Cache-Control', value: 'max-age=30' }, { key: 'X-Frame-Options', value: 'DENY' }]

A repeated key

A warning, not a block: the last one wins.

tsx
import { KeyValueEditor } from '@weft-js/warp'export function Duplicate() {  return (    <KeyValueEditor label="Environment variables" keyLabel="Name" defaultValue={[...ENV, { key: 'LOG_LEVEL', value: 'debug' }]} />  )}const ENV = [{ key: 'NODE_ENV', value: 'production' }, { key: 'API_URL', value: 'https://api.weft.dev' }, { key: 'LOG_LEVEL', value: 'info' }]

States

A key value editor can start empty or filled, and can show a hint or an error.

tsx
import { Grid, KeyValueEditor, Stack, Text } from '@weft-js/warp'export function States() {  return (    <Grid cols={2} gap="group">      <Cell name="Empty"><KeyValueEditor label="Response headers" /></Cell>      <Cell name="Filled"><KeyValueEditor label="Response headers" defaultValue={HEADERS} /></Cell>      <Cell name="Hint"><KeyValueEditor label="Response headers" hint="Added to every response." defaultValue={HEADERS} /></Cell>      <Cell name="Invalid"><KeyValueEditor label="Response headers" error="X-Frame-Options can't be changed on this plan." defaultValue={HEADERS} /></Cell>    </Grid>  )}const Cell = (p: { name: string; children?: unknown }) => (  <Stack gap="tight">    <Text role="caption" subtle>{p.name}</Text>    {p.children}  </Stack>)const HEADERS = [{ key: 'Cache-Control', value: 'max-age=30' }, { key: 'X-Frame-Options', value: 'DENY' }]

When to use

Use it for

  • Pairs people add and remove by hand

Reach for something else

Accessibility

Tab
Moves from cell to cell
Enter
On Add row, adds one and moves to it

API Reference

PropTypeDefaultDescription
labelstring—The visible name, and what screen readers announce.
hintunknown—A short line of help under the field.
errorunknown—Replaces the hint and marks the field invalid.
iconIconRef—Leading icon: it sweeps with the border.
prefixunknown—Text or an element inside the field before the value, such as $.
suffixunknown—Text or an element inside the field after the value, such as kg.
variant"floating" | "classic"—floating (default, or the app's warp({ fields })) · classic: the label above.
appearance"line"—line: only a bottom line under the value, no box or fill. The boxed field otherwise.
size"sm" | "md" | "lg"'md'Control size. md by default.
clearablebooleanfalseA clear button (shown once there is a value).
counternumber—Maximum length, shown as a counter.
idstring—The element id. One is made for you if you leave it out.
disabledbooleanfalseCan't be focused or changed.
readonlybooleanfalseShows the value and lets it be selected and copied, but not changed.
valueKeyValue[] | State<KeyValue[]>—The rows as { key, value }, or a State to bind.
defaultValueKeyValue[]—The starting value, when the component owns the state.
keyLabelstring'Key'The first column's heading.
valueLabelstring'Value'The second column's heading.
jsonbooleanfalseAdds a switch to a JSON view of the same value.
EventTypeDefaultDescription
onInput((e: InputEvent & { currentTarget: HTMLInputElement & HTMLTextAreaElemen…—Called on each keystroke.
onValueChange((v: KeyValue[], reason: ChangeReason) => void)—Hears the new rows and why they changed.

Parts

Every element carries a stable name, so you can style it from your own CSS: [data-scope="kv"] [data-part="remove"]

removeaddsr-only

RelatedJson editorTable