Component

Input group

<DomInputGroup>

A display-only frame that presents several independent form controls as one grouped input, followed by optional feedback and their errors.

Composition

Keep separate fields inside one visual control

The group only owns presentation. Whether controls come from a form definition or direct template markup, they still register with DomForm, so names, values, validators, and the emitted model shape stay unchanged. Set each field's chrome to false. Supporting UI such as DomPasswordStrength belongs in the group's feedback slot. Its descriptionMode can be set to tooltip to keep the group compact.

Form definition: email and password are declared as configuration children, including the named feedback slot.

<script setup>
import { ref } from 'vue';
import { DomButton, DomForm } from '@getdom/studio/vue';

const credentials = ref({
	email: '',
	password: '',
});

const credentialFields = [
	{
		id: 'credentials',
		component: 'DomInputGroup',
		props: {
			ariaLabel: 'Account credentials',
		},
		children: [
			{
				component: 'DomEmailInput',
				props: {
					name: 'email',
					label: 'Email',
					placeholder: 'you@example.com',
					chrome: false,
					required: true,
				},
			},
			{
				component: 'DomPasswordInput',
				props: {
					name: 'password',
					label: 'Password',
					placeholder: 'Enter your password',
					autocomplete: 'new-password',
					chrome: false,
					showStrength: false,
					required: true,
				},
			},
			{
				component: 'DomPasswordStrength',
				slot: 'feedback',
				props: {
					name: 'password',
					descriptionMode: 'tooltip',
				},
			},
		],
	},
];
</script>

<template>
	<DomForm v-model="credentials" :children="credentialFields" class="w-full max-w-md space-y-4">
		<DomButton type="submit" class="w-full">Create account</DomButton>
	</DomForm>
</template>

Template markup: the same controls are ordinary Vue children, and the strength meter uses #feedback.

<script setup>
import { ref } from 'vue';
import {
	DomButton,
	DomEmailInput,
	DomForm,
	DomInputGroup,
	DomPasswordInput,
	DomPasswordStrength,
} from '@getdom/studio/vue';

const credentials = ref({
	email: '',
	password: '',
});
</script>

<template>
	<DomForm v-model="credentials" class="w-full max-w-md space-y-4">
		<DomInputGroup aria-label="Account credentials">
			<DomEmailInput
				name="email"
				label="Email"
				placeholder="you@example.com"
				:chrome="false"
				required
			/>
			<DomPasswordInput
				name="password"
				label="Password"
				placeholder="Enter your password"
				autocomplete="new-password"
				:chrome="false"
				:show-strength="false"
				required
			/>

			<template #feedback>
				<DomPasswordStrength name="password" description-mode="tooltip" />
			</template>
		</DomInputGroup>

		<DomButton type="submit" class="w-full">Create account</DomButton>
	</DomForm>
</template>

Layout

Split a grouped row into columns

Declarative children can include layout elements as well as fields. This card example keeps the long card number on its own row, then uses a two-column child with a visible divider between expiry and CVC. Each value still registers independently and any validation messages render below the complete control.

Form definition: a declarative div creates the two-column expiry and CVC row.

Card information

<script setup>
import { ref } from 'vue';
import { DomButton, DomForm } from '@getdom/studio/vue';

const paymentCard = ref({
	cardNumber: '',
	expiry: '',
	cvc: '',
});

const paymentCardFields = [
	{
		id: 'card-information',
		component: 'DomInputGroup',
		props: {
			ariaLabel: 'Card information',
		},
		children: [
			{
				component: 'DomTextInput',
				props: {
					name: 'cardNumber',
					label: 'Card number',
					placeholder: '1234 1234 1234 1234',
					autocomplete: 'cc-number',
					chrome: false,
					required: true,
				},
			},
			{
				component: 'div',
				props: {
					class: 'grid grid-cols-2',
				},
				children: [
					{
						component: 'DomTextInput',
						props: {
							name: 'expiry',
							label: 'Expiry',
							placeholder: 'MM / YY',
							autocomplete: 'cc-exp',
							chrome: false,
							required: true,
						},
					},
					{
						component: 'div',
						props: {
							class: 'border-l border-border',
						},
						children: [
							{
								component: 'DomTextInput',
								props: {
									name: 'cvc',
									label: 'CVC',
									placeholder: 'CVC',
									autocomplete: 'cc-csc',
									chrome: false,
									required: true,
								},
							},
						],
					},
				],
			},
		],
	},
];
</script>

<template>
	<DomForm
		v-model="paymentCard"
		:children="paymentCardFields"
		label="Card information"
		class="w-full max-w-md space-y-4"
	>
		<DomButton type="submit" class="w-full">Continue</DomButton>
	</DomForm>
</template>

Template markup: an ordinary grid div creates the same second row while all three inputs remain independent form fields.

Card information

<script setup>
import { ref } from 'vue';
import {
	DomButton,
	DomForm,
	DomInputGroup,
	DomTextInput,
} from '@getdom/studio/vue';

const paymentCard = ref({
	cardNumber: '',
	expiry: '',
	cvc: '',
});
</script>

<template>
	<DomForm
		v-model="paymentCard"
		label="Card information"
		class="w-full max-w-md space-y-4"
	>
		<DomInputGroup aria-label="Card information">
			<DomTextInput
				name="cardNumber"
				label="Card number"
				placeholder="1234 1234 1234 1234"
				autocomplete="cc-number"
				:chrome="false"
				required
			/>

			<div class="grid grid-cols-2">
				<DomTextInput
					name="expiry"
					label="Expiry"
					placeholder="MM / YY"
					autocomplete="cc-exp"
					:chrome="false"
					required
				/>
				<div class="border-l border-border">
					<DomTextInput
						name="cvc"
						label="CVC"
						placeholder="CVC"
						autocomplete="cc-csc"
						:chrome="false"
						required
					/>
				</div>
			</div>
		</DomInputGroup>

		<DomButton type="submit" class="w-full">Continue</DomButton>
	</DomForm>
</template>

Reference

Props

Control props

NameTypeTSDefaultDescription
ariaLabelstringstring'Grouped form fields'Accessible name for the shared control frame.
descriptionstringstring''Optional helper text shown below the grouped errors.
errorsarray | object | stringArray<unknown>[]Optional group-level validation errors shown after child field errors.
errorLabelsbooleanbooleantruePrefix each child error with its field label so repeated messages remain distinguishable.
invalidbooleanbooleanfalseMark the shared frame invalid in addition to any child field state.
visiblebooleanbooleantrueShow or hide the complete input group.

Auto-generated from Input group.props and inline _edit hints.

Slots

NameScopeDescription
#(default)Form controls using chrome="false". Each control keeps its own model, name, validation, and form registration.
#feedbackSupporting content shown outside the shared frame and before validation errors.