diff --git a/packages/css/src/components/field/README.md b/packages/css/src/components/field/README.md
new file mode 100644
index 0000000000..581cad51f1
--- /dev/null
+++ b/packages/css/src/components/field/README.md
@@ -0,0 +1,9 @@
+
+
+# Field
+
+Wraps a single input and its related elements. May indicate that the input has a validation error.
+
+## Guidelines
+
+Only use Field to wrap a single input. Use [Fieldset](/docs/components-forms-fieldset--docs) to wrap multiple inputs.
diff --git a/packages/css/src/components/field/field.scss b/packages/css/src/components/field/field.scss
new file mode 100644
index 0000000000..681d1f88ee
--- /dev/null
+++ b/packages/css/src/components/field/field.scss
@@ -0,0 +1,16 @@
+/**
+ * @license EUPL-1.2+
+ * Copyright Gemeente Amsterdam
+ */
+
+.ams-field {
+ break-inside: avoid;
+ display: flex;
+ flex-direction: column;
+ gap: var(--ams-field-gap);
+}
+
+.ams-field--invalid {
+ border-inline-start: var(--ams-field-invalid-border-inline-start);
+ padding-inline-start: var(--ams-field-invalid-padding-inline-start);
+}
diff --git a/packages/css/src/components/index.scss b/packages/css/src/components/index.scss
index 063a49713e..b95b00fbdd 100644
--- a/packages/css/src/components/index.scss
+++ b/packages/css/src/components/index.scss
@@ -4,6 +4,7 @@
*/
/* Append here */
+@import "./field/field";
@import "./select/select";
@import "./time-input/time-input";
@import "./date-input/date-input";
diff --git a/packages/react/src/Field/Field.test.tsx b/packages/react/src/Field/Field.test.tsx
new file mode 100644
index 0000000000..a2a5cf7577
--- /dev/null
+++ b/packages/react/src/Field/Field.test.tsx
@@ -0,0 +1,44 @@
+import { render } from '@testing-library/react'
+import { createRef } from 'react'
+import { Field } from './Field'
+import '@testing-library/jest-dom'
+
+describe('Field', () => {
+ it('renders', () => {
+ const { container } = render()
+ const component = container.querySelector(':only-child')
+
+ expect(component).toBeInTheDocument()
+ expect(component).toBeVisible()
+ })
+
+ it('renders a design system BEM class name', () => {
+ const { container } = render()
+ const component = container.querySelector(':only-child')
+
+ expect(component).toHaveClass('ams-field')
+ })
+
+ it('renders an additional class name', () => {
+ const { container } = render()
+ const component = container.querySelector(':only-child')
+
+ expect(component).toHaveClass('ams-field extra')
+ })
+
+ it('renders with the error class', () => {
+ const { container } = render()
+ const component = container.querySelector(':only-child')
+
+ expect(component).toHaveClass('ams-field--invalid')
+ })
+
+ it('supports ForwardRef in React', () => {
+ const ref = createRef()
+
+ const { container } = render()
+ const component = container.querySelector(':only-child')
+
+ expect(ref.current).toBe(component)
+ })
+})
diff --git a/packages/react/src/Field/Field.tsx b/packages/react/src/Field/Field.tsx
new file mode 100644
index 0000000000..e8404fbf10
--- /dev/null
+++ b/packages/react/src/Field/Field.tsx
@@ -0,0 +1,23 @@
+/**
+ * @license EUPL-1.2+
+ * Copyright Gemeente Amsterdam
+ */
+
+import clsx from 'clsx'
+import { forwardRef } from 'react'
+import type { ForwardedRef, HTMLAttributes, PropsWithChildren } from 'react'
+
+export type FieldProps = {
+ /** Whether the field has an input with a validation error */
+ invalid?: boolean
+} & PropsWithChildren>
+
+export const Field = forwardRef(
+ ({ children, className, invalid, ...restProps }: FieldProps, ref: ForwardedRef) => (
+
+ {children}
+
+ ),
+)
+
+Field.displayName = 'Field'
diff --git a/packages/react/src/Field/README.md b/packages/react/src/Field/README.md
new file mode 100644
index 0000000000..fb9b52624d
--- /dev/null
+++ b/packages/react/src/Field/README.md
@@ -0,0 +1,5 @@
+
+
+# React Field component
+
+[Field documentation](../../../css/src/components/field/README.md)
diff --git a/packages/react/src/Field/index.ts b/packages/react/src/Field/index.ts
new file mode 100644
index 0000000000..93ca84d382
--- /dev/null
+++ b/packages/react/src/Field/index.ts
@@ -0,0 +1,2 @@
+export { Field } from './Field'
+export type { FieldProps } from './Field'
diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts
index 70b5d5f6da..d6b0c721f6 100644
--- a/packages/react/src/index.ts
+++ b/packages/react/src/index.ts
@@ -4,6 +4,7 @@
*/
/* Append here */
+export * from './Field'
export * from './Select'
export * from './TimeInput'
export * from './DateInput'
diff --git a/proprietary/tokens/src/components/ams/field.tokens.json b/proprietary/tokens/src/components/ams/field.tokens.json
new file mode 100644
index 0000000000..350ba5caad
--- /dev/null
+++ b/proprietary/tokens/src/components/ams/field.tokens.json
@@ -0,0 +1,17 @@
+{
+ "ams": {
+ "field": {
+ "gap": {
+ "value": "{ams.space.stack.sm}"
+ },
+ "invalid": {
+ "border-inline-start": {
+ "value": "{ams.border.width.lg} solid {ams.color.primary-red}"
+ },
+ "padding-inline-start": {
+ "value": "{ams.space.inside.md}"
+ }
+ }
+ }
+ }
+}
diff --git a/storybook/src/components/Field/Field.docs.mdx b/storybook/src/components/Field/Field.docs.mdx
new file mode 100644
index 0000000000..b465ae15f5
--- /dev/null
+++ b/storybook/src/components/Field/Field.docs.mdx
@@ -0,0 +1,17 @@
+import { Canvas, Controls, Markdown, Meta, Primary } from "@storybook/blocks";
+import * as FieldStories from "./Field.stories.tsx";
+import README from "../../../../packages/css/src/components/field/README.md?raw";
+
+
+
+{README}
+
+
+
+
+
+## With Error
+
+A Field can indicate if the contained input has a validation error.
+
+
diff --git a/storybook/src/components/Field/Field.stories.tsx b/storybook/src/components/Field/Field.stories.tsx
new file mode 100644
index 0000000000..1eb2bb8480
--- /dev/null
+++ b/storybook/src/components/Field/Field.stories.tsx
@@ -0,0 +1,44 @@
+/**
+ * @license EUPL-1.2+
+ * Copyright Gemeente Amsterdam
+ */
+
+import { TextInput } from '@amsterdam/design-system-react'
+import { Field, Label, Paragraph } from '@amsterdam/design-system-react/src'
+import { Meta, StoryObj } from '@storybook/react'
+
+const meta = {
+ title: 'Components/Forms/Field',
+ component: Field,
+ args: {
+ invalid: false,
+ },
+ render: (args) => (
+
+
+
+ Typ geen persoonsgegevens in deze omschrijving. We vragen dit later in dit formulier aan u.
+
+
+
+ ),
+} satisfies Meta
+
+export default meta
+
+type Story = StoryObj
+
+export const Default: Story = {}
+
+export const WithError: Story = {
+ args: { invalid: true },
+ render: (args) => (
+
+
+
+ Typ geen persoonsgegevens in deze omschrijving. We vragen dit later in dit formulier aan u.
+
+
+
+ ),
+}