nb-input-search
This is a search field component for Vue.js 3+.
Typing debounce uses @vlalg-nimbus/magic-debounce, included as a dependency of @vlalg-nimbus/nb-inputs.
Loading component...
Installation
yarn add @vlalg-nimbus/nb-inputs
Usage
import { createApp } from 'vue'
import App from './App.vue'
import NbInputComponents from '@vlalg-nimbus/nb-inputs'
import "@vlalg-nimbus/nb-inputs/dist/style.css";
const app = createApp(App)
app.use(NbInputComponents)
app.mount('#app')
To use, simply call the component, in this case it will be NbInputSearch or nb-input-search.
<template>
<NbInputSearch />
</template>
Preview & Playground
Select the component you want to edit/test
Props
Items with an (*) mean they are required
| name | Value type | Default | Description |
|---|---|---|---|
| nbId (*) | String | — | Root id for the field. |
| display | String | 'b' | Display mode: b or ib. |
| tabIndex | Number | 0 | Tab index for keyboard focus. |
| hasTabIndexEnter | Boolean | true | When true, Enter runs the submit path (emit entered + interactionFunction when allowed). |
| ariaLabel | String | 'Alternate Text Button' | aria-label for the control / button context. |
| ariaAttrs | Object | {} | Extra ARIA attributes (keys get aria- prefix). |
| title | String | '' | Native tooltip on hover. |
| textColor | String | '#ffffff' | Main text color (hex or CSS color). |
| caretColor | String | '' | Caret color; empty uses browser default. |
| selectionBgColor | String | '' | Selection background; empty uses browser default. |
| selectionTextColor | String | '' | Selected text color; empty uses browser default. |
| theme | String | 'light' | light or dark. |
| hasBorderRadius | Boolean | false | Enables border radius on the control. |
| hasBorderFocus | Boolean | true | When true, applies a highlight border on the input while focused (light-focus-active-border-color / dark-focus-active-border-color). |
| lightFocusActiveBorderColor | String | '#2563eb' | Focus border when theme="light" (light-focus-active-border-color). |
| darkFocusActiveBorderColor | String | '#7dd3fc' | Focus border when theme="dark" (dark-focus-active-border-color). |
| borderRadius | Number | 0.5 | Border radius value. |
| disabled | Boolean | false | Disables the field. |
| fontFamily | String | 'Lato', sans-serif | Input font-family. |
| fontSize | String | null | Input font-size; when null, size follows sizeMediaQuery (same rules as nb-input). |
| fontWeight | Number | 400 | Input font-weight. |
| fontFamilyMsg | String | 'Lato', sans-serif | Message font-family. |
| fontSizeMsg | String | '1em' | Message font-size. |
| fontWeightMsg | Number | 400 | Message font-weight. |
| textMessageColor | String | '#f15574' | Message text color. |
| textAlign | String | 'left' | left, center, or right. |
| inputText | String, Number | null | Initial value. |
| hasTrim | Boolean | false | Trim resolved value before interactionFunction and related emits. |
| inputName (*) | String | — | name on the input / label for. |
| inputPadding | String | '6px 10px' | CSS padding for the inner input area. |
| inputPlaceholder | String | '' | Placeholder text. |
| activeTextStyle | String | 'normal' | normal, italic, or oblique when active. |
| sizeMediaQuery | String | 'sm' | Responsive size: xs, sm, md, lg. |
| required | Boolean | false | HTML required when applicable. |
| inputReadonly | Boolean | false | Read-only input. |
| blockPaste | Boolean | false | When true, blocks paste into the input (still emits paste). |
| inputAutocomplete | String | 'on' | on or off. |
| inputUppercase | Boolean | false | Uppercase display/input behavior. |
| inputWidth | Number | 200 | Width in pixels. |
| inputStyle | String | 'background' | background, line, or border. |
| lightBgColor | String | '#f8f8f2' | Light theme background. |
| lightBgColorFocus | String | '#eaeaea' | Light theme background when focused. |
| lightControlBorderColor | String | '#334155' | Light theme border. |
| lightControlBorderColorActive | String | '#2563eb' | Light theme border when active. |
| lightDisabledBgColor | String | '#dfdfd9' | Light theme disabled background. |
| lightTextColor | String | '#0f172a' | Light theme primary text. |
| darkBgColor | String | '#1e293b' | Dark theme background. |
| darkBgColorFocus | String | '#334155' | Dark theme background when focused. |
| darkControlBorderColor | String | '#94a3b8' | Dark theme border. |
| darkControlBorderColorActive | String | '#38bdf8' | Dark theme border when active. |
| darkDisabledBgColor | String | 'rgba(40, 42, 54, 1)' | Dark theme disabled background. |
| darkTextColor | String | '#f1f5f9' | Dark theme primary text. |
| darkDisabledControlBorderColor | String | 'rgba(68, 71, 90, 0.3)' | Dark theme disabled border. |
| lightDisabledControlBorderColor | String | 'rgba(53, 55, 52, 0.3)' | Light theme disabled border. |
| tabindex | String, Number | 0 | Native tabindex on the input (see also tabIndex). |
| showMsg | Boolean | false | Show validation / message area. |
| hasMsg | Boolean | false | Whether a message should be considered present. |
| message | String | 'Default message text' | Default message copy. |
| hasCustomMsg | Boolean | false | Use the message slot instead of default markup. |
| extraContendAbsolute | Boolean | false | Lay out message with position: absolute under the field (prop keeps Contend spelling). |
| hasIcon | Boolean | false | Show icon slot (left or right). |
| iconWidth | Number | 32 | Icon area width (px). |
| iconDirection | String | 'left' | left or right. right applies only while the search button is hidden (debounce + interaction-debounce-wait > 0). If has-icon and the submit button are both visible, layout forces left. |
| iconPadding | String | '5px 10px' | Icon padding (two space-separated values). |
| iconPaddingInput | Number | 35 | Extra input padding on the icon side. |
| iconMargin | String | '0' | Icon margin. |
| iconLightTextColor | String | '#f8fafc' | Icon foreground (light). |
| iconDarkTextColor | String | '#f1f5f9' | Icon foreground (dark). |
| iconLightBgColor | String | '#334155' | Icon background (light). |
| iconLightBgColorActive | String | '#475569' | Icon background active (light). |
| iconDarkBgColor | String | '#334155' | Icon background (dark). |
| iconDarkBgColorActive | String | '#475569' | Icon background active (dark). |
| iconDarkDisabledBgColor | String | 'rgba(68, 71, 90, 0.3)' | Icon disabled (dark). |
| iconLightDisabledBgColor | String | 'rgba(53, 55, 52, 0.3)' | Icon disabled (light). |
| iconBorderRadius | Number | 0 | Icon border-radius. |
| iconSize | Number | 1 | Icon scale multiplier. |
| showLabel | Boolean | false | Floating label. |
| label | String | 'Label text' | Label copy. |
| labelBreakOnActive | Boolean | true | Label line-break / ellipsis when active. |
| labelBackground | String | 'transparent' | Label background when floated. |
| labelPadding | String | '1px 5px' | Label padding when active. |
| labelBorderRadius | Number | 0 | Label border radius. |
| labelLeft | Number | 5 | Label horizontal offset (inactive). |
| inputLabelMarginActive | Number | 15 | Top margin when label is active. |
| labelActiveTop | Number | -13 | Label top offset when active. |
| labelActiveLeft | Number | 5 | Label left offset when active. |
| labelRight | Number | 0 | Label right offset (inactive). |
| labelActiveRight | Number | 0 | Label right offset when active. |
| fontFamilyLabel | String | 'Lato', sans-serif | Label font family. |
| fontSizeLabel | String | '1em' | Label size when inactive. |
| fontSizeLabelActive | String | '0.8em' | Label size when active. |
| fontWeightLabel | Number | 400 | Label weight. |
| lightTextColorLabel | String | '#333333' | Label color (light, inactive). |
| lightTextColorLabelActive | String | '#333333' | Label color (light, active). |
| darkTextColorLabel | String | '#ffffff' | Label color (dark, inactive). |
| darkTextColorLabelActive | String | '#ffffff' | Label color (dark, active). |
| interactionFunction | Function | async () => {} | Called with the resolved query string; may be async. |
| interactionDebounceWait | Number | 0 | Debounce delay (ms) while typing; only when interaction-trigger="debounce" and > 0. |
| interactionTrigger | String | 'submit' | submit (only Enter / button) or debounce (typing debounce + Enter / button). |
| interactionDebounceMinLength | Number | 2 | Minimum length (after has-trim) when min-length enforcement is on. |
| interactionDebounceEnforceMinLength | Boolean | false | When true and trigger is debounce, skips interactionFunction until length ≥ interactionDebounceMinLength; debounce cancel reason below-min-length. |
| submitButtonLabel | String | 'search' | Label on the submit control when the search button is visible (submit, or debounce with interactionDebounceWait <= 0). Maps to kebab-case submit-button-label. |
| lightSubmitButtonBgColor | String | '#1d4ed8' | Button background when theme="light" (light-submit-button-bg-color). |
| darkSubmitButtonBgColor | String | '#38bdf8' | Button background when theme="dark" (dark-submit-button-bg-color). |
| lightSubmitButtonBgColorHover | String | '#1e40af' | Hover background, theme="light" (light-submit-button-bg-color-hover). |
| darkSubmitButtonBgColorHover | String | '#0ea5e9' | Hover background, theme="dark" (dark-submit-button-bg-color-hover). |
| lightSubmitButtonTextColor | String | '#f8fafc' | Button text when theme="light" (light-submit-button-text-color). |
| darkSubmitButtonTextColor | String | '#0f172a' | Button text when theme="dark" (dark-submit-button-text-color). |
| lightSubmitButtonTextColorHover | String | '#ffffff' | Hover text, theme="light" (light-submit-button-text-color-hover). |
| darkSubmitButtonTextColorHover | String | '#020617' | Hover text, theme="dark" (dark-submit-button-text-color-hover). |
| submitButtonPadding | String | '5px 10px' | Search button padding (submit-button-padding). |
| submitButtonBorderRadius | String | '0px' | Search button border-radius (submit-button-border-radius). |
| submitButtonTop | String | '0' | Search button top when absolutely positioned (submit-button-top). |
| submitButtonRight | String | '0' | Search button right (submit-button-right). |
| submitButtonBottom | String | '0' | Search button bottom (submit-button-bottom). |
| submitButtonFontFamily | String | 'Lato', sans-serif | Search button font-family (submit-button-font-family). |
| submitButtonFontSize | String | '1.1em' | Search button font-size (submit-button-font-size). |
| submitButtonFontWeight | Number | 700 | Search button font-weight (submit-button-font-weight). |
| submitButtonIsUppercase | Boolean | true | Uppercase label on the search button (submit-button-is-uppercase). |
Events
| name | Return type | Description |
|---|---|---|
| current-value | value | Fired when the value changes (trim rules apply like changed). |
| changed | value | Fired when the value changes. |
| cleared | nothing | Fired when the value becomes empty after having had content (previous value was non-empty). |
| focused | nothing | Input focused. |
| blurred | nothing | Input blurred. |
| clicked | MouseEvent | Wrapper clicked. |
| entered | value | Fired when Enter / submit path runs, before interactionFunction. |
| paste | string | Pasted text; still fires if blockPaste blocks default paste. |
| interaction-start | { source, value } | source: 'debounce' or 'submit'. Before interactionFunction. |
| interaction-end | { source, value } | After interactionFunction resolves. |
| interaction-error | { source, value, error } | interactionFunction threw or rejected. |
| interaction-cancel | { reason } | reason: 'submit', 'clear-value', 'below-min-length', 'unmount', 'reconfigure'. |
Slots
The component has slots for custom content:
icon
Only when hasIcon is true.
<template>
<NbInputSearch
nb-id="search-1"
input-name="q"
:has-icon="true"
icon-direction="left"
>
<template #icon>
<span>🔍</span>
</template>
</NbInputSearch>
</template>
message
When the message area is enabled (same flags as nb-input: show-msg, has-msg, has-custom-msg, etc.).
<template>
<NbInputSearch
nb-id="search-1"
input-name="q"
:show-msg="true"
:has-msg="true"
:has-custom-msg="true"
>
<template #message>
<span>Helper or validation text</span>
</template>
</NbInputSearch>
</template>
Examples
Debounce on typing (500 ms) + trim + lifecycle events
interactionFunction runs after the user stops typing for interaction-debounce-wait ms. Enter (with hasTabIndexEnter) or a new keystroke resets the timer; Enter also runs the function immediately and emits entered first. The search button is hidden when debounce and wait > 0.
<template>
<NbInputSearch
nb-id="search-debounce"
display="b"
label="Search (debounce)"
input-name="search-debounce"
input-style="border"
input-text=""
:has-trim="true"
interaction-trigger="debounce"
:interaction-debounce-wait="500"
:interaction-function="onSearch"
@entered="onEntered"
@changed="lastChanged = $event"
@interaction-start="onInteractionStart"
@interaction-end="onInteractionEnd"
@interaction-cancel="onInteractionCancel"
@interaction-error="onInteractionError"
/>
<p>Current value (<code>@changed</code>): {{ lastChanged || '(empty)' }}</p>
<ul>
<li v-for="row in log" :key="row.id">{{ row.text }}</li>
</ul>
</template>
<script setup>
import { ref } from 'vue'
const lastChanged = ref('')
const log = ref([])
function push(kind, detail) {
log.value = [
{ id: Date.now() + Math.random(), text: `${kind}: ${detail}` },
...log.value
].slice(0, 25)
}
const onSearch = async (query) => {
push('interactionFunction', query)
// await fetch('/api?q=' + encodeURIComponent(query))
}
const onEntered = (value) => push('@entered', value)
const onInteractionStart = (p) => push('@interaction-start', `${p.source} → ${p.value}`)
const onInteractionEnd = (p) => push('@interaction-end', `${p.source} → ${p.value}`)
const onInteractionCancel = (p) => push('@interaction-cancel', p.reason)
const onInteractionError = (p) => {
const msg = p.error instanceof Error ? p.error.message : String(p.error)
push('@interaction-error', `${p.source} → ${msg}`)
}
</script>
Submit + interaction-debounce-wait="0" (typical default)
Nothing runs while typing. interactionFunction runs on Enter (if hasTabIndexEnter) or search button click. The button is visible (submit always shows the button).
<template>
<NbInputSearch
nb-id="search-submit-zero"
display="b"
label="Search (Enter / button)"
input-name="search-submit-zero"
input-style="border"
input-text=""
interaction-trigger="submit"
:interaction-debounce-wait="0"
:interaction-function="onSearch"
@entered="(v) => console.log('entered', v)"
/>
</template>
<script setup>
const onSearch = async (query) => {
console.log('search', query)
}
</script>
Submit + interaction-debounce-wait="500"
Still no calls while typing: with interaction-trigger="submit", interaction-debounce-wait does not enable debounced typing. Only Enter / button run interactionFunction. Useful if you keep a single wait prop for toggling modes elsewhere.
<template>
<NbInputSearch
nb-id="search-submit-wait"
display="b"
label="Search (submit; wait ignored on type)"
input-name="search-submit-wait"
input-style="border"
input-text=""
interaction-trigger="submit"
:interaction-debounce-wait="500"
:interaction-function="onSearch"
@entered="(v) => console.log('entered', v)"
/>
</template>
<script setup>
const onSearch = async (query) => {
console.log('search', query)
}
</script>
Debounce + in-memory list (fake latency)
Filter an array of { id, text, ... } by text inside interactionFunction. A short setTimeout (or fetch) simulates network delay; swap the delay for a real API call when needed.
<template>
<NbInputSearch
nb-id="catalog-search"
input-name="q"
label="Search products"
interaction-trigger="debounce"
:interaction-debounce-wait="400"
:has-trim="true"
:interaction-function="runSearch"
@changed="onChangedClear"
/>
<ul v-if="results.length">
<li v-for="item in results" :key="item.id">{{ item.text }} — {{ item.category }}</li>
</ul>
<p v-else-if="lastQuery !== ''">No matches for "{{ lastQuery }}".</p>
</template>
<script setup>
import { ref } from 'vue'
const CATALOG = [
{ id: '1', text: 'Wireless mouse', category: 'Peripherals' },
{ id: '2', text: 'USB hub', category: 'Accessories' },
{ id: '3', text: 'Mechanical keyboard', category: 'Peripherals' }
]
const results = ref([])
const lastQuery = ref('')
async function runSearch(query) {
await new Promise((r) => setTimeout(r, 450))
const q = String(query).trim().toLowerCase()
lastQuery.value = String(query).trim()
if (!q) {
results.value = []
return
}
results.value = CATALOG.filter((item) => item.text.toLowerCase().includes(q))
}
function onChangedClear(v) {
if (!String(v ?? '').trim()) {
results.value = []
lastQuery.value = ''
}
}
</script>
Debounce + fake API dropdown (clear naming)
Same behavior as the App.vue demo, with more descriptive variable/function names.
<div
class="wrapper-field-search"
ref="catalogResultsWrapperRef"
@mouseleave="handleCatalogWrapperMouseLeave"
@focusout="handleCatalogWrapperFocusOut"
>
<NbInputSearch
nb-id="search-demo-fake-catalog"
display="b"
label="Search product (fake API)"
input-name="search-demo-fake-catalog"
:input-text="catalogSearchInputValue"
input-style="border"
light-text-color="#ffffff"
input-text=""
:has-trim="true"
interaction-trigger="debounce"
:interaction-debounce-wait="400"
:interaction-debounce-enforce-min-length="false"
:interaction-debounce-min-length="3"
:interaction-function="runCatalogSearch"
@changed="handleCatalogSearchChanged"
@cleared="handleCatalogSearchCleared"
@interaction-start="handleCatalogInteractionStart"
@blurred="handleCatalogSearchBlurred"
@focused="handleCatalogSearchFocused"
/>
<div v-if="isCatalogResultsVisible" class="wrapper-field-search__results" @click="closeCatalogResults">
<div v-if="isCatalogLoading">
<p>Carregando...</p>
</div>
<div v-else>
<div v-if="filteredCatalogResults.length">
<strong>Resultados ({{ filteredCatalogResults.length }})</strong>
<ul>
<li v-for="item in filteredCatalogResults" :key="item.id" @click.stop="handleCatalogResultSelect(item)">
<strong>{{ item.text }}</strong>
<span style="opacity: 0.75"> — {{ item.category }}</span>
</li>
</ul>
</div>
<p
v-else
style="margin: 0.75rem 0 0; font-size: 0.9em; opacity: 0.85"
>
Nenhum resultado para <code>{{ lastCatalogSearchQuery }}</code>.
</p>
</div>
</div>
</div>
Behaviour (summary)
interaction-trigger | While typing | Enter / button |
|---|---|---|
submit (default) | Does not call interactionFunction. | Calls interactionFunction (after entered on that path). Enter requires hasTabIndexEnter. |
debounce | If interactionDebounceWait > 0, calls after pause. | Cancels pending debounce, emits entered, runs interactionFunction immediately. |
Search button: shown for submit, or debounce with interactionDebounceWait <= 0. Hidden for debounce + wait > 0.
Trim: interactionFunction receives the value after hasTrim rules; empty / whitespace-only skips the call; clearing the field can emit interaction-cancel with reason clear-value.
interaction-cancel: emitted for explicit timer clears (submit, clear-value, unmount, reconfigure). Rapid retyping before wait only resets the timer inside magic-debounce — no interaction-cancel in that case.
