SearchInput

A field for entering a search query.

<script setup lang="ts">
  import { ref } from 'vue'
  import { SearchInput } from '@hina-ui/vue'

  const query = ref('Spice and Wolf')
</script>

<template>
  <SearchInput v-model="query" aria-label="Search" placeholder="Search works" class="w-72" />
</template>
vue

Usage

import { SearchInput } from '@hina-ui/vue'
ts

The field starts with a search icon and shows a clear button at the end once it has content, with v-model bound to the query. Enter emits search with the current value and Esc clears it. Attributes it does not declare land on the input.

Last submitted: none

<script setup lang="ts">
  import { ref } from 'vue'
  import { SearchInput, Stack, Text } from '@hina-ui/vue'

  const query = ref('')
  const submitted = ref('')
</script>

<template>
  <Stack class="w-72">
    <SearchInput
      v-model="query"
      aria-label="Search"
      placeholder="Search works"
      @search="submitted = $event"
    />
    <Text tone="muted">Last submitted: {{ submitted || 'none' }}</Text>
  </Stack>
</template>
vue

Examples

Loading

loading swaps the search icon for a spinner while a search request is in flight.

<script setup lang="ts">
  import { SearchInput } from '@hina-ui/vue'
</script>

<template>
  <SearchInput model-value="Spice" loading aria-label="Search" class="w-72" />
</template>
vue

Sizes

Three sizes, matching the input.

<script setup lang="ts">
  import { SearchInput, Stack } from '@hina-ui/vue'
</script>

<template>
  <Stack class="w-72">
    <SearchInput size="sm" model-value="Spice" aria-label="Small" />
    <SearchInput size="md" model-value="Spice" aria-label="Medium" />
    <SearchInput size="lg" model-value="Spice" aria-label="Large" />
  </Stack>
</template>
vue

Variants

primary has a border, background and shadow; secondary uses a tinted background; bare is transparent, without a border, shadow, hover fill or container focus ring. Size and padding keep their existing settings.

bare preserves the disabled state and aria-invalid; FormField can display the error message. Action buttons keep their own hover and keyboard focus feedback.

<script setup lang="ts">
  import { SearchInput, Stack } from '@hina-ui/vue'
</script>

<template>
  <Stack class="w-full max-w-xs">
    <SearchInput variant="primary" aria-label="primary" placeholder="primary" />
    <SearchInput variant="secondary" aria-label="secondary" placeholder="secondary" />
    <SearchInput variant="bare" aria-label="bare" placeholder="bare" />
  </Stack>
</template>
vue

States

disabled prevents editing. With clearable off the clear button is never shown.

<script setup lang="ts">
  import { SearchInput, Stack } from '@hina-ui/vue'
</script>

<template>
  <Stack class="w-72">
    <SearchInput disabled model-value="Spice" aria-label="Disabled" />
    <SearchInput :clearable="false" model-value="Spice" aria-label="Without clear button" />
  </Stack>
</template>
vue

In a form

Inside a FormField the label points at the input, and the error message is rendered by the field; validation rules and submission belong to the Form, and Enter to submit and the submit button keep their default behavior.

<script setup lang="ts">
  import { reactive, ref } from 'vue'
  import * as v from 'valibot'
  import { Button, Form, FormField, SearchInput, Text } from '@hina-ui/vue'

  const schema = v.object({
    keyword: v.pipe(
      v.string('Enter a keyword'),
      v.trim(),
      v.nonEmpty('Enter a keyword'),
      v.minLength(2, 'At least 2 characters'),
    ),
  })

  const values = reactive({ keyword: '' })
  const result = ref('')

  async function search(data: unknown) {
    await new Promise(resolve => setTimeout(resolve, 600))
    result.value = (data as { keyword: string }).keyword
  }
</script>

<template>
  <Form v-slot="{ submitting }" :values="values" :rules="schema" class="w-80" @submit="search">
    <FormField name="keyword" label="Search works">
      <SearchInput v-model="values.keyword" placeholder="Title or alias" />
    </FormField>
    <Button type="submit" :loading="submitting" class="self-start">Search</Button>
    <Text v-if="result" tone="muted" size="sm">Searching for "{{ result }}".</Text>
  </Form>
</template>
vue

Behaviour

  • The clear button only appears with content and when not disabled; clicking it does not blur the text area.
  • Esc clears only when there is content and does nothing otherwise.
  • Hover, focus and disabled behave the same as Input.

Accessibility

  • The text area is type="search", so mobile keyboards show a search action key.
  • The clear button is reachable with Tab and named from the locale pack. The root sets aria-busy while loading.
  • Pair it with a label element or an aria-label for its name.

API

Props

Prop
Type
Default
Description
modelValue
string
''
The query
clearable
boolean
true
Whether the clear button is shown
loading
boolean
false
Whether the spinner is shown
variant
'primary' | 'secondary' | 'bare'
'primary'
Variant
size
'sm' | 'md' | 'lg'
'md'
Size
disabled
boolean
false
Whether the field is disabled
class
string
—
Classes appended to the root element
Event
Payload
Description
update:modelValue
value: string
The query changed
search
value: string
Enter submitted the query
clear
—
The query was cleared