Skip to content
RTL Support: Full
Accessibility: Full
Translations: Not Needed

Text Input

Basic Usage

Basic text input with two-way binding and placeholder text.

Enter text...
vue
<template>
  <HLInput v-model:modelValue="value" placeholder="Enter text..." />
</template>

<script setup>
import { ref } from 'vue'
import { HLInput } from '@platform-ui/highrise'

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

All Sizes

Available size variants from largest (lg) to smallest (3xs).

Please Input
Please Input
Please Input
Please Input
Please Input
Please Input
vue
<template>
  <HLInput v-model:modelValue="value" size="lg" placeholder="Large input" />
  <HLInput v-model:modelValue="value" size="md" placeholder="Medium input" />
  <HLInput v-model:modelValue="value" size="sm" placeholder="Small input" />
  <HLInput v-model:modelValue="value" size="xs" placeholder="Extra small input" />
  <HLInput v-model:modelValue="value" size="2xs" placeholder="2x Extra small input" />
  <HLInput v-model:modelValue="value" size="3xs" placeholder="3x Extra small input" />
</template>

<script setup>
import { ref } from 'vue'
import { HLInput } from '@platform-ui/highrise'

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

Text Align

Control text alignment within the input field.

Please Input
Please Input
Please Input
vue
<template>
  <HLInput v-model:modelValue="value" text-align="start" placeholder="Left aligned" />
  <HLInput v-model:modelValue="value" text-align="center" placeholder="Center aligned" />
  <HLInput v-model:modelValue="value" text-align="end" placeholder="Right aligned" />
</template>

<script setup>
import { ref } from 'vue'
import { HLInput } from '@platform-ui/highrise'

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

Custom font styles

Override the input's font size and weight with custom values.

Please Input
vue
<template>
  <HLInput v-model:modelValue="value" font-size="var(--hr-font-size-4xl)" font-weight="var(--hr-font-weight-semibold)" />
</template>

<script setup>
import { ref } from 'vue'
import { HLInput } from '@platform-ui/highrise'

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

Prefix

Add content before the input text.

$
Please Input
vue
<template>
  <HLInput v-model:modelValue="value">
    <template #prefix>$</template>
  </HLInput>
</template>

<script setup>
import { ref } from 'vue'
import { HLInput } from '@platform-ui/highrise'

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

Suffix

Add content after the input text.

Please Input
$
vue
<template>
  <HLInput v-model:modelValue="value">
    <template #suffix>$</template>
  </HLInput>
</template>

<script setup>
import { ref } from 'vue'
import { HLInput } from '@platform-ui/highrise'

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

Disabled state

Input field in disabled state, preventing user interaction.

Please Input
vue
<template>
  <HLInput v-model:modelValue="value" disabled />
</template>

<script setup>
import { ref } from 'vue'
import { HLInput } from '@platform-ui/highrise'

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

Readonly state

Input field in readonly state, allowing only text selection.

vue
<template>
  <HLInput v-model:modelValue="value" readonly />
</template>

<script setup>
import { ref } from 'vue'
import { HLInput } from '@platform-ui/highrise'

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

Auto size

Input that automatically adjusts its width based on content.

Please Input
 
vue
<template>
  <HLInput v-model:modelValue="value" autosize />
</template>

<script setup>
import { ref } from 'vue'
import { HLInput } from '@platform-ui/highrise'

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

Clearable

Input with a clear button to reset its value.

Please Input
vue
<template>
  <HLInput v-model:modelValue="value" clearable />
</template>

<script setup>
import { ref } from 'vue'
import { HLInput } from '@platform-ui/highrise'

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

Password

Secure input field for password entry with toggle visibility.

Please Input
vue
<template>
  <HLInput v-model:modelValue="value" type="password" />
</template>

<script setup>
import { ref } from 'vue'
import { HLInput } from '@platform-ui/highrise'

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

Password reveal trigger

Use showPasswordOn to choose how the eye icon reveals the value. click (the default) toggles visibility and keeps the value shown until the icon is clicked again. mousedown reveals the value only while the pointer is held down — releasing or moving away hides it again.

vue
<template>
  <!-- Click the eye to toggle; stays visible until clicked again -->
  <HLInput v-model:modelValue="value" type="password" show-password-on="click" />

  <!-- Hold the eye to reveal; hides on release -->
  <HLInput v-model:modelValue="value" type="password" show-password-on="mousedown" />
</template>

<script setup>
import { ref } from 'vue'
import { HLInput } from '@platform-ui/highrise'

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

INFO

showPasswordOn only applies when type="password". On other input types there is no reveal icon for it to control.

Loading

Input field with loading indicator.

Please Input
vue
<template>
  <HLInput v-model:modelValue="value" loading />
</template>

<script setup>
import { ref } from 'vue'
import { HLInput } from '@platform-ui/highrise'

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

Character Limit

Input with maximum character limit enforcement.

Please Input
vue
<template>
  <HLInput v-model:modelValue="value" :maxlength="10" />
</template>

<script setup>
import { ref } from 'vue'
import { HLInput } from '@platform-ui/highrise'

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

Count characters

Input displaying current character count.

Please Input
0
vue
<template>
  <HLInput v-model:modelValue="value" showCount />
</template>

<script setup>
import { ref } from 'vue'
import { HLInput } from '@platform-ui/highrise'

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

Character Limit with Count

Input with both character limit and count display.

Please Input
0 / 10
vue
<template>
  <HLInput v-model:modelValue="value" :maxlength="10" showCount />
</template>

<script setup>
import { ref } from 'vue'
import { HLInput } from '@platform-ui/highrise'

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

Grapheme-aware count

By default, the character count and maxlength/minlength limits are based on the string's length in JavaScript, which counts UTF-16 code units. As a result, a single emoji or accented character made up of multiple code units is counted as more than one character. Provide a countGraphemes function (for example, one built on Intl.Segmenter) to count user-perceived characters (graphemes) instead. In the example below, the family emoji is counted as a single character, in contrast to the Character Limit with Count example above where it would count as several.

1 / 5
vue
<template>
  <HLInput v-model:modelValue="value" :maxlength="5" showCount :countGraphemes="countGraphemes" />
</template>

<script setup>
import { ref } from 'vue'
import { HLInput } from '@platform-ui/highrise'

const value = ref('👨‍👩‍👧‍👦')

// Count user-perceived characters (graphemes) instead of code units.
const countGraphemes = (input) => [...new Intl.Segmenter().segment(input)].length
</script>

INFO

countGraphemes only has an effect alongside the props it customises: showCount (for the count display) and maxlength (for limit enforcement). On its own it does nothing.

Leading icon

Input with an icon at the start.

Please Input
vue
<template>
  <HLInput v-model:modelValue="value" :prefixIcon="Mail01Icon" />
</template>

<script setup>
import { ref } from 'vue'
import { HLInput } from '@platform-ui/highrise'
import { Mail01Icon } from '@gohighlevel/ghl-icons/24/outline'

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

Payment input

Input with credit card logo prefix for payment forms.

credit card
Please Input
vue
<template>
  <HLInput v-model:modelValue="value">
    <template #prefix>
      <div class="flex items-center" :style="{ width: '40px', height: '40px' }">
        <img
          src="https://download.logo.wine/logo/Mastercard/Mastercard-Logo.wine.png"
          alt="credit card"
          class="w-full h-full object-contain"
        />
      </div>
    </template>
  </HLInput>
</template>

<script setup>
import { ref } from 'vue'
import { HLInput } from '@platform-ui/highrise'

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

Leading text

Input with text prefix for URL or similar inputs.

https://
Please Input
vue
<template>
  <HLInput v-model:modelValue="value">
    <template #prefix>https://</template>
  </HLInput>
</template>

<script setup>
import { ref } from 'vue'
import { HLInput } from '@platform-ui/highrise'

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

Trailing button

Input with an action button appended.

Please Input
vue
<template>
  <HLInputGroup>
    <HLInput v-model:modelValue="value" />
    <HLButton :size="24" variant="secondary">
      <HLIcon style="margin-right:4px;fill:var(--gray-700)" :size="20">
        <Copy05Icon style="color:var(--gray-700);stroke-width: 2.5;" />
      </HLIcon>
      <HLText :size="20" weight="semibold">Copy</HLText>
    </HLButton>
  </HLInputGroup>
</template>

<script setup>
import { ref } from 'vue'
import { HLInput, HLInputGroup, HLButton, HLText } from '@platform-ui/highrise'
import { Copy05Icon } from '@gohighlevel/ghl-icons/24/outline'

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

Leading dropdown

Input with a dropdown menu at the start.

Please Input
vue
<template>
  <HLInput v-model:modelValue="inputValue">
    <template #prefix>
      <HLDropdown @update:show="showDropdown" :disabled="false" :size="24" :options="options" @select="selectedValue">
        <div class="flex items-center">
          <span size="xs">{{ value }}</span>
          <HLIcon
            :style="{
              transform: isDropdownOpen ? 'rotate(180deg)' : 'rotate(0deg)',
              transition: 'all 0.3s',
              marginLeft: '4px',
            }"
            :size="24"
          >
            <ChevronDownIcon />
          </HLIcon>
        </div>
      </HLDropdown>
    </template>
  </HLInput>
</template>

<script setup>
import { ref } from 'vue'
import { HLInput, HLDropdown, HLIcon } from '@platform-ui/highrise'
import { ChevronDownIcon } from '@gohighlevel/ghl-icons/24/outline'
import { options } from './options'

const inputValue = ref('')
const value = ref('Option 1')
const isDropdownOpen = ref(false)

const selectedValue = temp => {
  value.value = temp.label
}

const showDropdown = val => {
  isDropdownOpen.value = val
}
</script>
ts
export const options = [
  { key: 'option1', label: 'Option 1' },
  { key: 'option2', label: 'Option 2' },
  { key: 'option3', label: 'Option 3' },
  { key: 'option4', label: 'Option 4' },
]

Trailing dropdown

Input with a dropdown menu at the end.

Please Input
vue
<template>
  <HLInput v-model:modelValue="inputValue" :suffix-icon="HelpCircleIcon" suffix-icon-tooltip-content="Help">
    <template #suffix>
      <HLDropdown @update:show="showDropdown" :disabled="false" :size="24" :options="options" @select="selectedValue">
        <div class="flex items-center">
          <span size="xs">{{ value }}</span>
          <HLIcon
            :style="{
              transform: isDropdownOpen ? 'rotate(180deg)' : 'rotate(0deg)',
              transition: 'all 0.3s',
              marginLeft: '4px',
            }"
            :size="24"
          >
            <ChevronDownIcon />
          </HLIcon>
        </div>
      </HLDropdown>
    </template>
  </HLInput>
</template>

<script setup>
import { ref } from 'vue'
import { HLInput, HLDropdown, HLIcon } from '@platform-ui/highrise'
import { ChevronDownIcon, HelpCircleIcon } from '@gohighlevel/ghl-icons/24/outline'
import { options } from './options'

const inputValue = ref('')
const value = ref('Option 1')
const isDropdownOpen = ref(false)

const selectedValue = temp => {
  value.value = temp.label
}

const showDropdown = val => {
  isDropdownOpen.value = val
}
</script>
ts
export const options = [
  { key: 'option1', label: 'Option 1' },
  { key: 'option2', label: 'Option 2' },
  { key: 'option3', label: 'Option 3' },
  { key: 'option4', label: 'Option 4' },
]

Event Testing

This example shows how to test the events the component emits. Try the following:

  • Type text to see the @update:modelValue event, then blur the field to see @change
  • Press a key to test the @keydown event
  • Focus and blur the input to test the @focus and @blur events

Migration note

@onBlur has been removed. Use the native @blur event instead.

Test events here...

Event Log:

No events logged yet. Try the actions above.
vue
<template>
  <HLInput
    v-model:modelValue="inputModelValue"
    placeholder="Test events here..."
    clearable
    @change="addEventLog('Change event triggered')"
    @keydown="addEventLog('Keydown event triggered')"
    @focus="addEventLog('Focus event triggered')"
    @blur="addEventLog('Blur event triggered')"
    @update:modelValue="addEventLog(`Value updated: ${$event}`)"
  />
  <div class="text-sm">
    <p class="font-bold mb-2">Event Log:</p>
    <div v-if="eventLog.length === 0" class="text-gray-500">No events logged yet. Try the actions above.</div>
    <div v-for="(log, index) in eventLog" :key="index" class="text-gray-700">{{ log.timestamp }}: {{ log.event }}</div>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { HLInput } from '@platform-ui/highrise'

const inputModelValue = ref('')
const eventLog = ref<{ event: string; timestamp: string }[]>([])

const addEventLog = (event: string) => {
  eventLog.value.unshift({ event, timestamp: new Date().toLocaleTimeString() })
  // Keep only last 5 events
  if (eventLog.value.length > 5) {
    eventLog.value.pop()
  }
}
</script>

Accessibility

  • Connect the field to visible labels via id / for or supply aria-label when the label is hidden.
  • Reference helper and error copy through aria-describedby, and toggle aria-invalid when validation fails.
  • Manage suggestion lists with aria-expanded, aria-controls, and (when applicable) aria-autocomplete so assistive tech understands the relationship.
  • Use inputProps prop to pass attributes to the internal input element (<input>, or <textarea> when type="textarea").

Imports

ts
import { HLInput } from '@platform-ui/highrise'

Props

NameTypeDefaultDescription
id *string | undefinedundefinedThe id of the element
modelValuestring''The value of the input
type'text' | 'textarea' | 'password' | 'tel' | 'email' | 'url''text'The type of the input
readonlybooleanfalseIf true, the input is read-only
rowsnumber3Number of rows for textarea
clearablebooleanfalseIf true, a clear button is shown
size'lg' | 'md' | 'sm' | 'xs' | '2xs' | '3xs''sm'The size of the input
placeholderstring | undefinedundefinedPlaceholder text for the input
autosizeboolean | AutosizeConfig | undefinedundefinedIf true, the textarea auto-resizes
maxlengthnumber | undefinedundefinedMaximum number of characters allowed
minlengthnumber | undefinedundefinedMinimum number of characters required
loadingboolean | undefinedundefinedIf true, a loading indicator is shown
showCountbooleanfalseIf true, character count is shown
countGraphemes((value: string) => number) | undefinedundefinedCounts characters as graphemes (emoji/combining marks count as one) for the count display and maxlength enforcement
disabledboolean | undefinedundefinedIf true, the input is disabled
autofocusbooleanfalseIf true, the input is focused on mount
inputPropsObjectundefinedAdditional attributes applied to the native input element
showPasswordOn'mousedown' | 'click''click'Event that reveals the value when type="password"
prefixIconstring | Component | undefinedundefinedIcon shown at the start — an image src or an icon component
suffixIconstring | Component | 'dropdown' | undefinedundefinedIcon shown at the end — an image src, an icon component, or 'dropdown'
suffixIconTooltipContentstring | undefinedundefinedTooltip content of suffix icon
textAlign'start' | 'center' | 'end''start'Text and placeholder alignment of the input
fontSizestring | undefinedundefinedFont size of the input
fontWeightstring | undefinedundefinedFont weight of the input

Types

ts
// Configuration for auto-sizing textarea
interface AutosizeConfig {
  minRows?: number
  maxRows?: number
}

Type Details

AutosizeConfig

  • minRows: Minimum number of rows for textarea
  • maxRows: Maximum number of rows for textarea

Emits

NameDefaultTrigger
@update:modelValue(value: string | [string, string] | number | null) => voidWhen the input value changes
@change(value: string) => voidWhen native change event is fired
@focus(FocusEvent) => voidWhen the input is focused
@blur(FocusEvent) => voidWhen the input is blurred
@keydown(event: KeyboardEvent) => voidWhen the keydown event is fired

Slots

NameParametersDescription
prefix()Content shown before the input
suffix()Content shown after the input

Methods

MethodTypeDescription
focus()() => voidFocuses the input
blur()() => voidBlurs the input
select()() => voidSelects the input
scrollTo(value: number)(value: number) => voidScrolls the input to a specific position