Skip to content
design
Components

IP address input

An IP address field for one address, or several as chips, each checked as it is added.

On this page
9.9.9.9149.112.112.112
IPv4 or IPv6, separated by commas or spaces.
A network's DNS servers: two held as chips, the next typed after them.
Code
import { IpAddressInput } from "@bosunkit/components/ip-address-input";
import { useState, type Dispatch, type ReactElement, type SetStateAction } from "react";

export const description: string =
  "A network's DNS servers, two held as chips: a space, a comma or Enter adds the next, a pasted list adds each " +
  "address in it, and one that is not an address stays typed with the error naming it.";

export default function Showcase(): ReactElement {
  const [servers, setServers]: [readonly string[], Dispatch<SetStateAction<readonly string[]>>] = useState<
    readonly string[]
  >(["9.9.9.9", "149.112.112.112"]);
  return (
    <div style={{ width: "100%", maxWidth: 440 }}>
      <IpAddressInput
        label="DNS servers"
        multiple
        maxAddresses={4}
        value={servers}
        onValueChange={setServers}
        hint="IPv4 or IPv6, separated by commas or spaces."
      />
    </div>
  );
}

An IP address input takes IPv4 addresses in dotted decimal and IPv6 addresses without a zone, the forms NetworkManager and bosund's network settings store. With multiple it holds several, each a chip with a remove button: a space, a comma or Enter after an address adds it, leaving the field adds what is typed, and a pasted list adds each address in it. Without multiple it holds one, reported when the crew leave the field or press Enter.

An address the field cannot take stays typed, and the error names it and says what to write: text that is not an address, an IPv6 address in a field that takes version="ipv4" (or the reverse), an address it already holds, or one more than maxAddresses. The addresses it can take are added beside it, so a pasted list loses none of them. Nothing wrong is ever reported, and an address with a space, a tab or a line break inside it is not an address. An IPv6 address is reported as Go's net/netip writes it, the form bosund stores: compressed and in lower case (2620:fe::fe for 2620:00FE::00FE), an IPv4-mapped address with its IPv4 part dotted (::ffff:192.0.2.1), so two spellings of one server are one address. A field with version="ipv4" opens a phone's number pad. The form receives the addresses joined by commas under name.

Examples

Several addresses

9.9.9.9149.112.112.112
IPv4 or IPv6, separated by commas or spaces.
A network's DNS servers, two held as chips: a space, a comma or Enter adds the next, a pasted list adds each address in it, and one that is not an address stays typed with the error naming it.
Code
import { IpAddressInput } from "@bosunkit/components/ip-address-input";
import { useState, type Dispatch, type ReactElement, type SetStateAction } from "react";

export const description: string =
  "A network's DNS servers, two held as chips: a space, a comma or Enter adds the next, a pasted list adds each " +
  "address in it, and one that is not an address stays typed with the error naming it.";

export default function Showcase(): ReactElement {
  const [servers, setServers]: [readonly string[], Dispatch<SetStateAction<readonly string[]>>] = useState<
    readonly string[]
  >(["9.9.9.9", "149.112.112.112"]);
  return (
    <div style={{ width: "100%", maxWidth: 440 }}>
      <IpAddressInput
        label="DNS servers"
        multiple
        maxAddresses={4}
        value={servers}
        onValueChange={setServers}
        hint="IPv4 or IPv6, separated by commas or spaces."
      />
    </div>
  );
}

One address

One IPv4 address, the radar's on the Navico network: a phone shows its number pad, and an IPv6 address is refused.
Code
import { IpAddressInput } from "@bosunkit/components/ip-address-input";
import type { ReactElement } from "react";

export const description: string =
  "One IPv4 address, the radar's on the Navico network: a phone shows its number pad, and an IPv6 address is refused.";

export default function OneAddress(): ReactElement {
  return (
    <div style={{ width: "100%", maxWidth: 320 }}>
      <IpAddressInput label="Radar address" version="ipv4" placeholder="236.6.7.8" defaultValue={["236.6.7.8"]} />
    </div>
  );
}

Read-only

1.1.1.12606:4700:4700::1111
Read-only, the addresses show as chips with no remove buttons.
Code
import { IpAddressInput } from "@bosunkit/components/ip-address-input";
import type { ReactElement } from "react";

export const description: string = "Read-only, the addresses show as chips with no remove buttons.";

export default function ReadOnly(): ReactElement {
  return (
    <div style={{ width: "100%", maxWidth: 440 }}>
      <IpAddressInput label="DNS servers" multiple readOnly defaultValue={["1.1.1.1", "2606:4700:4700::1111"]} />
    </div>
  );
}

Disabled

9.9.9.9
Disabled, the field and its chips take no input.
Code
import { IpAddressInput } from "@bosunkit/components/ip-address-input";
import type { ReactElement } from "react";

export const description: string = "Disabled, the field and its chips take no input.";

export default function Disabled(): ReactElement {
  return (
    <div style={{ width: "100%", maxWidth: 440 }}>
      <IpAddressInput label="DNS servers" multiple disabled defaultValue={["9.9.9.9"]} />
    </div>
  );
}

Props

PropTypeDefaultDescription
labelstring | undefined–The field's name on the outline, translated by the caller; omit it inside a FormField.
valuereadonly string[] | undefined–The addresses, when the caller holds them: one at most unless multiple, IPv6 in its compressed form.
defaultValuereadonly string[] | undefined–The addresses it starts with when it holds them itself.
onValueChange((addresses: readonly string[]) => void) | undefined–Called with every address the field holds once one is added or removed; nothing while a typed one is wrong.
multipleboolean | undefinedfalseWhether it takes more than one address, each shown as a chip with a remove button.
maxAddressesnumber | undefined–The most addresses it holds, with multiple.
versionAcceptedIpVersion | undefined"any"Which addresses it takes: ipv4, ipv6, or either. Either unless set.
hintstring | undefined–Text under the field, translated by the caller.
errorstring | undefined–The caller's own error, shown in place of the field's.
requiredboolean | undefined–Whether the field must hold an address.
disabledboolean | undefined–Whether the field takes no input.
readOnlyboolean | undefined–Whether the field shows its addresses without letting the crew change them.
idstring | undefined–The text input's id, for a Label's htmlFor.
namestring | undefined–The form field name; the form receives the addresses joined by commas.
placeholderstring | undefined–Text shown while nothing is typed, translated by the caller.
aria-labelstring | undefined–Names the text input when there is no label.
classNamestring | undefined–Classes added to the field's root.
testIdstring | undefined–data-testid on the field.
refRef<HTMLInputElement> | undefined–The text input.
motionMotionProp | undefined–How the error line moves; see OutlinedField.
  • DNS picker: its custom servers are an IP address input.
  • Chips: the chips the addresses are drawn as.
  • Outlined field: the box the field draws in.