SearchField
Search input field with clear button and search icon.
Import
ts
import {
SearchField,
SearchFieldClearButton,
SearchFieldGroup,
SearchFieldInput,
SearchFieldSearchIcon,
} from '@heroui-vue/vue'Usage
<template>
<SearchField name="search">
<Label>Search</Label>
<SearchFieldGroup>
<SearchFieldSearchIcon />
<SearchFieldInput class="demo-search-field-input" placeholder="Search..." />
<SearchFieldClearButton />
</SearchFieldGroup>
</SearchField>
</template>
<script setup lang="ts">
import { Label, SearchField, SearchFieldClearButton, SearchFieldGroup, SearchFieldInput, SearchFieldSearchIcon } from '@heroui-vue/vue'
</script>
<style lang="less">
.demo-search-field-input {
width: 17.5rem;
}
</style>Anatomy
vue
<SearchField>
<Label />
<SearchFieldGroup>
<SearchFieldSearchIcon />
<SearchFieldInput />
<SearchFieldClearButton />
</SearchFieldGroup>
<Description />
<FieldError />
</SearchField>With Description
Enter keywords to search for products
Search by name, email, or username
<template>
<div class="demo-search-field-stack">
<SearchField name="search">
<Label>Search products</Label>
<SearchFieldGroup>
<SearchFieldSearchIcon />
<SearchFieldInput class="demo-search-field-input" placeholder="Search products..." />
<SearchFieldClearButton />
</SearchFieldGroup>
<Description>Enter keywords to search for products</Description>
</SearchField>
<SearchField name="search-users">
<Label>Search users</Label>
<SearchFieldGroup>
<SearchFieldSearchIcon />
<SearchFieldInput class="demo-search-field-input" placeholder="Search users..." />
<SearchFieldClearButton />
</SearchFieldGroup>
<Description>Search by name, email, or username</Description>
</SearchField>
</div>
</template>
<script setup lang="ts">
import { Description, Label, SearchField, SearchFieldClearButton, SearchFieldGroup, SearchFieldInput, SearchFieldSearchIcon } from '@heroui-vue/vue'
</script>
<style lang="less">
.demo-search-field-stack {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 1rem;
}
.demo-search-field-input {
width: 17.5rem;
}
</style>Required Field
Minimum 3 characters required
<template>
<div class="demo-search-field-stack">
<SearchField name="search" is-required>
<Label>Search</Label>
<SearchFieldGroup>
<SearchFieldSearchIcon />
<SearchFieldInput class="demo-search-field-input" placeholder="Search..." />
<SearchFieldClearButton />
</SearchFieldGroup>
</SearchField>
<SearchField name="search-query" is-required>
<Label>Search query</Label>
<SearchFieldGroup>
<SearchFieldSearchIcon />
<SearchFieldInput class="demo-search-field-input" placeholder="Enter search query..." />
<SearchFieldClearButton />
</SearchFieldGroup>
<Description>Minimum 3 characters required</Description>
</SearchField>
</div>
</template>
<script setup lang="ts">
import { Description, Label, SearchField, SearchFieldClearButton, SearchFieldGroup, SearchFieldInput, SearchFieldSearchIcon } from '@heroui-vue/vue'
</script>
<style lang="less">
.demo-search-field-stack {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 1rem;
}
.demo-search-field-input {
width: 17.5rem;
}
</style>Validation
Search query must be at least 3 characters
Invalid characters in search query
<template>
<div class="demo-search-field-stack">
<SearchField name="search" value="ab" is-required is-invalid>
<Label>Search</Label>
<SearchFieldGroup>
<SearchFieldSearchIcon />
<SearchFieldInput class="demo-search-field-input" placeholder="Search..." />
<SearchFieldClearButton />
</SearchFieldGroup>
<FieldError>Search query must be at least 3 characters</FieldError>
</SearchField>
<SearchField name="search-invalid" value="invalid@query" is-invalid>
<Label>Search</Label>
<SearchFieldGroup>
<SearchFieldSearchIcon />
<SearchFieldInput class="demo-search-field-input" placeholder="Search..." />
<SearchFieldClearButton />
</SearchFieldGroup>
<FieldError>Invalid characters in search query</FieldError>
</SearchField>
</div>
</template>
<script setup lang="ts">
import { FieldError, Label, SearchField, SearchFieldClearButton, SearchFieldGroup, SearchFieldInput, SearchFieldSearchIcon } from '@heroui-vue/vue'
</script>
<style lang="less">
.demo-search-field-stack {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 1rem;
}
.demo-search-field-input {
width: 17.5rem;
}
</style>Disabled State
This search field is disabled
This search field is disabled
<template>
<div class="demo-search-field-stack">
<SearchField name="search" value="Disabled search" is-disabled>
<Label>Search</Label>
<SearchFieldGroup>
<SearchFieldSearchIcon />
<SearchFieldInput class="demo-search-field-input" placeholder="Search..." />
<SearchFieldClearButton />
</SearchFieldGroup>
<Description>This search field is disabled</Description>
</SearchField>
<SearchField name="search-empty" is-disabled>
<Label>Search</Label>
<SearchFieldGroup>
<SearchFieldSearchIcon />
<SearchFieldInput class="demo-search-field-input" placeholder="Search..." />
<SearchFieldClearButton />
</SearchFieldGroup>
<Description>This search field is disabled</Description>
</SearchField>
</div>
</template>
<script setup lang="ts">
import { Description, Label, SearchField, SearchFieldClearButton, SearchFieldGroup, SearchFieldInput, SearchFieldSearchIcon } from '@heroui-vue/vue'
</script>
<style lang="less">
.demo-search-field-stack {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 1rem;
}
.demo-search-field-input {
width: 17.5rem;
}
</style>Controlled
Current value: (empty)
<template>
<div class="demo-search-field-stack">
<SearchField v-model="value" name="search">
<Label>Search</Label>
<SearchFieldGroup>
<SearchFieldSearchIcon />
<SearchFieldInput class="demo-search-field-input" placeholder="Search..." />
<SearchFieldClearButton />
</SearchFieldGroup>
<Description>Current value: {{ value || '(empty)' }}</Description>
</SearchField>
<div class="demo-search-field-actions">
<Button variant="tertiary" @click="value = ''">Clear</Button>
<Button variant="tertiary" @click="value = 'example query'">Set example</Button>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { Button, Description, Label, SearchField, SearchFieldClearButton, SearchFieldGroup, SearchFieldInput, SearchFieldSearchIcon } from '@heroui-vue/vue'
const value = ref('')
</script>
<style lang="less">
.demo-search-field-stack {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 1rem;
}
.demo-search-field-input {
width: 17.5rem;
}
.demo-search-field-actions {
display: flex;
gap: 0.5rem;
}
</style>With Validation
Enter at least 3 characters to search
<template>
<SearchField v-model="value" name="search" is-required :is-invalid="isInvalid">
<Label>Search</Label>
<SearchFieldGroup>
<SearchFieldSearchIcon />
<SearchFieldInput class="demo-search-field-input" placeholder="Search..." />
<SearchFieldClearButton />
</SearchFieldGroup>
<FieldError v-if="isInvalid">Search query must be at least 3 characters</FieldError>
<Description v-else>Enter at least 3 characters to search</Description>
</SearchField>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { Description, FieldError, Label, SearchField, SearchFieldClearButton, SearchFieldGroup, SearchFieldInput, SearchFieldSearchIcon } from '@heroui-vue/vue'
const value = ref('')
const isInvalid = computed(() => value.value.length > 0 && value.value.length < 3)
</script>
<style lang="less">
.demo-search-field-input {
width: 17.5rem;
}
</style>Custom Icons
Custom icon children
<template>
<SearchField name="search-custom">
<Label>Search (Custom Icons)</Label>
<SearchFieldGroup>
<SearchFieldSearchIcon>
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
<path
clip-rule="evenodd"
d="M12.5 4c0 .174-.071.513-.885.888S9.538 5.5 8 5.5s-2.799-.237-3.615-.612C3.57 4.513 3.5 4.174 3.5 4s.071-.513.885-.888S6.462 2.5 8 2.5s2.799.237 3.615.612c.814.375.885.714.885.888m-1.448 2.66C10.158 6.888 9.115 7 8 7s-2.158-.113-3.052-.34l1.98 2.905c.21.308.322.672.322 1.044v3.37q.088.02.25.021c.422 0 .749-.14.95-.316c.185-.162.3-.38.3-.684v-2.39c0-.373.112-.737.322-1.045zM8 1c3.314 0 6 1 6 3a3.24 3.24 0 0 1-.563 1.826l-3.125 4.584a.35.35 0 0 0-.062.2V13c0 1.5-1.25 2.5-2.75 2.5s-1.75-1-1.75-1v-3.89a.35.35 0 0 0-.061-.2L2.563 5.826A3.24 3.24 0 0 1 2 4c0-2 2.686-3 6-3m-.88 12.936q-.015-.008-.013-.01z"
fill="currentColor"
fill-rule="evenodd"
/>
</svg>
</SearchFieldSearchIcon>
<SearchFieldInput class="demo-search-field-input" placeholder="Search..." />
<SearchFieldClearButton>
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
<path
clip-rule="evenodd"
d="M8 15A7 7 0 1 0 8 1a7 7 0 0 0 0 14M6.53 5.47a.75.75 0 0 0-1.06 1.06L6.94 8L5.47 9.47a.75.75 0 1 0 1.06 1.06L8 9.06l1.47 1.47a.75.75 0 1 0 1.06-1.06L9.06 8l1.47-1.47a.75.75 0 1 0-1.06-1.06L8 6.94z"
fill="currentColor"
fill-rule="evenodd"
/>
</svg>
</SearchFieldClearButton>
</SearchFieldGroup>
<Description>Custom icon children</Description>
</SearchField>
</template>
<script setup lang="ts">
import { Description, Label, SearchField, SearchFieldClearButton, SearchFieldGroup, SearchFieldInput, SearchFieldSearchIcon } from '@heroui-vue/vue'
</script>
<style lang="less">
.demo-search-field-input {
width: 17.5rem;
}
</style>Full Width
<template>
<div class="demo-search-field-width">
<SearchField full-width name="search">
<Label>Search</Label>
<SearchFieldGroup>
<SearchFieldSearchIcon />
<SearchFieldInput placeholder="Search..." />
<SearchFieldClearButton />
</SearchFieldGroup>
</SearchField>
</div>
</template>
<script setup lang="ts">
import { Label, SearchField, SearchFieldClearButton, SearchFieldGroup, SearchFieldInput, SearchFieldSearchIcon } from '@heroui-vue/vue'
</script>
<style lang="less">
.demo-search-field-width {
width: 25rem;
}
</style>Variants
<template>
<div class="demo-search-field-stack">
<SearchField name="primary-search" variant="primary">
<Label>Primary variant</Label>
<SearchFieldGroup>
<SearchFieldSearchIcon />
<SearchFieldInput class="demo-search-field-input" placeholder="Search..." />
<SearchFieldClearButton />
</SearchFieldGroup>
</SearchField>
<SearchField name="secondary-search" variant="secondary">
<Label>Secondary variant</Label>
<SearchFieldGroup>
<SearchFieldSearchIcon />
<SearchFieldInput class="demo-search-field-input" placeholder="Search..." />
<SearchFieldClearButton />
</SearchFieldGroup>
</SearchField>
</div>
</template>
<script setup lang="ts">
import { Label, SearchField, SearchFieldClearButton, SearchFieldGroup, SearchFieldInput, SearchFieldSearchIcon } from '@heroui-vue/vue'
</script>
<style lang="less">
.demo-search-field-stack {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 1rem;
}
.demo-search-field-input {
width: 17.5rem;
}
</style>In Surface
Use variant="secondary" when placing SearchField inside a Surface.
Enter keywords to search
Use filters to refine your search
<template>
<Surface class="demo-search-field-surface">
<SearchField name="search" variant="secondary">
<Label>Search</Label>
<SearchFieldGroup>
<SearchFieldSearchIcon />
<SearchFieldInput class="demo-search-field-full" placeholder="Search..." />
<SearchFieldClearButton />
</SearchFieldGroup>
<Description>Enter keywords to search</Description>
</SearchField>
<SearchField name="search-2" variant="secondary">
<Label>Advanced search</Label>
<SearchFieldGroup>
<SearchFieldSearchIcon />
<SearchFieldInput class="demo-search-field-full" placeholder="Advanced search..." />
<SearchFieldClearButton />
</SearchFieldGroup>
<Description>Use filters to refine your search</Description>
</SearchField>
</Surface>
</template>
<script setup lang="ts">
import { Description, Label, SearchField, SearchFieldClearButton, SearchFieldGroup, SearchFieldInput, SearchFieldSearchIcon, Surface } from '@heroui-vue/vue'
</script>
<style lang="less">
.demo-search-field-surface {
display: flex;
width: 100%;
max-width: 24rem;
flex-direction: column;
gap: 1rem;
border-radius: 1.5rem;
padding: 1.5rem;
}
.demo-search-field-full {
width: 100%;
}
</style>Form Example
<template>
<form class="demo-search-field-form" @submit.prevent="handleSubmit">
<SearchField v-model="value" name="search" is-required :is-invalid="isInvalid">
<Label>Search products</Label>
<SearchFieldGroup>
<SearchFieldSearchIcon />
<SearchFieldInput class="demo-search-field-full" placeholder="Search products..." />
<SearchFieldClearButton />
</SearchFieldGroup>
<FieldError v-if="isInvalid">Search query must be at least {{ minLength }} characters</FieldError>
<Description v-else>Enter at least {{ minLength }} characters to search</Description>
</SearchField>
<Button class="demo-search-field-full" :is-disabled="value.length < minLength" :is-pending="isSubmitting" type="submit" variant="primary">
<Spinner v-if="isSubmitting" color="current" size="sm" />
{{ isSubmitting ? 'Searching...' : 'Search' }}
</Button>
</form>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { Button, Description, FieldError, Label, SearchField, SearchFieldClearButton, SearchFieldGroup, SearchFieldInput, SearchFieldSearchIcon, Spinner } from '@heroui-vue/vue'
const value = ref('')
const isSubmitting = ref(false)
const minLength = 3
const isInvalid = computed(() => value.value.length > 0 && value.value.length < minLength)
const handleSubmit = () => {
if (value.value.length < minLength) return
isSubmitting.value = true
window.setTimeout(() => {
value.value = ''
isSubmitting.value = false
}, 1500)
}
</script>
<style lang="less">
.demo-search-field-form {
display: flex;
width: 17.5rem;
flex-direction: column;
align-items: flex-start;
gap: 1rem;
}
.demo-search-field-full {
width: 100%;
}
</style>With Keyboard Shortcut
Use keyboard shortcut to quickly focus this field
Press⇧Sto focus the search field
<template>
<div class="demo-search-field-stack">
<SearchField v-model="value" name="search">
<Label>Search</Label>
<SearchFieldGroup>
<SearchFieldSearchIcon />
<SearchFieldInput id="shortcut-search" class="demo-search-field-input" placeholder="Search..." />
<SearchFieldClearButton />
</SearchFieldGroup>
<Description>Use keyboard shortcut to quickly focus this field</Description>
</SearchField>
<div class="demo-search-field-shortcut">
<span>Press</span>
<Kbd :keys="['⇧']">S</Kbd>
<span>to focus the search field</span>
</div>
</div>
</template>
<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref } from 'vue'
import { Description, Kbd, Label, SearchField, SearchFieldClearButton, SearchFieldGroup, SearchFieldInput, SearchFieldSearchIcon } from '@heroui-vue/vue'
const value = ref('')
const handleKeydown = (event: KeyboardEvent) => {
if (event.shiftKey && event.key === 'S' && !event.metaKey && !event.ctrlKey && !event.altKey) {
event.preventDefault()
document.getElementById('shortcut-search')?.focus()
}
if (event.key === 'Escape' && document.activeElement?.id === 'shortcut-search') {
;(document.activeElement as HTMLElement).blur()
}
}
onMounted(() => window.addEventListener('keydown', handleKeydown))
onBeforeUnmount(() => window.removeEventListener('keydown', handleKeydown))
</script>
<style lang="less">
.demo-search-field-stack {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 1rem;
}
.demo-search-field-input {
width: 17.5rem;
}
.demo-search-field-shortcut {
display: flex;
align-items: center;
gap: 0.5rem;
color: var(--color-muted);
font-size: 0.875rem;
}
</style>Related Components
Custom Render Function
<template>
<SearchField name="search" data-custom="foo">
<Label>Search</Label>
<SearchFieldGroup>
<SearchFieldSearchIcon />
<SearchFieldInput class="demo-search-field-input" placeholder="Search..." />
<SearchFieldClearButton />
</SearchFieldGroup>
</SearchField>
</template>
<script setup lang="ts">
import { Label, SearchField, SearchFieldClearButton, SearchFieldGroup, SearchFieldInput, SearchFieldSearchIcon } from '@heroui-vue/vue'
</script>
<style lang="less">
.demo-search-field-input {
width: 17.5rem;
}
</style>Styling
Use component props for state and variant behavior first. Demo-only layout styles are included in each demo source.
API
| Prop | Type | Default | Description |
|---|---|---|---|
modelValue / value | string | undefined | Controlled value |
defaultValue | string | '' | Initial uncontrolled value |
fullWidth | boolean | false | Makes the field width fill its container |
variant | 'primary' | 'secondary' | 'primary' | Visual variant |
disabled / isDisabled | boolean | false | Disables the search field |
required / isRequired | boolean | false | Marks the input required |
isInvalid | boolean | false | Applies invalid state |
name | string | undefined | Form field name |
| Event | Payload | Description |
|---|---|---|
update:modelValue | string | Emitted when the value changes |
change | string | Emitted when the value changes |
clear | void | Emitted when clear button is pressed |
submit | string | Emitted when Enter is pressed |