Vue Material Component Framework implementing Google's Material Design specification with comprehensive UI components, theming system, and accessibility features.
—
Form components and validation utilities for user input collection and data entry.
Form wrapper component with validation, submission handling, and state management.
/**
* Form wrapper with validation and submission handling
*/
const VForm: Component;
interface FormProps {
/** Form model value for validation state */
modelValue?: boolean;
/** Fast fail validation mode */
fastFail?: boolean;
/** Read-only form state */
readonly?: boolean;
/** Disabled form state */
disabled?: boolean;
/** Validate on blur */
validateOn?: 'blur' | 'input' | 'submit' | 'blur lazy' | 'input lazy' | 'submit lazy';
}
interface FormExposed {
/** Validate all form fields */
validate(): Promise<{ valid: boolean; errors: ValidationError[] }>;
/** Reset all form fields */
reset(): void;
/** Reset validation state */
resetValidation(): void;
/** Check if form is valid */
isValid: Ref<boolean>;
/** Form validation errors */
errors: Ref<ValidationError[]>;
}
// Events
interface FormEvents {
'update:modelValue': (valid: boolean) => void;
'submit': (event: SubmitEventPromise) => void;
}
interface SubmitEventPromise extends Event {
/** Promise that resolves when validation completes */
readonly valid: Promise<{ valid: boolean; errors: ValidationError[] }>;
}
interface ValidationError {
/** Field identifier */
id: string | number;
/** Error message */
errorMessages: string[];
}Usage Examples:
<template>
<v-form
ref="form"
v-model="valid"
@submit.prevent="handleSubmit"
>
<v-text-field
v-model="name"
:rules="nameRules"
label="Name"
required
/>
<v-text-field
v-model="email"
:rules="emailRules"
label="Email"
type="email"
required
/>
<v-btn
type="submit"
:disabled="!valid"
color="primary"
>
Submit
</v-btn>
</v-form>
</template>
<script setup>
const form = ref();
const valid = ref(false);
const name = ref('');
const email = ref('');
const nameRules = [
v => !!v || 'Name is required',
v => v.length >= 2 || 'Name must be at least 2 characters',
];
const emailRules = [
v => !!v || 'Email is required',
v => /.+@.+\..+/.test(v) || 'Email must be valid',
];
const handleSubmit = async () => {
const { valid } = await form.value.validate();
if (valid) {
// Submit form data
console.log('Form is valid, submitting...');
}
};
</script>Single-line text input field with validation and formatting capabilities.
/**
* Single-line text input component
*/
const VTextField: Component;
interface TextFieldProps {
/** Input value */
modelValue?: string | number;
/** Input type */
type?: 'text' | 'password' | 'email' | 'url' | 'tel' | 'number';
/** Field label */
label?: string;
/** Placeholder text */
placeholder?: string;
/** Hint text */
hint?: string;
/** Persistent hint */
persistentHint?: boolean;
/** Validation rules */
rules?: ValidationRule[];
/** Error messages */
errorMessages?: string | string[];
/** Error state */
error?: boolean;
/** Success state */
success?: boolean;
/** Disabled state */
disabled?: boolean;
/** Readonly state */
readonly?: boolean;
/** Required field indicator */
required?: boolean;
/** Field density */
density?: 'default' | 'comfortable' | 'compact';
/** Field variant */
variant?: 'filled' | 'outlined' | 'underlined' | 'plain' | 'solo' | 'solo-inverted' | 'solo-filled';
/** Hide details */
hideDetails?: boolean | 'auto';
/** Prepend icon */
prependIcon?: string;
/** Append icon */
appendIcon?: string;
/** Prepend inner icon */
prependInnerIcon?: string;
/** Append inner icon */
appendInnerIcon?: string;
/** Clear icon */
clearIcon?: string;
/** Clearable input */
clearable?: boolean;
/** Counter for character count */
counter?: boolean | number | string;
/** Maximum length */
maxlength?: number | string;
/** Prefix text */
prefix?: string;
/** Suffix text */
suffix?: string;
/** Loading state */
loading?: boolean;
/** Autofocus */
autofocus?: boolean;
}
// Events
interface TextFieldEvents {
'update:modelValue': (value: string | number) => void;
'click:prepend': (event: MouseEvent) => void;
'click:append': (event: MouseEvent) => void;
'click:prependInner': (event: MouseEvent) => void;
'click:appendInner': (event: MouseEvent) => void;
'click:clear': (event: MouseEvent) => void;
'focus': (event: FocusEvent) => void;
'blur': (event: FocusEvent) => void;
'keydown': (event: KeyboardEvent) => void;
}Usage Examples:
<template>
<!-- Basic text field -->
<v-text-field
v-model="username"
label="Username"
prepend-inner-icon="mdi-account"
/>
<!-- Password field -->
<v-text-field
v-model="password"
:type="showPassword ? 'text' : 'password'"
label="Password"
:append-inner-icon="showPassword ? 'mdi-eye' : 'mdi-eye-off'"
@click:append-inner="showPassword = !showPassword"
/>
<!-- Validated field -->
<v-text-field
v-model="email"
:rules="emailRules"
label="Email"
type="email"
hint="Enter a valid email address"
persistent-hint
/>
<!-- With counter -->
<v-text-field
v-model="description"
label="Description"
counter="200"
maxlength="200"
/>
</template>Multi-line text input area for longer text content.
/**
* Multi-line text input component
*/
const VTextarea: Component;
interface TextareaProps extends Omit<TextFieldProps, 'type' | 'prependInnerIcon' | 'appendInnerIcon'> {
/** Number of rows */
rows?: number | string;
/** Maximum number of rows */
maxRows?: number | string;
/** Auto-grow height */
autoGrow?: boolean;
/** No resize handle */
noResize?: boolean;
}Usage Examples:
<template>
<!-- Basic textarea -->
<v-textarea
v-model="message"
label="Message"
rows="3"
/>
<!-- Auto-growing textarea -->
<v-textarea
v-model="content"
label="Content"
auto-grow
max-rows="10"
/>
<!-- With validation -->
<v-textarea
v-model="feedback"
:rules="[v => v.length >= 10 || 'Minimum 10 characters required']"
label="Feedback"
counter="500"
maxlength="500"
/>
</template>Dropdown selection component with single or multiple selection modes.
/**
* Dropdown selection component
*/
const VSelect: Component;
interface SelectProps {
/** Selected value(s) */
modelValue?: any;
/** Selection items */
items?: any[];
/** Item title key */
itemTitle?: string | ((item: any) => string);
/** Item value key */
itemValue?: string | ((item: any) => any);
/** Item props key */
itemProps?: string | ((item: any) => Record<string, any>);
/** Return object instead of value */
returnObject?: boolean;
/** Multiple selection */
multiple?: boolean;
/** Chips display for multiple */
chips?: boolean;
/** Closable chips */
closableChips?: boolean;
/** Field label */
label?: string;
/** Placeholder text */
placeholder?: string;
/** Validation rules */
rules?: ValidationRule[];
/** Menu props */
menuProps?: Record<string, any>;
/** Clearable selection */
clearable?: boolean;
/** Loading state */
loading?: boolean;
/** Hide selected items from list */
hideSelected?: boolean;
/** Menu open state */
menu?: boolean;
}
// Events
interface SelectEvents {
'update:modelValue': (value: any) => void;
'update:menu': (open: boolean) => void;
}Usage Examples:
<template>
<!-- Basic select -->
<v-select
v-model="selectedItem"
:items="items"
label="Choose an option"
item-title="text"
item-value="value"
/>
<!-- Multiple select with chips -->
<v-select
v-model="selectedItems"
:items="items"
label="Multiple selection"
multiple
chips
closable-chips
item-title="text"
item-value="value"
/>
<!-- Object return format -->
<v-select
v-model="selectedObject"
:items="complexItems"
label="Select user"
item-title="name"
item-value="id"
return-object
/>
</template>
<script setup>
const items = [
{ text: 'Option 1', value: 1 },
{ text: 'Option 2', value: 2 },
{ text: 'Option 3', value: 3 },
];
const complexItems = [
{ id: 1, name: 'John Doe', email: 'john@example.com' },
{ id: 2, name: 'Jane Smith', email: 'jane@example.com' },
];
</script>Combobox component allowing both selection from list and free text input.
/**
* Combobox with selection and text input capabilities
*/
const VCombobox: Component;
interface ComboboxProps extends SelectProps {
/** Allow custom values */
customValue?: boolean;
/** Delimiter for multiple values */
delimiters?: string[];
/** Filter function */
customFilter?: FilterFunction;
}Usage Examples:
<template>
<!-- Basic combobox -->
<v-combobox
v-model="selectedTags"
:items="availableTags"
label="Tags"
multiple
chips
delimiters=" ,"
/>
<!-- Custom value combobox -->
<v-combobox
v-model="customSelection"
:items="predefinedOptions"
label="Select or enter custom"
custom-value
/>
</template>Autocomplete component with filtering and async data loading capabilities.
/**
* Autocomplete input with filtering
*/
const VAutocomplete: Component;
interface AutocompleteProps extends SelectProps {
/** Search input value */
search?: string;
/** Custom filter function */
customFilter?: FilterFunction;
/** Filter function keys */
customKeyFilter?: Record<string, FilterFunction>;
/** No filter mode for server-side filtering */
noFilter?: boolean;
/** Auto-select first item */
autoSelectFirst?: boolean;
}
// Events
interface AutocompleteEvents extends SelectEvents {
'update:search': (search: string) => void;
}Usage Examples:
<template>
<!-- Basic autocomplete -->
<v-autocomplete
v-model="selectedUser"
:items="users"
label="Search users"
item-title="name"
item-value="id"
/>
<!-- Server-side filtering -->
<v-autocomplete
v-model="selectedItem"
v-model:search="searchQuery"
:items="searchResults"
:loading="searching"
label="Search items"
no-filter
@update:search="performSearch"
/>
</template>
<script setup>
const searchQuery = ref('');
const searchResults = ref([]);
const searching = ref(false);
const performSearch = async (query) => {
if (!query) {
searchResults.value = [];
return;
}
searching.value = true;
try {
const results = await api.search(query);
searchResults.value = results;
} finally {
searching.value = false;
}
};
</script>Checkbox input component for boolean selections.
/**
* Checkbox input component
*/
const VCheckbox: Component;
interface CheckboxProps {
/** Checkbox value */
modelValue?: any;
/** True value when checked */
trueValue?: any;
/** False value when unchecked */
falseValue?: any;
/** Indeterminate state */
indeterminate?: boolean;
/** Checkbox label */
label?: string;
/** Validation rules */
rules?: ValidationRule[];
/** Disabled state */
disabled?: boolean;
/** Readonly state */
readonly?: boolean;
/** Error state */
error?: boolean;
/** Checkbox color */
color?: string;
/** Hide details */
hideDetails?: boolean | 'auto';
/** Field density */
density?: 'default' | 'comfortable' | 'compact';
}
// Events
interface CheckboxEvents {
'update:modelValue': (value: any) => void;
'update:indeterminate': (indeterminate: boolean) => void;
}Usage Examples:
<template>
<!-- Basic checkbox -->
<v-checkbox
v-model="agreed"
label="I agree to the terms"
/>
<!-- Custom values -->
<v-checkbox
v-model="status"
true-value="active"
false-value="inactive"
label="Active status"
/>
<!-- Indeterminate checkbox -->
<v-checkbox
v-model="selectAll"
:indeterminate="someSelected"
@click="toggleAll"
label="Select all"
/>
</template>Radio button components for single selection from multiple options.
/**
* Radio button component
*/
const VRadio: Component;
interface RadioProps {
/** Radio value */
value?: any;
/** Radio label */
label?: string;
/** Disabled state */
disabled?: boolean;
/** Readonly state */
readonly?: boolean;
/** Radio color */
color?: string;
/** Field density */
density?: 'default' | 'comfortable' | 'compact';
}
/**
* Radio group container
*/
const VRadioGroup: Component;
interface RadioGroupProps {
/** Selected value */
modelValue?: any;
/** Group label */
label?: string;
/** Validation rules */
rules?: ValidationRule[];
/** Error state */
error?: boolean;
/** Error messages */
errorMessages?: string | string[];
/** Disabled state */
disabled?: boolean;
/** Readonly state */
readonly?: boolean;
/** Inline layout */
inline?: boolean;
/** Hide details */
hideDetails?: boolean | 'auto';
/** Field density */
density?: 'default' | 'comfortable' | 'compact';
}
// Events
interface RadioGroupEvents {
'update:modelValue': (value: any) => void;
}Usage Examples:
<template>
<v-radio-group v-model="selectedOption" label="Choose an option">
<v-radio
v-for="option in options"
:key="option.value"
:value="option.value"
:label="option.text"
/>
</v-radio-group>
<!-- Inline radio group -->
<v-radio-group
v-model="size"
label="Size"
inline
>
<v-radio value="small" label="Small" />
<v-radio value="medium" label="Medium" />
<v-radio value="large" label="Large" />
</v-radio-group>
</template>Toggle switch component for boolean state control.
/**
* Toggle switch component
*/
const VSwitch: Component;
interface SwitchProps {
/** Switch value */
modelValue?: any;
/** True value when on */
trueValue?: any;
/** False value when off */
falseValue?: any;
/** Switch label */
label?: string;
/** Validation rules */
rules?: ValidationRule[];
/** Disabled state */
disabled?: boolean;
/** Readonly state */
readonly?: boolean;
/** Loading state */
loading?: boolean;
/** Error state */
error?: boolean;
/** Switch color */
color?: string;
/** Inset style */
inset?: boolean;
/** Flat style (no elevation) */
flat?: boolean;
/** Hide details */
hideDetails?: boolean | 'auto';
/** Field density */
density?: 'default' | 'comfortable' | 'compact';
}
// Events
interface SwitchEvents {
'update:modelValue': (value: any) => void;
}Usage Examples:
<template>
<!-- Basic switch -->
<v-switch
v-model="enabled"
label="Enable notifications"
/>
<!-- Custom values -->
<v-switch
v-model="mode"
true-value="dark"
false-value="light"
label="Dark mode"
/>
<!-- Loading switch -->
<v-switch
v-model="autoSave"
:loading="saving"
label="Auto-save"
/>
</template>Slider components for numeric value selection.
/**
* Single value slider component
*/
const VSlider: Component;
interface SliderProps {
/** Slider value */
modelValue?: number | string;
/** Minimum value */
min?: number | string;
/** Maximum value */
max?: number | string;
/** Step increment */
step?: number | string;
/** Slider label */
label?: string;
/** Validation rules */
rules?: ValidationRule[];
/** Disabled state */
disabled?: boolean;
/** Readonly state */
readonly?: boolean;
/** Show thumb label */
thumbLabel?: boolean | 'always';
/** Thumb size */
thumbSize?: number | string;
/** Show ticks */
showTicks?: boolean | 'always';
/** Tick labels */
tickLabels?: string[];
/** Tick size */
tickSize?: number | string;
/** Vertical orientation */
vertical?: boolean;
/** Slider color */
color?: string;
/** Track color */
trackColor?: string;
/** Track fill color */
trackFillColor?: string;
/** Track size */
trackSize?: number | string;
/** Thumb color */
thumbColor?: string;
/** Hide details */
hideDetails?: boolean | 'auto';
/** Field density */
density?: 'default' | 'comfortable' | 'compact';
}
/**
* Range slider with dual handles
*/
const VRangeSlider: Component;
interface RangeSliderProps extends Omit<SliderProps, 'modelValue'> {
/** Range values [min, max] */
modelValue?: number[];
}
// Events
interface SliderEvents {
'update:modelValue': (value: number | number[]) => void;
'start': (value: number | number[]) => void;
'end': (value: number | number[]) => void;
}Usage Examples:
<template>
<!-- Basic slider -->
<v-slider
v-model="volume"
:min="0"
:max="100"
label="Volume"
thumb-label
/>
<!-- Range slider -->
<v-range-slider
v-model="priceRange"
:min="0"
:max="1000"
:step="10"
label="Price Range"
thumb-label="always"
/>
<!-- Slider with ticks -->
<v-slider
v-model="rating"
:min="1"
:max="5"
:step="1"
label="Rating"
show-ticks="always"
:tick-labels="['Poor', 'Fair', 'Good', 'Very Good', 'Excellent']"
/>
</template>File upload input component with drag-and-drop support.
/**
* File input component
*/
const VFileInput: Component;
interface FileInputProps {
/** Selected files */
modelValue?: File | File[];
/** Accept file types */
accept?: string;
/** Multiple file selection */
multiple?: boolean;
/** Field label */
label?: string;
/** Placeholder text */
placeholder?: string;
/** Validation rules */
rules?: ValidationRule[];
/** Disabled state */
disabled?: boolean;
/** Readonly state */
readonly?: boolean;
/** Show file size */
showSize?: boolean | ((file: File) => string);
/** Clearable selection */
clearable?: boolean;
/** Chips display for multiple files */
chips?: boolean;
/** Counter for file count */
counter?: boolean;
/** Prepend icon */
prependIcon?: string;
/** Append icon */
appendIcon?: string;
/** Hide details */
hideDetails?: boolean | 'auto';
}
// Events
interface FileInputEvents {
'update:modelValue': (files: File | File[]) => void;
'click:prepend': (event: MouseEvent) => void;
'click:append': (event: MouseEvent) => void;
'click:clear': (event: MouseEvent) => void;
}Usage Examples:
<template>
<!-- Basic file input -->
<v-file-input
v-model="selectedFile"
label="Choose file"
accept="image/*"
/>
<!-- Multiple files with chips -->
<v-file-input
v-model="selectedFiles"
label="Choose files"
multiple
chips
show-size
counter
/>
<!-- With validation -->
<v-file-input
v-model="document"
:rules="fileRules"
label="Upload document"
accept=".pdf,.doc,.docx"
/>
</template>
<script setup>
const fileRules = [
v => !!v || 'File is required',
v => !v || v.size <= 5 * 1024 * 1024 || 'File must be less than 5MB',
];
</script>Numeric input component with increment/decrement controls.
/**
* Number input with controls
*/
const VNumberInput: Component;
interface NumberInputProps {
/** Number value */
modelValue?: number;
/** Minimum value */
min?: number;
/** Maximum value */
max?: number;
/** Step increment */
step?: number;
/** Field label */
label?: string;
/** Validation rules */
rules?: ValidationRule[];
/** Disabled state */
disabled?: boolean;
/** Readonly state */
readonly?: boolean;
/** Hide control buttons */
hideControls?: boolean;
/** Control variant */
controlVariant?: 'default' | 'stacked' | 'split';
/** Reverse controls */
reverse?: boolean;
/** Hide details */
hideDetails?: boolean | 'auto';
}
// Events
interface NumberInputEvents {
'update:modelValue': (value: number) => void;
}Usage Examples:
<template>
<!-- Basic number input -->
<v-number-input
v-model="quantity"
:min="1"
:max="100"
label="Quantity"
/>
<!-- Stacked controls -->
<v-number-input
v-model="price"
:step="0.01"
:min="0"
label="Price"
control-variant="stacked"
/>
</template>One-time password (OTP) input component for verification codes.
/**
* OTP/PIN input component
*/
const VOtpInput: Component;
interface OtpInputProps {
/** OTP value */
modelValue?: string;
/** Number of input fields */
length?: number;
/** Input type */
type?: 'text' | 'number' | 'password';
/** Field variant */
variant?: 'filled' | 'outlined' | 'underlined' | 'plain';
/** Validation rules */
rules?: ValidationRule[];
/** Disabled state */
disabled?: boolean;
/** Readonly state */
readonly?: boolean;
/** Field density */
density?: 'default' | 'comfortable' | 'compact';
/** Hide details */
hideDetails?: boolean | 'auto';
/** Placeholder character */
placeholder?: string;
/** Autofocus */
autofocus?: boolean;
}
// Events
interface OtpInputEvents {
'update:modelValue': (value: string) => void;
'finish': (value: string) => void;
}Usage Examples:
<template>
<!-- Basic OTP input -->
<v-otp-input
v-model="otpCode"
:length="6"
type="number"
@finish="handleOtpComplete"
/>
<!-- Password OTP -->
<v-otp-input
v-model="pinCode"
:length="4"
type="password"
variant="outlined"
/>
</template>// Validation types
type ValidationRule<T = any> =
| true
| string
| ((value: T) => true | string);
// Form submission event
interface SubmitEventPromise extends Event {
readonly valid: Promise<{ valid: boolean; errors: ValidationError[] }>;
}
// Validation error structure
interface ValidationError {
id: string | number;
errorMessages: string[];
}
// Field variants
type FieldVariant = 'filled' | 'outlined' | 'underlined' | 'plain' | 'solo' | 'solo-inverted' | 'solo-filled';
// Input types
type InputType = 'text' | 'password' | 'email' | 'url' | 'tel' | 'number';
// Density options
type Density = 'default' | 'comfortable' | 'compact';
// Filter function for autocomplete/combobox
type FilterFunction = (item: any, queryText: string, itemText: string) => boolean;Install with Tessl CLI
npx tessl i tessl/npm-vuetify