Skip to main content
Nexus Design System
GitHub

InputGroup

InputGroup frames an input or textarea with icons, text, buttons or key hints in one field. Use it for search boxes, URL prefixes and inline actions. For a plain field, use Input.

'use client';

import { MagnifyingGlassIcon } from '@phosphor-icons/react/ssr';

import {
  InputGroup,
  InputGroupAddon,
  InputGroupInput,
} from '@/components/nxds/components/input-group/input-group';

export default function InputGroupDefault() {
  return (
    <InputGroup className="nx:w-80">
      <InputGroupAddon>
        <MagnifyingGlassIcon aria-hidden />
      </InputGroupAddon>
      <InputGroupInput aria-label="Search" placeholder="Search…" />
    </InputGroup>
  );
}

Installation

npx nxds-cli@latest add input-group

Run npx nxds-cli@latest init first if nxds isn’t set up in your app yet. See Install.

Usage

import {
  InputGroup,
  InputGroupAddon,
  InputGroupButton,
  InputGroupInput,
  InputGroupText,
  InputGroupTextarea,
} from '@/components/nxds/components/input-group/input-group';

Composition

InputGroup
├── InputGroupAddon
│   ├── InputGroupButton
│   └── InputGroupText
├── InputGroupInput
└── InputGroupTextarea

Examples

With button

Add a trailing button for an action on the field's value.

'use client';

import {
  InputGroup,
  InputGroupAddon,
  InputGroupButton,
  InputGroupInput,
} from '@/components/nxds/components/input-group/input-group';

export default function InputGroupWithButton() {
  return (
    <InputGroup className="nx:w-80">
      <InputGroupInput aria-label="Email" placeholder="you@example.com" />
      <InputGroupAddon align="inline-end">
        <InputGroupButton>Subscribe</InputGroupButton>
      </InputGroupAddon>
    </InputGroup>
  );
}

With text

Show a fixed text prefix, such as a URL scheme, before the input.

https://
'use client';

import {
  InputGroup,
  InputGroupAddon,
  InputGroupInput,
  InputGroupText,
} from '@/components/nxds/components/input-group/input-group';

export default function InputGroupWithText() {
  return (
    <InputGroup className="nx:w-80">
      <InputGroupAddon>
        <InputGroupText>https://</InputGroupText>
      </InputGroupAddon>
      <InputGroupInput aria-label="Site URL" placeholder="nxds.dev" />
    </InputGroup>
  );
}

Alignments

Place addons at the start, end, top or bottom of the field.

Bio
0 / 200
'use client';

import { EnvelopeIcon, XIcon } from '@phosphor-icons/react/ssr';

import {
  InputGroup,
  InputGroupAddon,
  InputGroupButton,
  InputGroupInput,
  InputGroupText,
} from '@/components/nxds/components/input-group/input-group';

export default function InputGroupAlignments() {
  return (
    <div className="nx:flex nx:w-80 nx:flex-col nx:gap-3">
      <InputGroup>
        <InputGroupAddon align="inline-start">
          <EnvelopeIcon aria-hidden />
        </InputGroupAddon>
        <InputGroupInput aria-label="Email" placeholder="you@example.com" />
      </InputGroup>
      <InputGroup>
        <InputGroupInput aria-label="Search" placeholder="Search projects" />
        <InputGroupAddon align="inline-end">
          <InputGroupButton size="icon-xs" aria-label="Clear">
            <XIcon aria-hidden />
          </InputGroupButton>
        </InputGroupAddon>
      </InputGroup>
      <InputGroup>
        <InputGroupAddon align="block-start">
          <InputGroupText>Bio</InputGroupText>
        </InputGroupAddon>
        <InputGroupInput aria-label="Bio" placeholder="A few words about you" />
      </InputGroup>
      <InputGroup>
        <InputGroupInput
          aria-label="Status"
          placeholder="What are you working on?"
        />
        <InputGroupAddon align="block-end">
          <InputGroupText>0 / 200</InputGroupText>
        </InputGroupAddon>
      </InputGroup>
    </div>
  );
}

With textarea

Pair a textarea with a footer addon below it.

Markdown supported
'use client';

import {
  InputGroup,
  InputGroupAddon,
  InputGroupText,
  InputGroupTextarea,
} from '@/components/nxds/components/input-group/input-group';

export default function InputGroupWithTextarea() {
  return (
    <InputGroup className="nx:w-80">
      <InputGroupTextarea aria-label="Message" placeholder="Your message…" />
      <InputGroupAddon align="block-end">
        <InputGroupText>Markdown supported</InputGroupText>
      </InputGroupAddon>
    </InputGroup>
  );
}

Disabled

Set data-disabled="true" on the group to dim its addons along with the disabled control.

'use client';

import {
  InputGroup,
  InputGroupAddon,
  InputGroupButton,
  InputGroupInput,
} from '@/components/nxds/components/input-group/input-group';

export default function InputGroupDisabled() {
  return (
    <InputGroup className="nx:w-80" data-disabled="true">
      <InputGroupInput
        aria-label="Email"
        placeholder="you@example.com"
        disabled
      />
      <InputGroupAddon align="inline-end">
        <InputGroupButton disabled>Subscribe</InputGroupButton>
      </InputGroupAddon>
    </InputGroup>
  );
}

Sizes

Match the height of a standalone Input at each control size.

'use client';

import { MagnifyingGlassIcon } from '@phosphor-icons/react/ssr';

import {
  InputGroup,
  InputGroupAddon,
  InputGroupInput,
} from '@/components/nxds/components/input-group/input-group';

export default function InputGroupSizes() {
  return (
    <div className="nx:flex nx:w-80 nx:flex-col nx:gap-3">
      <InputGroup>
        <InputGroupAddon>
          <MagnifyingGlassIcon aria-hidden />
        </InputGroupAddon>
        <InputGroupInput
          size="sm"
          aria-label="Search, small"
          placeholder="Search"
        />
      </InputGroup>
      <InputGroup>
        <InputGroupAddon>
          <MagnifyingGlassIcon aria-hidden />
        </InputGroupAddon>
        <InputGroupInput aria-label="Search, default" placeholder="Search" />
      </InputGroup>
      <InputGroup>
        <InputGroupAddon>
          <MagnifyingGlassIcon aria-hidden />
        </InputGroupAddon>
        <InputGroupInput
          size="lg"
          aria-label="Search, large"
          placeholder="Search"
        />
      </InputGroup>
    </div>
  );
}

Props

InputGroup

InputGroup props
PropTypeDefaultDescription
variant"borderless" | "bordered" | null"bordered"No description

InputGroupAddon

InputGroupAddon props
PropTypeDefaultDescription
align"inline-start" | "inline-end" | "block-start" | "block-end" | null"inline-start"No description

InputGroupButton

InputGroupButton props
PropTypeDefaultDescription
asChildbooleanfalse

Renders the child, such as an <a>, with button styles. The loading spinner and icon slots then don't render.

endIconReactNodeNo default documented

Decorative icon after the label. Use endIcon or startIcon, not both.

loadingbooleanfalse

Disables the button and shows a spinner in place of its content.

size"sm" | "icon-sm" | "xs" | "icon-xs" | null"xs"No description
startIconReactNodeNo default documented

Decorative icon before the label. Use startIcon or endIcon, not both.

variant"link" | "default" | "error" | "destructive" | "outline" | "dashed" | "secondary" | "ghost" | null"ghost"No description

InputGroupInput

InputGroupInput props
PropTypeDefaultDescription
size"default" | "sm" | "lg" | null"default"No description

InputGroupText

InputGroupText adds no props of its own; it accepts the props of the element or primitive it wraps.

InputGroupTextarea

InputGroupTextarea adds no props of its own; it accepts the props of the element or primitive it wraps.

InputGroup frames an input or textarea with icons, text, buttons or key hints in one field. Use it for search boxes, URL prefixes and inline actions. For a plain field, use Input.

<script setup lang="ts">
import { PhMagnifyingGlass } from '@phosphor-icons/vue';

import {
  InputGroup,
  InputGroupAddon,
  InputGroupInput,
} from '@/components/input-group';
</script>

<template>
  <InputGroup class="nx:w-80">
    <InputGroupAddon>
      <PhMagnifyingGlass aria-hidden="true" />
    </InputGroupAddon>
    <InputGroupInput aria-label="Search" placeholder="Search…" />
  </InputGroup>
</template>

Installation

  1. Install these components first:

    Button, ButtonGroup, Input, Spinner, Textarea.

  2. Copy and paste the following code into your project.

    components/input-group/InputGroup.vue
    <script setup lang="ts">
    import type { HTMLAttributes } from 'vue';
    
    import { cn } from '../../lib/utils';
    
    import { type InputGroupVariants, inputGroupVariants } from '.';
    
    export interface InputGroupProps {
      variant?: InputGroupVariants['variant'];
      class?: HTMLAttributes['class'];
    }
    
    const props = withDefaults(defineProps<InputGroupProps>(), {
      variant: 'bordered',
    });
    </script>
    
    <template>
      <div
        data-slot="input-group"
        :data-variant="variant"
        role="group"
        :class="cn(inputGroupVariants({ variant }), props.class)"
      >
        <slot />
      </div>
    </template>
    
    components/input-group/InputGroupAddon.vue
    <script setup lang="ts">
    import type { HTMLAttributes } from 'vue';
    
    import { cn } from '../../lib/utils';
    
    import { type InputGroupAddonVariants, inputGroupAddonVariants } from '.';
    
    export interface InputGroupAddonProps {
      align?: InputGroupAddonVariants['align'];
      class?: HTMLAttributes['class'];
    }
    
    const props = withDefaults(defineProps<InputGroupAddonProps>(), {
      align: 'inline-start',
    });
    </script>
    
    <template>
      <div
        role="group"
        data-slot="input-group-addon"
        :data-align="align"
        :class="cn(inputGroupAddonVariants({ align }), props.class)"
      >
        <slot />
      </div>
    </template>
    
    components/input-group/InputGroupButton.vue
    <script setup lang="ts">
    import { reactiveOmit } from '@vueuse/core';
    import type { VNode } from 'vue';
    
    import { cn } from '../../lib/utils';
    import { Button, type ButtonProps } from '../button';
    
    import { type InputGroupButtonVariants, inputGroupButtonVariants } from '.';
    
    export interface InputGroupButtonProps extends Omit<ButtonProps, 'size'> {
      size?: InputGroupButtonVariants['size'];
    }
    
    const props = withDefaults(defineProps<InputGroupButtonProps>(), {
      as: 'button',
      variant: 'ghost',
      size: 'xs',
      loading: false,
      type: 'button',
    });
    
    const slots = defineSlots<{
      default?: () => VNode[];
      'start-icon'?: () => VNode[];
      'end-icon'?: () => VNode[];
    }>();
    
    const delegated = reactiveOmit(props, 'class', 'size');
    </script>
    
    <template>
      <Button
        v-bind="delegated"
        data-slot="input-group-button"
        :data-size="size"
        :class="cn(inputGroupButtonVariants({ size }), props.class)"
      >
        <template v-if="slots['start-icon']" #start-icon>
          <slot name="start-icon" />
        </template>
        <slot />
        <template v-if="slots['end-icon']" #end-icon>
          <slot name="end-icon" />
        </template>
      </Button>
    </template>
    
    components/input-group/InputGroupInput.vue
    <script setup lang="ts">
    import { reactiveOmit } from '@vueuse/core';
    import { useForwardPropsEmits } from 'reka-ui';
    import type { HTMLAttributes } from 'vue';
    
    import { cn } from '../../lib/utils';
    import { Input, type InputVariants } from '../input';
    
    export interface InputGroupInputProps {
      size?: InputVariants['size'];
      /** The value, for `v-model`. */
      modelValue?: string | number;
      /** The starting value when `v-model` isn't bound. */
      defaultValue?: string | number;
      class?: HTMLAttributes['class'];
    }
    
    const props = defineProps<InputGroupInputProps>();
    const emits = defineEmits<{
      'update:modelValue': [value: string | number];
    }>();
    
    const delegated = reactiveOmit(props, 'class');
    const forwarded = useForwardPropsEmits(delegated, emits);
    </script>
    
    <template>
      <Input
        v-bind="forwarded"
        data-slot="input-group-control"
        :class="
          cn(
            'nx:flex-1 nx:rounded-none nx:border-0 nx:focus-visible:outline-none',
            'nx:bg-transparent nx:enabled:hover:bg-transparent nx:disabled:bg-transparent',
            'nx:autofill-bg-transparent nx:enabled:hover:autofill-bg-transparent nx:disabled:autofill-bg-transparent',
            props.class
          )
        "
      />
    </template>
    
    components/input-group/InputGroupText.vue
    <script setup lang="ts">
    import type { HTMLAttributes } from 'vue';
    
    import { cn } from '../../lib/utils';
    
    export interface InputGroupTextProps {
      class?: HTMLAttributes['class'];
    }
    
    const props = defineProps<InputGroupTextProps>();
    </script>
    
    <template>
      <span
        data-slot="input-group-text"
        :class="
          cn(
            'nx:flex nx:items-center nx:gap-2 nx:typography-body-default nx:text-muted-foreground nx:group-data-[disabled=true]/input-group:text-disabled-foreground nx:[&_svg]:pointer-events-none nx:[&_svg]:size-4',
            props.class
          )
        "
      >
        <slot />
      </span>
    </template>
    
    components/input-group/InputGroupTextarea.vue
    <script setup lang="ts">
    import { reactiveOmit } from '@vueuse/core';
    import { useForwardPropsEmits } from 'reka-ui';
    import type { HTMLAttributes } from 'vue';
    
    import { cn } from '../../lib/utils';
    import { Textarea } from '../textarea';
    
    export interface InputGroupTextareaProps {
      /** The value, for `v-model`. */
      modelValue?: string;
      /** The starting value when `v-model` isn't bound. */
      defaultValue?: string;
      class?: HTMLAttributes['class'];
    }
    
    const props = defineProps<InputGroupTextareaProps>();
    const emits = defineEmits<{
      'update:modelValue': [value: string];
    }>();
    
    const delegated = reactiveOmit(props, 'class');
    const forwarded = useForwardPropsEmits(delegated, emits);
    </script>
    
    <template>
      <Textarea
        v-bind="forwarded"
        data-slot="input-group-control"
        :class="
          cn(
            'nx:flex-1 nx:resize-none nx:rounded-none nx:border-0 nx:py-3 nx:focus-visible:outline-none',
            'nx:bg-transparent nx:enabled:hover:bg-transparent nx:disabled:bg-transparent',
            'nx:autofill-bg-transparent nx:enabled:hover:autofill-bg-transparent nx:disabled:autofill-bg-transparent',
            props.class
          )
        "
      />
    </template>
    
    components/input-group/index.ts
    import { cva, type VariantProps } from 'class-variance-authority';
    
    export { default as InputGroup, type InputGroupProps } from './InputGroup.vue';
    export {
      default as InputGroupAddon,
      type InputGroupAddonProps,
    } from './InputGroupAddon.vue';
    export {
      default as InputGroupButton,
      type InputGroupButtonProps,
    } from './InputGroupButton.vue';
    export {
      default as InputGroupInput,
      type InputGroupInputProps,
    } from './InputGroupInput.vue';
    export {
      default as InputGroupText,
      type InputGroupTextProps,
    } from './InputGroupText.vue';
    export {
      default as InputGroupTextarea,
      type InputGroupTextareaProps,
    } from './InputGroupTextarea.vue';
    
    export const inputGroupVariants = cva(
      [
        'nx:group/input-group nx:relative nx:flex nx:box-border nx:w-full nx:min-w-0 nx:items-center nx:rounded-md nx:border-default nx:transition-field',
        // Size: an inline group matches standalone Input's height for the
        // control's data-size. `not-has-[>[data-align^=block]]` scopes this to
        // non-stacked layouts (no block addon) so the fixed-height rule and the
        // auto-height stacked case are mutually exclusive — they never both
        // match.
        'nx:not-has-[>[data-align^=block]]:has-[[data-slot=input-group-control][data-size=sm]]:h-8',
        'nx:not-has-[>[data-align^=block]]:has-[[data-slot=input-group-control][data-size=default]]:h-10',
        'nx:not-has-[>[data-align^=block]]:has-[[data-slot=input-group-control][data-size=lg]]:h-12',
        // The control fills the fixed-height frame without adding height (the
        // compound :has() selector outranks Input's own h-* by specificity).
        // Released in the stacked case, where the control keeps its own height.
        'nx:not-has-[>[data-align^=block]]:[&>input]:h-full',
        // Alignment: addons push the control's padding to make room.
        'nx:has-[>[data-align=inline-start]]:[&>input]:pl-2',
        'nx:has-[>[data-align=inline-end]]:[&>input]:pr-2',
        'nx:has-[>[data-align=block-start]]:flex-col nx:has-[>[data-align=block-start]]:[&>input]:pb-3',
        'nx:has-[>[data-align=block-end]]:flex-col nx:has-[>[data-align=block-end]]:[&>input]:pt-3',
        'nx:data-[disabled=true]:cursor-not-allowed nx:data-[disabled=true]:bg-disabled',
        // Focus: the group shows the ring when the inner control is focused
        // (the control suppresses its own outline).
        'nx:has-[[data-slot=input-group-control]:focus-visible]:outline-default nx:has-[[data-slot=input-group-control]:focus-visible]:outline-focus-default nx:has-[[data-slot=input-group-control]:focus-visible]:border-focus-default',
        // Error: an invalid control reddens the border; an invalid focused
        // control switches the ring to the error colour (matches Input).
        'nx:has-[[data-slot][aria-invalid=true]]:border-error-border',
        'nx:has-[[data-slot=input-group-control][aria-invalid=true]:focus-visible]:outline-focus-error nx:has-[[data-slot=input-group-control][aria-invalid=true]:focus-visible]:border-focus-error',
      ],
      {
        variants: {
          variant: {
            bordered:
              'nx:border-border-default nx:bg-container nx:not-data-[disabled=true]:hover:bg-container-hover nx:data-[disabled=true]:border-border-disabled',
            borderless:
              'nx:border-transparent nx:bg-control-background nx:not-data-[disabled=true]:hover:bg-control-background-hover',
          },
        },
        defaultVariants: {
          variant: 'bordered',
        },
      }
    );
    
    export const inputGroupAddonVariants = cva(
      'nx:flex nx:h-auto nx:cursor-text nx:items-center nx:justify-start nx:gap-2 nx:py-1.5 nx:typography-label-default nx:text-muted-foreground nx:select-none nx:group-data-[disabled=true]/input-group:text-disabled-foreground nx:[&>kbd]:rounded-sm nx:[&>svg]:size-4',
      {
        variants: {
          align: {
            'inline-start':
              'nx:order-first nx:pl-3 nx:has-[>button]:-ml-2 nx:has-[>kbd]:-ml-1.5',
            'inline-end':
              'nx:order-last nx:pr-3 nx:has-[>button]:-mr-2 nx:has-[>kbd]:-mr-1.5',
            'block-start': 'nx:order-first nx:w-full nx:px-3 nx:pt-3',
            'block-end': 'nx:order-last nx:w-full nx:px-3 nx:pb-3',
          },
        },
        defaultVariants: {
          align: 'inline-start',
        },
      }
    );
    
    export const inputGroupButtonVariants = cva(
      'nx:flex nx:items-center nx:py-0 nx:typography-label-default nx:shadow-none nx:group-data-[disabled=true]/input-group:text-disabled-foreground',
      {
        variants: {
          size: {
            xs: 'nx:h-6 nx:gap-1 nx:rounded-sm nx:px-2 nx:has-[>svg]:px-2 nx:[&>svg]:size-3.5',
            sm: 'nx:h-8 nx:gap-1.5 nx:rounded-md nx:px-2.5 nx:has-[>svg]:px-2.5',
            'icon-xs': 'nx:size-6 nx:rounded-sm nx:p-0 nx:has-[>svg]:p-0',
            'icon-sm': 'nx:size-8 nx:p-0 nx:has-[>svg]:p-0',
          },
        },
        defaultVariants: {
          size: 'xs',
        },
      }
    );
    
    export type InputGroupVariants = VariantProps<typeof inputGroupVariants>;
    export type InputGroupAddonVariants = VariantProps<
      typeof inputGroupAddonVariants
    >;
    export type InputGroupButtonVariants = VariantProps<
      typeof inputGroupButtonVariants
    >;
    
  3. Keep each file at the path shown above it.

    Paths are relative to packages/vue/src/. Keep the same folders under your @/ root, and the imports between files work without edits.

Usage

import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea } from '@/components/input-group';

Examples

With button

Add a trailing button for an action on the field's value.

<script setup lang="ts">
import {
  InputGroup,
  InputGroupAddon,
  InputGroupButton,
  InputGroupInput,
} from '@/components/input-group';
</script>

<template>
  <InputGroup class="nx:w-80">
    <InputGroupInput aria-label="Email" placeholder="you@example.com" />
    <InputGroupAddon align="inline-end">
      <InputGroupButton>Subscribe</InputGroupButton>
    </InputGroupAddon>
  </InputGroup>
</template>

With text

Show a fixed text prefix, such as a URL scheme, before the input.

<script setup lang="ts">
import {
  InputGroup,
  InputGroupAddon,
  InputGroupInput,
  InputGroupText,
} from '@/components/input-group';
</script>

<template>
  <InputGroup class="nx:w-80">
    <InputGroupAddon>
      <InputGroupText>https://</InputGroupText>
    </InputGroupAddon>
    <InputGroupInput aria-label="Site URL" placeholder="nxds.dev" />
  </InputGroup>
</template>

Alignments

Place addons at the start, end, top or bottom of the field.

<script setup lang="ts">
import { PhEnvelope, PhX } from '@phosphor-icons/vue';

import {
  InputGroup,
  InputGroupAddon,
  InputGroupButton,
  InputGroupInput,
  InputGroupText,
} from '@/components/input-group';
</script>

<template>
  <div class="nx:flex nx:w-80 nx:flex-col nx:gap-3">
    <InputGroup>
      <InputGroupAddon align="inline-start">
        <PhEnvelope aria-hidden="true" />
      </InputGroupAddon>
      <InputGroupInput aria-label="Email" placeholder="you@example.com" />
    </InputGroup>
    <InputGroup>
      <InputGroupInput aria-label="Search" placeholder="Search projects" />
      <InputGroupAddon align="inline-end">
        <InputGroupButton size="icon-xs" aria-label="Clear">
          <PhX aria-hidden="true" />
        </InputGroupButton>
      </InputGroupAddon>
    </InputGroup>
    <InputGroup>
      <InputGroupAddon align="block-start">
        <InputGroupText>Bio</InputGroupText>
      </InputGroupAddon>
      <InputGroupInput aria-label="Bio" placeholder="A few words about you" />
    </InputGroup>
    <InputGroup>
      <InputGroupInput
        aria-label="Status"
        placeholder="What are you working on?"
      />
      <InputGroupAddon align="block-end">
        <InputGroupText>0 / 200</InputGroupText>
      </InputGroupAddon>
    </InputGroup>
  </div>
</template>

With textarea

Pair a textarea with a footer addon below it.

<script setup lang="ts">
import {
  InputGroup,
  InputGroupAddon,
  InputGroupText,
  InputGroupTextarea,
} from '@/components/input-group';
</script>

<template>
  <InputGroup class="nx:w-80">
    <InputGroupTextarea aria-label="Message" placeholder="Your message…" />
    <InputGroupAddon align="block-end">
      <InputGroupText>Markdown supported</InputGroupText>
    </InputGroupAddon>
  </InputGroup>
</template>

Disabled

Set data-disabled="true" on the group to dim its addons along with the disabled control.

<script setup lang="ts">
import {
  InputGroup,
  InputGroupAddon,
  InputGroupButton,
  InputGroupInput,
} from '@/components/input-group';
</script>

<template>
  <InputGroup class="nx:w-80" data-disabled="true">
    <InputGroupInput
      aria-label="Email"
      placeholder="you@example.com"
      disabled
    />
    <InputGroupAddon align="inline-end">
      <InputGroupButton disabled>Subscribe</InputGroupButton>
    </InputGroupAddon>
  </InputGroup>
</template>

Sizes

Match the height of a standalone Input at each control size.

<script setup lang="ts">
import { PhMagnifyingGlass } from '@phosphor-icons/vue';

import {
  InputGroup,
  InputGroupAddon,
  InputGroupInput,
} from '@/components/input-group';
</script>

<template>
  <div class="nx:flex nx:w-80 nx:flex-col nx:gap-3">
    <InputGroup>
      <InputGroupAddon>
        <PhMagnifyingGlass aria-hidden="true" />
      </InputGroupAddon>
      <InputGroupInput
        size="sm"
        aria-label="Search, small"
        placeholder="Search"
      />
    </InputGroup>
    <InputGroup>
      <InputGroupAddon>
        <PhMagnifyingGlass aria-hidden="true" />
      </InputGroupAddon>
      <InputGroupInput aria-label="Search, default" placeholder="Search" />
    </InputGroup>
    <InputGroup>
      <InputGroupAddon>
        <PhMagnifyingGlass aria-hidden="true" />
      </InputGroupAddon>
      <InputGroupInput
        size="lg"
        aria-label="Search, large"
        placeholder="Search"
      />
    </InputGroup>
  </div>
</template>

Props

InputGroup

InputGroup props
PropTypeDefaultDescription
variant"bordered" | "borderless" | null"bordered"No description

InputGroupAddon

InputGroupAddon props
PropTypeDefaultDescription
align"inline-start" | "inline-end" | "block-start" | "block-end" | null"inline-start"No description

InputGroupButton

InputGroupButton props
PropTypeDefaultDescription
disabledbooleanNo default documentedNo description
loadingbooleanfalse

Disables the button and shows a spinner in place of its content.

size"sm" | "icon-sm" | "xs" | "icon-xs" | null"xs"No description
type"button" | "submit" | "reset""button"No description
variant"default" | "destructive" | "error" | "outline" | "dashed" | "secondary" | "ghost" | "link" | null"ghost"No description

InputGroupInput

InputGroupInput props
PropTypeDefaultDescription
defaultValuestring | numberNo default documented

The starting value when v-model isn't bound.

modelValuestring | numberNo default documented

The value, for v-model.

size"default" | "sm" | "lg" | null"default"No description

InputGroupText

InputGroupText adds no props of its own; it accepts the props of the element or primitive it wraps.

InputGroupTextarea

InputGroupTextarea props
PropTypeDefaultDescription
defaultValuestringNo default documented

The starting value when v-model isn't bound.

modelValuestringNo default documented

The value, for v-model.