@dnb/eufemia
Version:
DNB Eufemia Design System UI Library
520 lines (470 loc) • 17.4 kB
Markdown
---
title: 'Field.PostalCodeAndCity'
description: '`Field.PostalCodeAndCity` is a wrapper component for input of two separate values with user experience tailored for postal code and city values.'
version: 10.104.0
generatedAt: 2026-04-17T18:46:12.732Z
checksum: 090b7d977ba4be5e2c4c04d199a30a4048416c59f443a56985df2f80629d9c40
---
# Field.PostalCodeAndCity
## Import
```tsx
import { Field } from '@dnb/eufemia/extensions/forms'
render(<Field.PostalCodeAndCity />)
```
## Description
`Field.PostalCodeAndCity` is a wrapper component for input of two separate [input of strings](/uilib/extensions/forms/base-fields/String) with user experience tailored for postal code and city values.
These fields are meant for Norwegian postal codes and cities. The postal code input takes a 4-digit string as values, since it's meant for Norwegian postal codes. A Norwegian postal code can have a leading zero, which is why the value is a string and not a number.
More info about postal codes can be found at [Posten](https://www.bring.no/tjenester/adressetjenester/postnummer).
It supports the HTML [autocomplete](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete) attribute, and by default set it to `postal-code` for the postal code field, and to `address-level2` for the city field.
There is a corresponding [Value.PostalCodeAndCity](/uilib/extensions/forms/Value/PostalCodeAndCity) component.
## Relevant links
- [Source code](https://github.com/dnbexperience/eufemia/tree/main/packages/dnb-eufemia/src/extensions/forms/Field/PostalCodeAndCity)
- [Docs code](https://github.com/dnbexperience/eufemia/tree/main/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/feature-fields/PostalCodeAndCity)
## Validation and autofill
Read more about how to use the [Bring API](/uilib/extensions/forms/Connectors/Bring/) to validate and autofill a postal code and city name.
## Demos
### Empty
```tsx
render(
<Field.PostalCodeAndCity
postalCode={{
onChange: (value) => console.log('postalCode onChange', value),
}}
city={{
onChange: (value) => console.log('city onChange', value),
}}
/>
)
```
### Placeholder
```tsx
render(
<Field.PostalCodeAndCity
postalCode={{
placeholder: '????',
onChange: (value) => console.log('postalCode onChange', value),
}}
city={{
placeholder: 'Your city',
onChange: (value) => console.log('city onChange', value),
}}
/>
)
```
### Label
```tsx
render(
<Field.PostalCodeAndCity
postalCode={{
label: 'PNR',
onChange: (value) => console.log('postalCode onChange', value),
}}
city={{
label: 'CTY',
onChange: (value) => console.log('city onChange', value),
}}
/>
)
```
### Label and value
```tsx
render(
<Field.PostalCodeAndCity
postalCode={{
label: 'Pnr.',
value: '0788',
onChange: (value) => console.log('postalCode onChange', value),
}}
city={{
value: 'Oslo',
onChange: (value) => console.log('city onChange', value),
}}
/>
)
```
### Iterate over array
By using the `itemPath` property, you can iterate over an array and use the `postalCode` and `city` properties to render the fields.
```tsx
render(
<Iterate.Array
value={[
{
postalCode: '0788',
city: 'Oslo',
},
{
postalCode: '0789',
city: 'Bergen',
},
]}
>
<Field.PostalCodeAndCity
postalCode={{
itemPath: '/postalCode',
}}
city={{
itemPath: '/city',
}}
/>
</Iterate.Array>
)
```
### Disabled
```tsx
render(
<Field.PostalCodeAndCity
postalCode={{
value: '1234',
disabled: true,
onChange: (value) => console.log('postalCode onChange', value),
}}
city={{
value: 'Oslo',
disabled: true,
onChange: (value) => console.log('city onChange', value),
}}
/>
)
```
### With help
```tsx
render(
<Field.PostalCodeAndCity
postalCode={{
onChange: (value) => console.log('postalCode onChange', value),
}}
city={{
onChange: (value) => console.log('city onChange', value),
}}
help={{
title: 'Help is available',
content:
'Helping others, encouraging others, are often acts of being kind that have more meaning that you may realize.',
}}
/>
)
```
### Error
```tsx
render(
<Field.PostalCodeAndCity
postalCode={{}}
city={{}}
error={new Error('This is what is wrong...')}
/>
)
```
### Validation - Required
```tsx
render(
<Field.PostalCodeAndCity
postalCode={{
required: true,
}}
city={{
required: true,
}}
/>
)
```
### Path Based Country
The `country` property supports a field path as value. This allows you to set the `country` based on the value of another field.
```tsx
render(
<Form.Handler>
<Form.Card>
<Field.SelectCountry path="/country" defaultValue="NO" />
<Field.PostalCodeAndCity countryCode="/country" />
</Form.Card>
</Form.Handler>
)
```
### Non-Norwegian Postal Codes
If you want to allow for a postal code that is not Norwegian, just set the `country` property to the desired country, and add your own custom validation.
NB: As of today, setting `country` property to anything other than `NO` will only remove the default norwegian postal code pattern, mask, and placeholder, but not actually set the postal code pattern, mask, and placeholder for the value provided to the `country` property. This functionality will hopefully be implemented in the future.
```tsx
render(
<Form.Handler
translations={{
'nb-NO': {
'PostalCode.errorPattern':
'Dette er ikke et gyldig postnummer (fem siffer).',
},
'en-GB': {
'PostalCode.errorPattern':
'This is not a valid postal code (five-digits).',
},
}}
>
<Field.PostalCodeAndCity
countryCode="DE"
postalCode={{
pattern: '^[0-9]{5}$',
onBlurValidator: undefined,
mask: [/\\d/, /\\d/, /\\d/, /\\d/, /\\d/],
placeholder: '00000',
width: '5.4rem',
}}
city={{
pattern: '^[a-zA-ZäöüÄÖÜß -]+$',
width: 'stretch',
}}
/>
</Form.Handler>
)
```
```tsx
render(
<Field.PostalCodeAndCity
postalCode={{
label: 'With a very long label',
}}
city={{
label: 'With a very long label',
}}
/>
)
```
## Properties
### Field-specific properties
```json
{
"props": {
"countryCode": {
"doc": "Defines which country the postal code and city is for, based on the [ISO 3166-1 alpha-2 code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) i.e. `NO`, `DE` etc. Setting it to anything other than `NO` will remove the default norwegian postal code pattern. You can also use the value of another field to define the countryCode, by using a path value i.e. `/myCountryCodePath`. Defaults to `NO`.",
"type": ["Path", "string"],
"status": "optional"
},
"postalCode": {
"doc": "Properties for the [Field.String](/uilib/extensions/forms/base-fields/String/) component for postal code.",
"type": "object",
"status": "required"
},
"city": {
"doc": "Properties for the [Field.String](/uilib/extensions/forms/base-fields/String/) component for city.",
"type": "object",
"status": "required"
},
"help": {
"doc": "Provide help content for the field using `title` and `content` as a string or React.Node. Additionally, you can set `open` to `true` to display the inline help, set the `breakout` property to `false` to disable the breakout of the inline help content, set `outset` to `false` to display the help text inline (inset) instead of the default outset behavior, or use `renderAs` set to `dialog` to render the content in a [Dialog](/uilib/components/dialog/) (recommended for larger amounts of content).",
"type": "object",
"status": "optional"
},
"size": {
"doc": "The sizes you can choose is `default` (2rem), `medium` (2.5rem) and `large` (3rem) are supported component sizes. Defaults to `default` / `null`. Also, if you define a number like `size={2}` then it will be forwarded as the input element attribute. Consider rather setting field sizes with [Form.Appearance](/uilib/extensions/forms/Form/Appearance/).",
"type": ["string", "number"],
"status": "optional"
}
}
}
```
### General properties
```json
{
"props": {
"label": {
"doc": "Label text displayed above the field. Most fields already have a default label, so check the field translations for an existing label entry. Only set `label` when you need to override the default.",
"type": "string",
"status": "optional"
},
"labelDescription": {
"doc": "A more discreet text displayed beside the label (i.e for \"(optional)\").",
"type": "string",
"status": "optional"
},
"labelDescriptionInline": {
"doc": "If true, the `labelDescription` will be displayed on the same line as the label.",
"type": "boolean",
"status": "optional"
},
"labelSrOnly": {
"doc": "Use `true` to make the label only readable by screen readers.",
"type": "boolean",
"status": "optional"
},
"labelSize": {
"doc": "Define one of the following [heading sizes](/uilib/elements/heading/): `medium` or `large`.",
"type": ["string", "false"],
"status": "optional"
},
"help": {
"doc": "Provide help content for the field using `title` and `content` as a string or React.Node. Additionally, you can set `open` to `true` to display the inline help, set the `breakout` property to `false` to disable the breakout of the inline help content, set `outset` to `false` to display the help text inline (inset) instead of the default outset behavior, or use `renderAs` set to `dialog` to render the content in a [Dialog](/uilib/components/dialog/) (recommended for larger amounts of content).",
"type": "object",
"status": "optional"
},
"hideHelpButton": {
"doc": "Set `true` when you render the inline help button outside the label (e.g. inside a checkbox suffix) so FieldBlock skips drawing the default label help button.",
"type": "boolean",
"status": "optional"
},
"statusPosition": {
"doc": "Controls where status messages (`error`, `warning`, `info`) are visually shown. Use `below` (default) or `above`.",
"type": ["\"below\"", "\"above\""],
"status": "optional"
},
"layout": {
"doc": "Layout for the label and input. Can be `horizontal` or `vertical`.",
"type": "string",
"status": "optional"
},
"layoutOptions": {
"doc": "Use this to set additional options for the `horizontal` layout. E.g. `{ width: \"medium\" }`. You can also use a custom width `{number}rem`. Instead of a width, you can use a min/max width. E.g. `{ minWidth: \"6rem\", maxWidth: \"12rem\" }`.",
"type": "object",
"status": "optional"
},
"width": {
"doc": "Will set the width for the whole block. Use `small`, `medium`, `large` for predefined standard widths. You can also set a custom width `{number}rem` or use `stretch` or `false`.",
"type": ["string", "false"],
"status": "optional"
},
"contentWidth": {
"doc": "Will set the width for its contents. Use `small`, `medium`, `large` for predefined standard widths. You can also set a custom width `{number}rem` or use `stretch` or `false`.",
"type": ["string", "false"],
"status": "optional"
},
"[Space](/uilib/layout/space/properties)": {
"doc": "Spacing properties like `top` or `bottom` are supported.",
"type": ["string", "object"],
"status": "optional"
},
"labelHeight": {
"doc": "Defines the height of an component (size prop), so the label can be aligned correctly. Can be `default`, `small`, `medium`, `large`.",
"type": "string",
"status": "optional"
},
"asFieldset": {
"doc": "Use `true` when you have several form elements. This way a `fieldset` with a `legend` is used.",
"type": "boolean",
"status": "optional"
},
"align": {
"doc": "`center` or `bottom` for aligning the contents vertically. Defaults to `bottom`.",
"type": ["string", "false"],
"status": "optional"
},
"disableStatusSummary": {
"doc": "Use `true` to disable the error summary.",
"type": "boolean",
"status": "optional"
},
"composition": {
"doc": "Use `true` for when you have more than one field wrapped.",
"type": "true",
"status": "optional"
},
"disabled": {
"doc": "Set `true` to make the inner [FormLabel](/uilib/components/form-label/) behave as disabled.",
"type": "boolean",
"status": "optional"
}
},
"pick": ["width", "help", "[Space](/uilib/layout/space/properties)"]
}
```
## Translations
```json
{
"locales": ["da-DK", "en-GB", "nb-NO", "sv-SE"],
"entries": {
"City.errorPattern": {
"nb-NO": "Du må skrive inn et gyldig stedsnavn. Kun bokstaver, bindestrek og mellomrom er tillatt.",
"en-GB": "You must enter a valid city name. Only letters, hyphens and spaces are allowed.",
"sv-SE": "Du måste ange ett giltigt ortsnamn. Endast bokstäver, bindestreck och mellanslag är tillåtna.",
"da-DK": "Du skal indtaste et gyldigt bynavn. Kun bogstaver, bindestreger og mellemrum er tilladt."
},
"City.errorRequired": {
"nb-NO": "Du må fylle inn et stedsnavn.",
"en-GB": "You must enter a city name.",
"sv-SE": "Du måste fylla i ett ortsnamn.",
"da-DK": "Du skal udfylde et bynavn."
},
"City.label": {
"nb-NO": "Sted",
"en-GB": "City",
"sv-SE": "Ort",
"da-DK": "By"
},
"Field.errorPattern": {
"nb-NO": "Du må skrive inn en gyldig verdi.",
"en-GB": "You must enter a valid value.",
"sv-SE": "Du måste ange ett giltigt värde.",
"da-DK": "Du skal indtaste en gyldig værdi."
},
"Field.errorRequired": {
"nb-NO": "Dette feltet må fylles ut.",
"en-GB": "This field is required.",
"sv-SE": "Detta fält måste fyllas i.",
"da-DK": "Dette felt skal udfyldes."
},
"Field.errorSummary": {
"nb-NO": "Feil som må rettes:",
"en-GB": "Please correct the following errors:",
"sv-SE": "Fel som måste åtgärdas:",
"da-DK": "Felter der skal rettes:"
},
"Field.errorSummaryTitle": {
"nb-NO": "Feil som må rettes",
"en-GB": "Please correct the following errors",
"sv-SE": "Fel som måste åtgärdas",
"da-DK": "Felter der skal rettes"
},
"Field.optionalLabelSuffix": {
"nb-NO": "(valgfritt)",
"en-GB": "(optional)",
"sv-SE": "(valfritt)",
"da-DK": "(valgfrit)"
},
"Field.stateSummary": {
"nb-NO": "Oppsummering:",
"en-GB": "Summary:",
"sv-SE": "Sammanfattning:",
"da-DK": "Oversigt:"
},
"PostalCode.errorPattern": {
"nb-NO": "Du må skrive inn et gyldig postnummer (fire siffer).",
"en-GB": "You must enter a valid postcode (4 digits).",
"sv-SE": "Du måste ange ett giltigt postnummer (fyra siffror).",
"da-DK": "Du skal indtaste et gyldigt postnummer (fire cifre)."
},
"PostalCode.errorRequired": {
"nb-NO": "Du må fylle inn et postnummer.",
"en-GB": "You must enter a postcode.",
"sv-SE": "Du måste fylla i ett postnummer.",
"da-DK": "Du skal udfylde et postnummer."
},
"PostalCode.label": {
"nb-NO": "Postnr.",
"en-GB": "Postcode",
"sv-SE": "Postnr.",
"da-DK": "Postnr."
}
}
}
```
## Events
```json
{
"props": {
"onChange": {
"doc": "Will be called on value changes made by the user, with the new value as argument. When an `async` function is used, the corresponding [FieldBlock](/uilib/extensions/forms/create-component/FieldBlock/) will show an indicator on the field label. You can return `{ success: 'saved' } as const` to show a success symbol, or an error or an object with these keys `{ info: 'Info message', warning: 'Warning message', error: Error('My error') } as const`. The second parameter is an object that e.g. contains `props` (all given `Field.*` properties).",
"type": "(value) => void",
"status": "optional"
},
"onFocus": {
"doc": "Will be called when the component gets into focus. Like clicking inside a text input or opening a dropdown. Called with active value as argument. The second parameter is an object that e.g. contains `props` (all given `Field.*` properties).",
"type": "(value) => void",
"status": "optional"
},
"onBlur": {
"doc": "Will be called when the component stop being in focus. Like when going to next field, or closing a dropdown. Called with active value as argument. The second parameter is an object that e.g. contains `props` (all given `Field.*` properties).",
"type": "(value) => void",
"status": "optional"
},
"onStatusChange": {
"doc": "Called whenever the status messages (info, warning or error) gets visible or changes. Receives the current `{ info, warning, error }` object.",
"type": "({ info?, warning?, error? }: FieldStatus) => void",
"status": "optional"
}
}
}
```