Skip to main content
Version: v1 (Current)

Input

A text input component with full v-model support and all standard HTML input attributes.

Import

import { Input } from '@gxp-dev/uikit'

Usage

Basic

<script setup lang="ts">
import { ref } from 'vue'
import { Input } from '@gxp-dev/uikit'

const value = ref('')
</script>

<template>
<Input v-model="value" placeholder="Enter text..." />
</template>

Without v-model

<script setup lang="ts">
import { Input } from '@gxp-dev/uikit'

const handleInput = (value: string | number) => {
console.log('Input value:', value)
}
</script>

<template>
<Input
default-value="Initial value"
@update:model-value="handleInput"
/>
</template>

Input Types

The Input supports all standard HTML input types:

<template>
<div class="space-y-4">
<Input type="text" placeholder="Text input" />
<Input type="email" placeholder="Email address" />
<Input type="password" placeholder="Password" />
<Input type="number" placeholder="Number" />
<Input type="tel" placeholder="Phone number" />
<Input type="url" placeholder="Website URL" />
<Input type="search" placeholder="Search..." />
</div>
</template>

File Input

<template>
<!-- Single file -->
<Input type="file" />

<!-- Multiple files -->
<Input type="file" multiple />

<!-- Specific file types -->
<Input type="file" accept="image/*" />
<Input type="file" accept=".pdf,.doc,.docx" />
</template>

States

Disabled

<template>
<Input disabled placeholder="Disabled input" />
<Input disabled value="Disabled with value" />
</template>

Read-only

<template>
<Input readonly value="Read-only value" />
</template>

Required

<template>
<form>
<Input required placeholder="Required field" />
<button type="submit">Submit</button>
</form>
</template>

With Labels

Basic Label

<template>
<div class="space-y-2">
<label for="email" class="text-sm font-medium">
Email Address
</label>
<Input id="email" type="email" placeholder="you@example.com" />
</div>
</template>

With Helper Text

<template>
<div class="space-y-2">
<label for="username" class="text-sm font-medium">
Username
</label>
<Input id="username" placeholder="johndoe" />
<p class="text-sm text-muted-foreground">
This will be your public display name.
</p>
</div>
</template>

With Error Message

<script setup lang="ts">
import { ref, computed } from 'vue'
import { Input } from '@gxp-dev/uikit'

const email = ref('')
const touched = ref(false)
const isValid = computed(() => email.value.includes('@'))
</script>

<template>
<div class="space-y-2">
<label for="email" class="text-sm font-medium">
Email
</label>
<Input
id="email"
v-model="email"
type="email"
:class="{ 'border-red-500 focus-visible:ring-red-500': touched && !isValid }"
@blur="touched = true"
/>
<p v-if="touched && !isValid" class="text-sm text-red-500">
Please enter a valid email address.
</p>
</div>
</template>

With Icons

Icon on Left

<script setup>
import { Input } from '@gxp-dev/uikit'
import { Search } from 'lucide-vue-next'
</script>

<template>
<div class="relative">
<Search class="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
<Input class="pl-10" placeholder="Search..." />
</div>
</template>

Icon on Right

<script setup>
import { Input } from '@gxp-dev/uikit'
import { Mail } from 'lucide-vue-next'
</script>

<template>
<div class="relative">
<Input type="email" placeholder="Email" class="pr-10" />
<Mail class="absolute right-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
</div>
</template>

Password with Toggle

<script setup lang="ts">
import { ref } from 'vue'
import { Input, Button } from '@gxp-dev/uikit'
import { Eye, EyeOff } from 'lucide-vue-next'

const password = ref('')
const showPassword = ref(false)
</script>

<template>
<div class="relative">
<Input
v-model="password"
:type="showPassword ? 'text' : 'password'"
placeholder="Enter password"
class="pr-10"
/>
<Button
type="button"
variant="ghost"
size="icon"
class="absolute right-0 top-0 h-full px-3 hover:bg-transparent"
@click="showPassword = !showPassword"
>
<Eye v-if="!showPassword" class="h-4 w-4" />
<EyeOff v-else class="h-4 w-4" />
</Button>
</div>
</template>

Validation Attributes

Character Limits

<script setup lang="ts">
import { ref, computed } from 'vue'
import { Input } from '@gxp-dev/uikit'

const bio = ref('')
const maxLength = 100
const remaining = computed(() => maxLength - bio.value.length)
</script>

<template>
<div class="space-y-2">
<Input
v-model="bio"
:maxlength="maxLength"
placeholder="Short bio"
/>
<p class="text-sm text-muted-foreground text-right">
{{ remaining }} characters remaining
</p>
</div>
</template>

Pattern Validation

<template>
<!-- US Phone number -->
<Input
type="tel"
pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}"
placeholder="123-456-7890"
/>

<!-- Zip code -->
<Input
pattern="[0-9]{5}"
placeholder="12345"
/>
</template>

Number Constraints

<template>
<Input
type="number"
min="0"
max="100"
step="5"
placeholder="0-100, step 5"
/>
</template>

Custom Styling

Via Class Prop

<template>
<Input class="rounded-full" placeholder="Rounded input" />
<Input class="border-2 border-primary" placeholder="Thick border" />
<Input class="bg-muted" placeholder="Muted background" />
</template>

Full Width

<template>
<Input class="w-full" placeholder="Full width input" />
</template>

API Reference

Props

PropTypeDefaultDescription
modelValuestring | number-Bound value (v-model)
defaultValuestring | number-Initial value when uncontrolled
typestring'text'Input type (text, email, password, etc.)
placeholderstring-Placeholder text
disabledbooleanfalseDisable the input
readonlybooleanfalseMake input read-only
requiredbooleanfalseMark as required
classstring-Additional CSS classes

Plus all standard HTML input attributes (id, name, maxlength, min, max, pattern, autocomplete, etc.)

Events

EventPayloadDescription
update:modelValuestring | numberEmitted when value changes
inputEventNative input event
changeEventNative change event
focusFocusEventEmitted on focus
blurFocusEventEmitted on blur

Slots

None - Input is a self-closing element.

Exports

export { Input } from '@gxp-dev/uikit'

Accessibility

  • Uses native <input> element for full accessibility
  • Associates with <label> via id attribute
  • Supports all ARIA attributes (aria-label, aria-describedby, etc.)
  • Focus ring visible for keyboard navigation

ARIA Example

<template>
<div>
<label id="email-label">Email Address</label>
<Input
aria-labelledby="email-label"
aria-describedby="email-hint email-error"
/>
<p id="email-hint">We'll never share your email.</p>
<p id="email-error" role="alert">Invalid email format.</p>
</div>
</template>

Examples

Login Form

<script setup lang="ts">
import { ref } from 'vue'
import { Input, Button } from '@gxp-dev/uikit'

const email = ref('')
const password = ref('')
const isLoading = ref(false)

const handleSubmit = async () => {
isLoading.value = true
// Authentication logic
await new Promise(r => setTimeout(r, 1000))
isLoading.value = false
}
</script>

<template>
<form @submit.prevent="handleSubmit" class="space-y-4 max-w-sm">
<div class="space-y-2">
<label for="email" class="text-sm font-medium">Email</label>
<Input
id="email"
v-model="email"
type="email"
placeholder="you@example.com"
required
:disabled="isLoading"
/>
</div>

<div class="space-y-2">
<label for="password" class="text-sm font-medium">Password</label>
<Input
id="password"
v-model="password"
type="password"
required
:disabled="isLoading"
/>
</div>

<Button type="submit" class="w-full" :disabled="isLoading">
{{ isLoading ? 'Signing in...' : 'Sign In' }}
</Button>
</form>
</template>

Search Input

<script setup lang="ts">
import { ref, watch } from 'vue'
import { Input, Button } from '@gxp-dev/uikit'
import { Search, X } from 'lucide-vue-next'

const query = ref('')

// Debounced search
watch(query, (value) => {
// Perform search
console.log('Searching:', value)
})
</script>

<template>
<div class="relative max-w-md">
<Search class="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
<Input
v-model="query"
type="search"
placeholder="Search..."
class="pl-10 pr-10"
/>
<Button
v-if="query"
type="button"
variant="ghost"
size="icon"
class="absolute right-0 top-0 h-full px-3 hover:bg-transparent"
@click="query = ''"
>
<X class="h-4 w-4" />
</Button>
</div>
</template>