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.
<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.
<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
| Name | Type | TS | Default | Description |
|---|---|---|---|---|
ariaLabel | string | string | 'Grouped form fields' | Accessible name for the shared control frame. |
description | string | string | '' | Optional helper text shown below the grouped errors. |
errors | array | object | string | Array<unknown> | [] | Optional group-level validation errors shown after child field errors. |
errorLabels | boolean | boolean | true | Prefix each child error with its field label so repeated messages remain distinguishable. |
invalid | boolean | boolean | false | Mark the shared frame invalid in addition to any child field state. |
visible | boolean | boolean | true | Show or hide the complete input group. |
Auto-generated from Input group.props and inline _edit hints.
Slots
| Name | Scope | Description |
|---|---|---|
| #(default) | — | Form controls using chrome="false". Each control keeps its own model, name, validation, and form registration. |
| #feedback | — | Supporting content shown outside the shared frame and before validation errors. |