# ion-input

URL: https://ionicframework.com/docs/api/input

**Encapsulation**: [scoped](/docs/reference/glossary.md#scoped)

The input component is a wrapper to the HTML input element with custom styling and additional functionality. It accepts most of the same properties as the HTML input and integrates with the keyboard on mobile devices.

## Basic Usage

**JavaScript**

```html
<ion-list>
  <ion-item>
    <ion-input label="Default input"></ion-input>
  </ion-item>

  <ion-item>
    <ion-input label="Input with placeholder" placeholder="Enter company name"></ion-input>
  </ion-item>

  <ion-item>
    <ion-input label="Input with value" value="121 S Pinckney St #300"></ion-input>
  </ion-item>

  <ion-item>
    <ion-input label="Readonly input" value="Madison" readonly="true"></ion-input>
  </ion-item>

  <ion-item>
    <ion-input label="Disabled input" value="53703" disabled="true"></ion-input>
  </ion-item>
</ion-list>
```

**Angular**

`src/app/example.component.html`

```html
<ion-list>
  <ion-item>
    <ion-input label="Default input"></ion-input>
  </ion-item>

  <ion-item>
    <ion-input label="Input with placeholder" placeholder="Enter company name"></ion-input>
  </ion-item>

  <ion-item>
    <ion-input label="Input with value" value="121 S Pinckney St #300"></ion-input>
  </ion-item>

  <ion-item>
    <ion-input label="Readonly input" value="Madison" [readonly]="true"></ion-input>
  </ion-item>

  <ion-item>
    <ion-input label="Disabled input" value="53703" [disabled]="true"></ion-input>
  </ion-item>
</ion-list>
```

`src/app/example.component.ts`

```ts
import { Component } from '@angular/core';
import { IonInput, IonItem, IonList } from '@ionic/angular';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [IonInput, IonItem, IonList],
})
export class ExampleComponent {}
```

**React**

```tsx
import React from 'react';
import { IonInput, IonItem, IonList } from '@ionic/react';

function Example() {
  return (
    <IonList>
      <IonItem>
        <IonInput label="Default input"></IonInput>
      </IonItem>

      <IonItem>
        <IonInput label="Input with placeholder" placeholder="Enter company name"></IonInput>
      </IonItem>

      <IonItem>
        <IonInput label="Input with value" value="121 S Pinckney St #300"></IonInput>
      </IonItem>

      <IonItem>
        <IonInput label="Readonly input" value="Madison" readonly={true}></IonInput>
      </IonItem>

      <IonItem>
        <IonInput label="Disabled input" value="53703" disabled={true}></IonInput>
      </IonItem>
    </IonList>
  );
}
export default Example;
```

**Vue**

```html
<template>
  <ion-list>
    <ion-item>
      <ion-input label="Default input"></ion-input>
    </ion-item>

    <ion-item>
      <ion-input label="Input with placeholder" placeholder="Enter company name"></ion-input>
    </ion-item>

    <ion-item>
      <ion-input label="Input with value" value="121 S Pinckney St #300"></ion-input>
    </ion-item>

    <ion-item>
      <ion-input label="Readonly input" value="Madison" :readonly="true"></ion-input>
    </ion-item>

    <ion-item>
      <ion-input label="Disabled input" value="53703" :disabled="true"></ion-input>
    </ion-item>
  </ion-list>
</template>

<script setup lang="ts">
  import { IonInput, IonItem, IonList } from '@ionic/vue';
</script>
```

## Types

The input component is meant for text type inputs only, such as `"text"`, `"password"`, `"email"`, `"number"`, `"search"`, `"tel"`, and `"url"`. It supports all standard text input events including `keyup`, `keydown`, `keypress`, and more. The default `type` is `"text"`.

**JavaScript**

```html
<ion-list>
  <ion-list>
    <ion-item>
      <ion-input label="Text input" placeholder="Enter text"></ion-input>
    </ion-item>

    <ion-item>
      <ion-input label="Number input" type="number" placeholder="000"></ion-input>
    </ion-item>

    <ion-item>
      <ion-input label="Password input" type="password" value="password"></ion-input>
    </ion-item>

    <ion-item>
      <ion-input label="Email input" type="email" placeholder="email@domain.com"></ion-input>
    </ion-item>

    <ion-item>
      <ion-input label="Telephone input" type="tel" placeholder="888-888-8888"></ion-input>
    </ion-item>
  </ion-list>
</ion-list>
```

**Angular**

`src/app/example.component.html`

```html
<ion-list>
  <ion-list>
    <ion-item>
      <ion-input label="Text input" placeholder="Enter text"></ion-input>
    </ion-item>

    <ion-item>
      <ion-input label="Number input" type="number" placeholder="000"></ion-input>
    </ion-item>

    <ion-item>
      <ion-input label="Password input" type="password" value="password"></ion-input>
    </ion-item>

    <ion-item>
      <ion-input label="Email input" type="email" placeholder="email@domain.com"></ion-input>
    </ion-item>

    <ion-item>
      <ion-input label="Telephone input" type="tel" placeholder="888-888-8888"></ion-input>
    </ion-item>
  </ion-list>
</ion-list>
```

`src/app/example.component.ts`

```ts
import { Component } from '@angular/core';
import { IonInput, IonItem, IonList } from '@ionic/angular';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [IonInput, IonItem, IonList],
})
export class ExampleComponent {}
```

**React**

```tsx
import React from 'react';
import { IonInput, IonItem, IonList } from '@ionic/react';

function Example() {
  return (
    <IonList>
      <IonItem>
        <IonInput label="Text input" placeholder="Enter text"></IonInput>
      </IonItem>

      <IonItem>
        <IonInput label="Number input" type="number" placeholder="000"></IonInput>
      </IonItem>

      <IonItem>
        <IonInput label="Password input" type="password" value="password"></IonInput>
      </IonItem>

      <IonItem>
        <IonInput label="Email input" type="email" placeholder="email@domain.com"></IonInput>
      </IonItem>

      <IonItem>
        <IonInput label="Telephone input" type="tel" placeholder="888-888-8888"></IonInput>
      </IonItem>
    </IonList>
  );
}
export default Example;
```

**Vue**

```html
<template>
  <ion-list>
    <ion-item>
      <ion-input label="Text input" placeholder="Enter text"></ion-input>
    </ion-item>

    <ion-item>
      <ion-input label="Number input" type="number" placeholder="000"></ion-input>
    </ion-item>

    <ion-item>
      <ion-input label="Password input" type="password" value="password"></ion-input>
    </ion-item>

    <ion-item>
      <ion-input label="Email input" type="email" placeholder="email@domain.com"></ion-input>
    </ion-item>

    <ion-item>
      <ion-input label="Telephone input" type="tel" placeholder="888-888-8888"></ion-input>
    </ion-item>
  </ion-list>
</template>

<script setup lang="ts">
  import { IonInput, IonItem, IonList } from '@ionic/vue';
</script>
```

## Labels

Labels should be used to describe the input. They can be used visually, and they will also be read out by screen readers when the user is focused on the input. This makes it easy for the user to understand the intent of the input. Input has several ways to assign a label:

- `label` property: used for plaintext labels
- `label` slot: used for custom HTML labels (experimental)
- `aria-label`: used to provide a label for screen readers but adds no visible label

### Label Placement

Labels will take up the width of their content by default. Developers can use the `labelPlacement` property to control how the label is placed relative to the control.

**JavaScript**

```html
<ion-list>
  <ion-item>
    <ion-input label="Default label" placeholder="Enter text"></ion-input>
  </ion-item>

  <ion-item>
    <ion-input label="Fixed label" label-placement="fixed" placeholder="Enter text"></ion-input>
  </ion-item>

  <ion-item>
    <ion-input label="Stacked label" label-placement="stacked" placeholder="Enter text"></ion-input>
  </ion-item>

  <ion-item>
    <ion-input label="Floating label" label-placement="floating" placeholder="Enter text"></ion-input>
  </ion-item>
</ion-list>
```

**Angular**

`src/app/example.component.html`

```html
<ion-list>
  <ion-item>
    <ion-input label="Default label" placeholder="Enter text"></ion-input>
  </ion-item>

  <ion-item>
    <ion-input label="Fixed label" labelPlacement="fixed" placeholder="Enter text"></ion-input>
  </ion-item>

  <ion-item>
    <ion-input label="Stacked label" labelPlacement="stacked" placeholder="Enter text"></ion-input>
  </ion-item>

  <ion-item>
    <ion-input label="Floating label" labelPlacement="floating" placeholder="Enter text"></ion-input>
  </ion-item>
</ion-list>
```

`src/app/example.component.ts`

```ts
import { Component } from '@angular/core';
import { IonInput, IonItem, IonList } from '@ionic/angular';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [IonInput, IonItem, IonList],
})
export class ExampleComponent {}
```

**React**

```tsx
import React from 'react';
import { IonInput, IonItem, IonList } from '@ionic/react';

function Example() {
  return (
    <IonList>
      <IonItem>
        <IonInput label="Default label" placeholder="Enter text"></IonInput>
      </IonItem>

      <IonItem>
        <IonInput label="Fixed label" labelPlacement="fixed" placeholder="Enter text"></IonInput>
      </IonItem>

      <IonItem>
        <IonInput label="Stacked label" labelPlacement="stacked" placeholder="Enter text"></IonInput>
      </IonItem>

      <IonItem>
        <IonInput label="Floating label" labelPlacement="floating" placeholder="Enter text"></IonInput>
      </IonItem>
    </IonList>
  );
}
export default Example;
```

**Vue**

```html
<template>
  <ion-list>
    <ion-item>
      <ion-input label="Default label" placeholder="Enter text"></ion-input>
    </ion-item>

    <ion-item>
      <ion-input label="Fixed label" label-placement="fixed" placeholder="Enter text"></ion-input>
    </ion-item>

    <ion-item>
      <ion-input label="Stacked label" label-placement="stacked" placeholder="Enter text"></ion-input>
    </ion-item>

    <ion-item>
      <ion-input label="Floating label" label-placement="floating" placeholder="Enter text"></ion-input>
    </ion-item>
  </ion-list>
</template>

<script setup lang="ts">
  import { IonInput, IonItem, IonList } from '@ionic/vue';
</script>
```

### Label Slot (experimental)

While plaintext labels should be passed in via the `label` property, if custom HTML is needed, it can be passed through the `label` slot instead.

Note that this feature is considered experimental because it relies on a simulated version of [Web Component slots](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_templates_and_slots). As a result, the simulated behavior may not exactly match the native slot behavior.

**JavaScript**

```html
<ion-list>
  <ion-item>
    <ion-input label-placement="floating" value="hi@ionic.io">
      <div slot="label">Email <ion-text color="danger">(Required)</ion-text></div>
    </ion-input>
  </ion-item>
</ion-list>
```

**Angular**

`src/app/example.component.html`

```html
<ion-list>
  <ion-item>
    <ion-input labelPlacement="floating" value="hi@ionic.io">
      <div slot="label">Email <ion-text color="danger">(Required)</ion-text></div>
    </ion-input>
  </ion-item>
</ion-list>
```

`src/app/example.component.ts`

```ts
import { Component } from '@angular/core';
import { IonInput, IonItem, IonList, IonText } from '@ionic/angular';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [IonInput, IonItem, IonList, IonText],
})
export class ExampleComponent {}
```

**React**

```tsx
import React from 'react';
import { IonInput, IonItem, IonList, IonText } from '@ionic/react';

function Example() {
  return (
    <IonList>
      <IonItem>
        <IonInput labelPlacement="floating" value="hi@ionic.io">
          <div slot="label">
            Email <IonText color="danger">(Required)</IonText>
          </div>
        </IonInput>
      </IonItem>
    </IonList>
  );
}
export default Example;
```

**Vue**

```html
<template>
  <ion-list>
    <ion-item>
      <ion-input label-placement="floating" value="hi@ionic.io">
        <div slot="label">Email <ion-text color="danger">(Required)</ion-text></div>
      </ion-input>
    </ion-item>
  </ion-list>
</template>

<script setup lang="ts">
  import { IonInput, IonItem, IonList, IonText } from '@ionic/vue';
</script>
```

### No Visible Label

If no visible label is needed, developers should still supply an `aria-label` so the input is accessible to screen readers.

**JavaScript**

```html
<ion-list>
  <ion-item>
    <ion-input aria-label="Email" value="hi@ionic.io"></ion-input>
  </ion-item>
</ion-list>
```

**Angular**

`src/app/example.component.html`

```html
<ion-list>
  <ion-item>
    <ion-input aria-label="Email" value="hi@ionic.io"></ion-input>
  </ion-item>
</ion-list>
```

`src/app/example.component.ts`

```ts
import { Component } from '@angular/core';
import { IonInput, IonItem, IonList } from '@ionic/angular';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [IonInput, IonItem, IonList],
})
export class ExampleComponent {}
```

**React**

```tsx
import React from 'react';
import { IonInput, IonItem, IonList } from '@ionic/react';

function Example() {
  return (
    <IonList>
      <IonItem>
        <IonInput aria-label="Email" value="hi@ionic.io"></IonInput>
      </IonItem>
    </IonList>
  );
}
export default Example;
```

**Vue**

```html
<template>
  <ion-list>
    <ion-item>
      <ion-input aria-label="Email" value="hi@ionic.io"></ion-input>
    </ion-item>
  </ion-list>
</template>

<script setup lang="ts">
  import { IonInput, IonItem, IonList } from '@ionic/vue';
</script>
```

## Clear Options

Inputs offer two options for clearing the input based on how you interact with it. The first way is by adding the `clearInput` property which will show a clear button when the input has a `value`. The second way is the `clearOnEdit` property which will clear the input after it has been blurred and then typed in again. Inputs with a `type` set to `"password"` will have `clearOnEdit` enabled by default.

**JavaScript**

```html
<ion-list>
  <ion-item>
    <ion-input
      label="Input with clear button"
      label-placement="stacked"
      clear-input="true"
      placeholder="Enter text to see clear button"
      value="Default value"
    >
    </ion-input>
  </ion-item>

  <ion-item>
    <ion-input
      label="Input with clear on edit"
      label-placement="stacked"
      clear-on-edit="true"
      placeholder="Enter text, leave the input, come back and type to clear"
    >
    </ion-input>
  </ion-item>

  <ion-item>
    <ion-input
      label="Password input"
      label-placement="stacked"
      type="password"
      placeholder="Enter text, leave the input, come back and type to clear"
    >
    </ion-input>
  </ion-item>
</ion-list>
```

**Angular**

`src/app/example.component.html`

```html
<ion-list>
  <ion-item>
    <ion-input
      label="Input with clear button"
      labelPlacement="stacked"
      [clearInput]="true"
      placeholder="Enter text to see clear button"
      value="Default value"
    >
    </ion-input>
  </ion-item>

  <ion-item>
    <ion-input
      label="Input with clear on edit"
      labelPlacement="stacked"
      [clearOnEdit]="true"
      placeholder="Enter text, leave the input, come back and type to clear"
    >
    </ion-input>
  </ion-item>

  <ion-item>
    <ion-input
      label="Password input"
      labelPlacement="stacked"
      type="password"
      placeholder="Enter text, leave the input, come back and type to clear"
    >
    </ion-input>
  </ion-item>
</ion-list>
```

`src/app/example.component.ts`

```ts
import { Component } from '@angular/core';
import { IonInput, IonItem, IonList } from '@ionic/angular';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [IonInput, IonItem, IonList],
})
export class ExampleComponent {}
```

**React**

```tsx
import React from 'react';
import { IonInput, IonItem, IonList } from '@ionic/react';

function Example() {
  return (
    <IonList>
      <IonItem>
        <IonInput
          label="Input with clear button"
          labelPlacement="stacked"
          clearInput={true}
          placeholder="Enter text to see clear button"
          value="Default value"
        ></IonInput>
      </IonItem>

      <IonItem>
        <IonInput
          label="Input with clear on edit"
          labelPlacement="stacked"
          clearOnEdit={true}
          placeholder="Enter text, leave the input, come back and type to clear"
        ></IonInput>
      </IonItem>

      <IonItem>
        <IonInput
          label="Password input"
          labelPlacement="stacked"
          type="password"
          placeholder="Enter text, leave the input, come back and type to clear"
        ></IonInput>
      </IonItem>
    </IonList>
  );
}
export default Example;
```

**Vue**

```html
<template>
  <ion-list>
    <ion-item>
      <ion-input
        label="Input with clear button"
        label-placement="stacked"
        :clear-input="true"
        placeholder="Enter text to see clear button"
        value="Default value"
      >
      </ion-input>
    </ion-item>

    <ion-item>
      <ion-input
        label="Input with clear on edit"
        label-placement="stacked"
        :clear-on-edit="true"
        placeholder="Enter text, leave the input, come back and type to clear"
      >
      </ion-input>
    </ion-item>

    <ion-item>
      <ion-input
        label="Password input"
        label-placement="stacked"
        type="password"
        placeholder="Enter text, leave the input, come back and type to clear"
      >
      </ion-input>
    </ion-item>
  </ion-list>
</template>

<script setup lang="ts">
  import { IonInput, IonItem, IonList } from '@ionic/vue';
</script>
```

## Filled Inputs

Material Design offers filled styles for an input. The `fill` property on the input can be set to either `"solid"` or `"outline"`.

Filled inputs can be used on iOS by setting the input's `mode` to `md`.

**Warning**

Inputs that use `fill` should not be used in an `ion-item` due to styling conflicts between the components.

**JavaScript**

```html
<ion-input label="Solid input" label-placement="floating" fill="solid" placeholder="Enter text"></ion-input>

<br />

<ion-input label="Outline input" label-placement="floating" fill="outline" placeholder="Enter text"></ion-input>
```

**Angular**

`src/app/example.component.html`

```html
<ion-input label="Solid input" label-placement="floating" fill="solid" placeholder="Enter text"></ion-input>

<br />

<ion-input label="Outline input" label-placement="floating" fill="outline" placeholder="Enter text"></ion-input>
```

`src/app/example.component.ts`

```ts
import { Component } from '@angular/core';
import { IonInput } from '@ionic/angular';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [IonInput],
})
export class ExampleComponent {}
```

**React**

```tsx
import React from 'react';
import { IonInput } from '@ionic/react';

function Example() {
  return (
    <>
      <IonInput label="Solid input" labelPlacement="floating" fill="solid" placeholder="Enter text"></IonInput>

      <br />

      <IonInput label="Outline input" labelPlacement="floating" fill="outline" placeholder="Enter text"></IonInput>
    </>
  );
}
export default Example;
```

**Vue**

```html
<template>
  <ion-input label="Solid input" label-placement="floating" fill="solid" placeholder="Enter text"></ion-input>

  <br />

  <ion-input label="Outline input" label-placement="floating" fill="outline" placeholder="Enter text"></ion-input>
</template>

<script setup lang="ts">
  import { IonInput } from '@ionic/vue';
</script>
```

## Helper & Error Text

Helper and error text can be used inside of an input with the `helperText` and `errorText` property. The error text will not be displayed unless the `ion-invalid` and `ion-touched` classes are added to the `ion-input`. This ensures errors are not shown before the user has a chance to enter data.

In Angular, this is done automatically through form validation. In JavaScript, React and Vue, the class needs to be manually added based on your own validation.

**JavaScript**

```html
<ion-input
  type="email"
  fill="solid"
  label="Email"
  label-placement="floating"
  helper-text="Enter a valid email"
  error-text="Invalid email"
></ion-input>

<script>
  const input = document.querySelector('ion-input');

  input.addEventListener('ionInput', (event) => validate(event));
  input.addEventListener('ionBlur', () => markTouched());

  const validateEmail = (email) => {
    return email.match(
      /^(?=.{1,254}$)(?=.{1,64}@)[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
    );
  };

  const validate = (event) => {
    const value = event.target.value;

    input.classList.remove('ion-valid');
    input.classList.remove('ion-invalid');

    if (value === '') return;

    validateEmail(value) ? input.classList.add('ion-valid') : input.classList.add('ion-invalid');
  };

  const markTouched = () => {
    input.classList.add('ion-touched');
  };
</script>
```

**Angular**

`src/app/example.component.html`

```html
<ion-input
  type="email"
  fill="solid"
  label="Email"
  labelPlacement="floating"
  helperText="Enter a valid email"
  errorText="Invalid email"
  ngModel
  email
></ion-input>
```

`src/app/example.component.ts`

```ts
import { Component } from '@angular/core';
import { IonInput } from '@ionic/angular';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [IonInput],
})
export class ExampleComponent {}
```

**React**

```tsx
import React, { useState } from 'react';
import { IonInput } from '@ionic/react';

function Example() {
  const [isTouched, setIsTouched] = useState(false);
  const [isValid, setIsValid] = useState<boolean>();

  const validateEmail = (email: string) => {
    return email.match(
      /^(?=.{1,254}$)(?=.{1,64}@)[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
    );
  };

  const validate = (event: Event) => {
    const value = (event.target as HTMLInputElement).value;

    setIsValid(undefined);

    if (value === '') return;

    validateEmail(value) !== null ? setIsValid(true) : setIsValid(false);
  };

  const markTouched = () => {
    setIsTouched(true);
  };

  return (
    <IonInput
      className={`${isValid && 'ion-valid'} ${isValid === false && 'ion-invalid'} ${isTouched && 'ion-touched'}`}
      type="email"
      fill="solid"
      label="Email"
      labelPlacement="floating"
      helperText="Enter a valid email"
      errorText="Invalid email"
      onIonInput={(event) => validate(event)}
      onIonBlur={() => markTouched()}
    ></IonInput>
  );
}
export default Example;
```

**Vue**

```html
<template>
  <ion-input
    ref="input"
    type="email"
    fill="solid"
    label="Email"
    label-placement="floating"
    helper-text="Enter a valid email"
    error-text="Invalid email"
    @ionInput="validate"
    @ionBlur="markTouched"
  ></ion-input>
</template>

<script setup lang="ts">
  import { IonInput } from '@ionic/vue';
  import { ref } from 'vue';

  const input = ref();

  const validateEmail = (email) => {
    return email.match(
      /^(?=.{1,254}$)(?=.{1,64}@)[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
    );
  };

  const validate = (event) => {
    const value = event.target.value;

    input.value.$el.classList.remove('ion-valid');
    input.value.$el.classList.remove('ion-invalid');

    if (value === '') return;

    validateEmail(value) ? input.value.$el.classList.add('ion-valid') : input.value.$el.classList.add('ion-invalid');
  };

  const markTouched = () => {
    input.value.$el.classList.add('ion-touched');
  };
</script>
```

## Input Counter

The input counter is text that displays under an input to notify the user of how many characters have been entered out of the total that the input will accept. When adding counter, the default behavior is to format the value that gets displayed as `inputLength` / `maxLength`. This behavior can be customized by passing in a formatter function to the `counterFormatter` property.

The `counter` and `counterFormatter` properties on `ion-item` were [deprecated in Ionic 7](https://ionic-docs-mt82qcyb0-ionic1.vercel.app/docs/v7/api/input#using-the-modern-syntax) and should be used directly on `ion-input` instead.

**JavaScript**

```html
<ion-input label="Default counter" label-placement="floating" counter="true" maxlength="20"></ion-input>

<ion-input
  id="custom-input"
  label="Custom Counter Format"
  label-placement="floating"
  counter="true"
  maxlength="20"
></ion-input>

<script>
  const customInput = document.querySelector('#custom-input');
  customInput.counterFormatter = (inputLength, maxLength) => `${maxLength - inputLength} characters remaining`;
</script>
```

**Angular**

`src/app/example.component.html`

```html
<ion-input label="Default counter" labelPlacement="floating" [counter]="true" maxlength="20"></ion-input>

<ion-input
  id="custom-input"
  label="Custom Counter Format"
  labelPlacement="floating"
  [counter]="true"
  maxlength="20"
  [counterFormatter]="customCounterFormatter"
></ion-input>
```

`src/app/example.component.ts`

```ts
import { Component } from '@angular/core';
import { IonInput } from '@ionic/angular';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [IonInput],
})
export class ExampleComponent {
  customCounterFormatter(inputLength: number, maxLength: number) {
    return `${maxLength - inputLength} characters remaining`;
  }
}
```

**React**

```tsx
import React from 'react';
import { IonInput } from '@ionic/react';

function Example() {
  return (
    <>
      <IonInput label="Default counter" labelPlacement="floating" counter={true} maxlength={20}></IonInput>

      <IonInput
        id="custom-input"
        label="Custom Counter Format"
        labelPlacement="floating"
        counter={true}
        maxlength={20}
        counterFormatter={(inputLength, maxLength) => `${maxLength - inputLength} characters remaining`}
      ></IonInput>
    </>
  );
}
export default Example;
```

**Vue**

```html
<template>
  <ion-input label="Default counter" label-placement="floating" :counter="true" :maxlength="20"></ion-input>

  <ion-input
    id="custom-input"
    label="Custom Counter Format"
    label-placement="floating"
    :counter="true"
    :maxlength="20"
    :counter-formatter="customFormatter"
  ></ion-input>
</template>

<script setup lang="ts">
  import { IonInput } from '@ionic/vue';

  const customFormatter = (inputLength, maxLength) => {
    return `${maxLength - inputLength} characters remaining`;
  };
</script>
```

Inputs with a counter add a border between the input and the counter, therefore they should not be placed inside of an `ion-item` which adds an additional border under the item. The `ion-padding-start` class can be added to align the counter inputs with inputs inside of items.

**JavaScript**

```html
<ion-list>
  <ion-item>
    <ion-input label="Text input" placeholder="Enter text"></ion-input>
  </ion-item>

  <div class="ion-padding-start">
    <ion-input label="Counter input" counter="true" maxlength="20"></ion-input>
  </div>
</ion-list>
```

**Angular**

`src/app/example.component.html`

```html
<ion-list>
  <ion-item>
    <ion-input label="Text input" placeholder="Enter text"></ion-input>
  </ion-item>

  <div class="ion-padding-start">
    <ion-input label="Counter input" [counter]="true" maxlength="20"></ion-input>
  </div>
</ion-list>
```

`src/app/example.component.ts`

```ts
import { Component } from '@angular/core';
import { IonInput, IonItem, IonList } from '@ionic/angular';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [IonInput, IonItem, IonList],
})
export class ExampleComponent {}
```

**React**

```tsx
import React from 'react';
import { IonInput, IonItem, IonList } from '@ionic/react';

function Example() {
  return (
    <>
      <IonList>
        <IonItem>
          <IonInput label="Text input" placeholder="Enter text"></IonInput>
        </IonItem>

        <div className="ion-padding-start">
          <IonInput label="Counter input" counter={true} maxlength={20}></IonInput>
        </div>
      </IonList>
    </>
  );
}
export default Example;
```

**Vue**

```html
<template>
  <ion-list>
    <ion-item>
      <ion-input label="Text input" placeholder="Enter text"></ion-input>
    </ion-item>

    <div class="ion-padding-start">
      <ion-input label="Counter input" :counter="true" maxlength="20"></ion-input>
    </div>
  </ion-list>
</template>

<script setup lang="ts">
  import { IonInput, IonItem, IonList } from '@ionic/vue';
</script>
```

## Filtering User Input

Developers can use the `ionInput` event to update the input value in response to user input such as a `keypress`. This is useful for filtering out invalid or unwanted characters.

When storing the value in a state variable, we recommend updating both the state variable and the `ion-input` component value. This ensures that the state variable and the `ion-input` component value remain in sync.

**JavaScript**

```html
<ion-list>
  <ion-item>
    <ion-input label="Alphanumeric Characters"></ion-input>
  </ion-item>
</ion-list>

<script>
  var input = document.querySelector('ion-input');
  input.addEventListener('ionInput', (event) => {
    const value = event.target.value;
    // Removes non alphanumeric characters
    input.value = value.replace(/[^a-zA-Z0-9]+/g, '');
  });
</script>
```

**Angular**

`src/app/example.component.html`

```html
<ion-list>
  <ion-item>
    <ion-input
      label="Alphanumeric Characters"
      [value]="inputModel"
      (ionInput)="onInput($event)"
      #ionInputEl
    ></ion-input>
  </ion-item>
</ion-list>
```

`src/app/example.component.ts`

```ts
import { Component, ViewChild } from '@angular/core';
import { IonInput, IonItem, IonList } from '@ionic/angular';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [IonInput, IonItem, IonList],
})
export class ExampleComponent {
  inputModel = '';

  @ViewChild('ionInputEl', { static: true }) ionInputEl!: IonInput;

  onInput(event: CustomEvent) {
    const value = (event.target as HTMLIonInputElement).value ?? '';

    // Removes non alphanumeric characters
    const filteredValue = (value as string).replace(/[^a-zA-Z0-9]+/g, '');

    /**
     * Update both the state variable and
     * the component to keep them in sync.
     */
    this.ionInputEl.value = this.inputModel = filteredValue;
  }
}
```

**React**

```tsx
import React, { useState, useRef } from 'react';
import { IonInput, IonItem, IonList } from '@ionic/react';

function Example() {
  const [inputModel, setInputModel] = useState('');
  const ionInputEl = useRef<HTMLIonInputElement>(null);

  const onInput = (event: Event) => {
    const value = (event.target as HTMLIonInputElement).value as string;

    // Removes non alphanumeric characters
    const filteredValue = value.replace(/[^a-zA-Z0-9]+/g, '');

    /**
     * Update both the state variable and
     * the component to keep them in sync.
     */
    setInputModel(filteredValue);

    const inputCmp = ionInputEl.current;
    if (inputCmp !== null) {
      inputCmp.value = filteredValue;
    }
  };

  return (
    <IonList>
      <IonItem>
        <IonInput label="Alphanumeric Characters" value={inputModel} onIonInput={onInput} ref={ionInputEl}></IonInput>
      </IonItem>
    </IonList>
  );
}
export default Example;
```

**Vue**

```html
<template>
  <ion-list>
    <ion-item>
      <ion-input
        label="Alphanumeric Characters"
        :value="inputModel"
        @ionInput="onInput($event)"
        ref="ionInputEl"
      ></ion-input>
    </ion-item>
  </ion-list>
</template>

<script setup lang="ts">
  import { IonInput, IonItem, IonList } from '@ionic/vue';
  import { ref } from 'vue';

  const ionInputEl = ref();
  const inputModel = ref('');
  const onInput = (event) => {
    const value = event.target!.value;

    // Removes non alphanumeric characters
    const filteredValue = value.replace(/[^a-zA-Z0-9]+/g, '');

    /**
     * Update both the state variable and
     * the component to keep them in sync.
     */
    inputModel.value = filteredValue;

    const inputCmp = ionInputEl.value;
    if (inputCmp !== undefined) {
      inputCmp.$el.value = filteredValue;
    }
  };
</script>
```

## Input Masking

Input masks are expressions that constrain input to support valid input values. Ionic recommends using [Maskito](https://maskito.dev) for input masking. Maskito is a lightweight, dependency-free library for masking input fields. It supports a wide range of masks, including phone numbers, credit cards, dates, and more.

To get started with Maskito, install the library:

```bash
npm install @maskito/core @maskito/{angular,react,vue}
```

**JavaScript**

`index.html`

```html
<ion-list>
  <ion-item>
    <ion-input id="card" label="Card number" placeholder="0000 0000 0000 0000"></ion-input>
  </ion-item>
  <ion-item>
    <ion-input id="phone" label="US phone number" placeholder="+1 (xxx) xxx-xxxx"></ion-input>
  </ion-item>
</ion-list>

<script>
  async function initPhoneMask() {
    const ionInput = document.querySelector('#phone');
    const nativeEl = await ionInput.getInputElement();
    const phoneMaskOptions = {
      mask: ['+', '1', ' ', '(', /\d/, /\d/, /\d/, ')', ' ', /\d/, /\d/, /\d/, '-', /\d/, /\d/, /\d/, /\d/],
    };
    new window.Maskito(nativeEl, phoneMaskOptions);

    //If you need to set an initial value, you can use maskitoTransform to ensure the value is valid
    ionInput.value = window.maskitoTransform('5555551212', phoneMaskOptions);
  }

  async function initCardMask() {
    const ionInput = document.querySelector('#card');
    const nativeEl = await ionInput.getInputElement();

    new window.Maskito(nativeEl, {
      mask: [
        ...Array(4).fill(/\d/),
        ' ',
        ...Array(4).fill(/\d/),
        ' ',
        ...Array(4).fill(/\d/),
        ' ',
        ...Array(4).fill(/\d/),
        ' ',
        ...Array(3).fill(/\d/),
      ],
    });
  }

  window.addEventListener('appload', () => {
    initCardMask();
    initPhoneMask();
  });
</script>
```

`index.ts`

```ts
import { defineCustomElements } from '@ionic/core/loader';

import { Maskito, maskitoTransform } from '@maskito/core';

/* Core CSS required for Ionic components to work properly */
import '@ionic/core/css/core.css';

/* Basic CSS for apps built with Ionic */
import '@ionic/core/css/normalize.css';
import '@ionic/core/css/structure.css';
import '@ionic/core/css/typography.css';

/* Optional CSS utils that can be commented out */
import '@ionic/core/css/padding.css';
import '@ionic/core/css/float-elements.css';
import '@ionic/core/css/text-alignment.css';
import '@ionic/core/css/text-transformation.css';
import '@ionic/core/css/flex-utils.css';
import '@ionic/core/css/display.css';

/**
 * Ionic Dark Palette
 * -----------------------------------------------------
 * For more information, please see:
 * https://ionicframework.com/docs/theming/dark-mode
 */

// import '@ionic/core/css/palettes/dark.always.css';
// import '@ionic/core/css/palettes/dark.class.css';
import '@ionic/core/css/palettes/dark.system.css';

/* Theme variables */
import './theme/variables.css';

defineCustomElements();

(window as any).Maskito = Maskito;
(window as any).maskitoTransform = maskitoTransform;
```

**Angular**

`src/app/example.component.html`

```html
<ion-list>
  <ion-item>
    <ion-input
      label="Card number"
      placeholder="0000 0000 0000 0000"
      [maskito]="cardMask"
      [maskitoElement]="maskPredicate"
    ></ion-input>
  </ion-item>
  <ion-item>
    <ion-input
      label="US phone number"
      placeholder="+1 (xxx) xxx-xxxx"
      [(ngModel)]="myPhoneNumber"
      [maskito]="phoneMask"
      [maskitoElement]="maskPredicate"
    ></ion-input>
  </ion-item>
</ion-list>
```

`src/app/example.component.ts`

```ts
import { Component } from '@angular/core';
import { IonInput, IonItem, IonList } from '@ionic/angular';
import { FormsModule } from '@angular/forms';

import { MaskitoOptions, MaskitoElementPredicate, maskitoTransform } from '@maskito/core';
import { MaskitoDirective } from '@maskito/angular';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [FormsModule, IonInput, IonItem, IonList, MaskitoDirective],
})
export class ExampleComponent {
  readonly phoneMask: MaskitoOptions = {
    mask: ['+', '1', ' ', '(', /\d/, /\d/, /\d/, ')', ' ', /\d/, /\d/, /\d/, '-', /\d/, /\d/, /\d/, /\d/],
  };

  //If you need to set an initial value, you can use maskitoTransform to ensure the value is valid
  myPhoneNumber = maskitoTransform('5555551212', this.phoneMask);

  readonly cardMask: MaskitoOptions = {
    mask: [
      ...Array(4).fill(/\d/),
      ' ',
      ...Array(4).fill(/\d/),
      ' ',
      ...Array(4).fill(/\d/),
      ' ',
      ...Array(4).fill(/\d/),
      ' ',
      ...Array(3).fill(/\d/),
    ],
  };

  readonly maskPredicate: MaskitoElementPredicate = async (el) => (el as HTMLIonInputElement).getInputElement();
}
```

**React**

`src/main.tsx`

```tsx
import { useState } from 'react';
import { IonInput, IonItem, IonList } from '@ionic/react';

import { MaskitoOptions, maskitoTransform } from '@maskito/core';
import { useMaskito } from '@maskito/react';

function Example() {
  const cardMask = useMaskito({
    options: {
      mask: [
        ...Array(4).fill(/\d/),
        ' ',
        ...Array(4).fill(/\d/),
        ' ',
        ...Array(4).fill(/\d/),
        ' ',
        ...Array(4).fill(/\d/),
        ' ',
        ...Array(3).fill(/\d/),
      ],
    },
  });

  const phoneMaskOptions: MaskitoOptions = {
    mask: ['+', '1', ' ', '(', /\d/, /\d/, /\d/, ')', ' ', /\d/, /\d/, /\d/, '-', /\d/, /\d/, /\d/, /\d/],
  };
  const phoneMask = useMaskito({ options: phoneMaskOptions });

  //If you need to set an initial value, you can use maskitoTransform to ensure the value is valid
  const [myPhoneNumber, setMyPhoneNumber] = useState(maskitoTransform('5555551212', phoneMaskOptions));

  return (
    <IonList>
      <IonItem>
        <IonInput
          ref={async (cardRef) => {
            if (cardRef) {
              const input = await cardRef.getInputElement();
              cardMask(input);
            }
          }}
          label="Card number"
          placeholder="0000 0000 0000 0000"
        ></IonInput>
      </IonItem>
      <IonItem>
        <IonInput
          ref={async (phoneInput) => {
            if (phoneInput) {
              const input = await phoneInput.getInputElement();
              phoneMask(input);
            }
          }}
          value={myPhoneNumber}
          onIonInput={(e) => setMyPhoneNumber(e.detail.value || '')}
          label="US phone number"
          placeholder="+1 (xxx) xxx-xxxx"
        ></IonInput>
      </IonItem>
    </IonList>
  );
}
export default Example;
```

**Vue**

`src/components/Example.vue`

```html
<template>
  <ion-list>
    <ion-item>
      <ion-input label="Card number" placeholder="0000 0000 0000 0000" v-maskito="cardOptions"></ion-input>
    </ion-item>
    <ion-item>
      <ion-input
        label="US phone number"
        placeholder="+1 (xxx) xxx-xxxx"
        v-model="myPhoneNumber"
        v-maskito="phoneOptions"
      ></ion-input>
    </ion-item>
  </ion-list>
</template>

<script setup lang="ts">
  import { ref } from 'vue';
  import { IonInput, IonItem, IonList } from '@ionic/vue';
  import { maskito as vMaskito } from '@maskito/vue';
  import { maskitoTransform } from '@maskito/core';

  const cardOptions = {
    mask: [
      ...Array(4).fill(/\d/),
      ' ',
      ...Array(4).fill(/\d/),
      ' ',
      ...Array(4).fill(/\d/),
      ' ',
      ...Array(4).fill(/\d/),
      ' ',
      ...Array(3).fill(/\d/),
    ],
    elementPredicate: (el: HTMLIonInputElement) => {
      return new Promise((resolve) => {
        requestAnimationFrame(async () => {
          const input = await el.getInputElement();
          resolve(input);
        });
      });
    },
  };

  const phoneOptions = {
    mask: ['+', '1', ' ', '(', /\d/, /\d/, /\d/, ')', ' ', /\d/, /\d/, /\d/, '-', /\d/, /\d/, /\d/, /\d/],
    elementPredicate: (el: HTMLIonInputElement) => {
      return new Promise((resolve) => {
        requestAnimationFrame(async () => {
          const input = await el.getInputElement();
          resolve(input);
        });
      });
    },
  };
  // If you need to set an initial value, you can use maskitoTransform to ensure the value is valid
  const myPhoneNumber = ref(maskitoTransform('5555551212', phoneOptions));
</script>
```

**Note**

Please submit bug reports with Maskito to the [Maskito Github repository](https://github.com/taiga-family/maskito/issues). For technical support, please use the [Ionic Forum](https://forum.ionicframework.com/) or [Ionic Discord](http://chat.ionicframework.com/).

## Start and End Slots (experimental)

The `start` and `end` slots can be used to place icons, buttons, or prefix/suffix text on either side of the input.

Note that this feature is considered experimental because it relies on a simulated version of [Web Component slots](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_templates_and_slots). As a result, the simulated behavior may not exactly match the native slot behavior.

**Note**

In most cases, [Icon](/docs/api/icon.md) components placed in these slots should have `aria-hidden="true"`. Refer to the [Icon accessibility docs](https://ionicframework.com/docs/api/icon#accessibility) for more information. If slot content is meant to be interacted with, it should be wrapped in an interactive element such as a [Button](/docs/api/button.md). This ensures that the content can be tabbed to.

**JavaScript**

`index.html`

```html
<ion-list>
  <ion-item>
    <ion-input label-placement="stacked" label="Email" placeholder="email@domain.com">
      <ion-icon slot="start" name="lock-closed" aria-hidden="true"></ion-icon>
      <ion-button fill="clear" slot="end" aria-label="Show/hide">
        <ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon>
      </ion-button>
    </ion-input>
  </ion-item>
</ion-list>
```

`index.ts`

```ts
import { defineCustomElements } from '@ionic/core/loader';

import { addIcons } from 'ionicons';
import { eye, lockClosed } from 'ionicons/icons';

/* Core CSS required for Ionic components to work properly */
import '@ionic/core/css/core.css';

/* Basic CSS for apps built with Ionic */
import '@ionic/core/css/normalize.css';
import '@ionic/core/css/structure.css';
import '@ionic/core/css/typography.css';

/* Optional CSS utils that can be commented out */
import '@ionic/core/css/padding.css';
import '@ionic/core/css/float-elements.css';
import '@ionic/core/css/text-alignment.css';
import '@ionic/core/css/text-transformation.css';
import '@ionic/core/css/flex-utils.css';
import '@ionic/core/css/display.css';

/**
 * Ionic Dark Palette
 * -----------------------------------------------------
 * For more information, please see:
 * https://ionicframework.com/docs/theming/dark-mode
 */

// import '@ionic/core/css/palettes/dark.always.css';
// import '@ionic/core/css/palettes/dark.class.css';
import '@ionic/core/css/palettes/dark.system.css';

/* Theme variables */
import './theme/variables.css';

/**
 * On Ionicons 7.2+ these icons
 * get mapped to a kebab-case key.
 * Alternatively, developers can do:
 * addIcons({ 'eye': eye, 'lock-closed': lockClosed });
 */
addIcons({ eye, lockClosed });

defineCustomElements();
```

**Angular**

`src/app/example.component.html`

```html
<ion-list>
  <ion-item>
    <ion-input labelPlacement="stacked" label="Email" placeholder="email@domain.com">
      <ion-icon slot="start" name="lock-closed" aria-hidden="true"></ion-icon>
      <ion-button fill="clear" slot="end" aria-label="Show/hide">
        <ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon>
      </ion-button>
    </ion-input>
  </ion-item>
</ion-list>
```

`src/app/example.component.ts`

```ts
import { Component } from '@angular/core';
import { IonButton, IonIcon, IonInput, IonItem, IonList } from '@ionic/angular';

import { addIcons } from 'ionicons';
import { eye, lockClosed } from 'ionicons/icons';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [IonButton, IonIcon, IonInput, IonItem, IonList],
})
export class ExampleComponent {
  constructor() {
    /**
     * Any icons you want to use in your application
     * can be registered in app.component.ts and then
     * referenced by name anywhere in your application.
     */
    addIcons({ eye, lockClosed });
  }
}
```

**React**

```tsx
import React from 'react';
import { IonButton, IonIcon, IonInput, IonItem, IonList } from '@ionic/react';
import { eye, lockClosed } from 'ionicons/icons';

function Example() {
  return (
    <IonList>
      <IonItem>
        <IonInput labelPlacement="stacked" label="Email" placeholder="email@domain.com">
          <IonIcon slot="start" icon={lockClosed} aria-hidden="true"></IonIcon>
          <IonButton fill="clear" slot="end" aria-label="Show/hide">
            <IonIcon slot="icon-only" icon={eye} aria-hidden="true"></IonIcon>
          </IonButton>
        </IonInput>
      </IonItem>
    </IonList>
  );
}
export default Example;
```

**Vue**

```html
<template>
  <ion-list>
    <ion-item>
      <ion-input label-placement="stacked" label="Email" placeholder="email@domain.com">
        <ion-icon slot="start" :icon="lockClosed" aria-hidden="true"></ion-icon>
        <ion-button fill="clear" slot="end" aria-label="Show/hide">
          <ion-icon slot="icon-only" :icon="eye" aria-hidden="true"></ion-icon>
        </ion-button>
      </ion-input>
    </ion-item>
  </ion-list>
</template>

<script setup lang="ts">
  import { IonButton, IonIcon, IonInput, IonItem, IonList } from '@ionic/vue';
  import { eye, lockClosed } from 'ionicons/icons';
</script>
```

## Theming

### Colors

Setting the `color` property changes the color palette for each input. On `ios` mode, this property changes the caret color. On `md` mode, this property changes the caret color and the highlight/underline color.

**Note**

The `color` property does *not* change the text color of the input. For that, use the [`--color` CSS property](#css-custom-properties-1).

**JavaScript**

```html
<ion-input aria-label="Primary input" color="primary" placeholder="Primary input"></ion-input>
<ion-input aria-label="Secondary input" color="secondary" placeholder="Secondary input"></ion-input>
<ion-input aria-label="Tertiary input" color="tertiary" placeholder="Tertiary input"></ion-input>
<ion-input aria-label="Success input" color="success" placeholder="Success input"></ion-input>
<ion-input aria-label="Warning input" color="warning" placeholder="Warning input"></ion-input>
<ion-input aria-label="Danger input" color="danger" placeholder="Danger input"></ion-input>
<ion-input aria-label="Light input" color="light" placeholder="Light input"></ion-input>
<ion-input aria-label="Medium input" color="medium" placeholder="Medium input"></ion-input>
<ion-input aria-label="Dark input" color="dark" placeholder="Dark input"></ion-input>
```

**Angular**

`src/app/example.component.html`

```html
<ion-input aria-label="Primary input" color="primary" placeholder="Primary input"></ion-input>
<ion-input aria-label="Secondary input" color="secondary" placeholder="Secondary input"></ion-input>
<ion-input aria-label="Tertiary input" color="tertiary" placeholder="Tertiary input"></ion-input>
<ion-input aria-label="Success input" color="success" placeholder="Success input"></ion-input>
<ion-input aria-label="Warning input" color="warning" placeholder="Warning input"></ion-input>
<ion-input aria-label="Danger input" color="danger" placeholder="Danger input"></ion-input>
<ion-input aria-label="Light input" color="light" placeholder="Light input"></ion-input>
<ion-input aria-label="Medium input" color="medium" placeholder="Medium input"></ion-input>
<ion-input aria-label="Dark input" color="dark" placeholder="Dark input"></ion-input>
```

`src/app/example.component.ts`

```ts
import { Component } from '@angular/core';
import { IonInput } from '@ionic/angular';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [IonInput],
})
export class ExampleComponent {}
```

**React**

```tsx
import React from 'react';
import { IonInput } from '@ionic/react';

function Example() {
  return (
    <>
      <IonInput aria-label="Primary input" color="primary" placeholder="Primary input"></IonInput>
      <IonInput aria-label="Secondary input" color="secondary" placeholder="Secondary input"></IonInput>
      <IonInput aria-label="Tertiary input" color="tertiary" placeholder="Tertiary input"></IonInput>
      <IonInput aria-label="Success input" color="success" placeholder="Success input"></IonInput>
      <IonInput aria-label="Warning input" color="warning" placeholder="Warning input"></IonInput>
      <IonInput aria-label="Danger input" color="danger" placeholder="Danger input"></IonInput>
      <IonInput aria-label="Light input" color="light" placeholder="Light input"></IonInput>
      <IonInput aria-label="Medium input" color="medium" placeholder="Medium input"></IonInput>
      <IonInput aria-label="Dark input" color="dark" placeholder="Dark input"></IonInput>
    </>
  );
}
export default Example;
```

**Vue**

```html
<template>
  <ion-input aria-label="Primary input" color="primary" placeholder="Primary input"></ion-input>
  <ion-input aria-label="Secondary input" color="secondary" placeholder="Secondary input"></ion-input>
  <ion-input aria-label="Tertiary input" color="tertiary" placeholder="Tertiary input"></ion-input>
  <ion-input aria-label="Success input" color="success" placeholder="Success input"></ion-input>
  <ion-input aria-label="Warning input" color="warning" placeholder="Warning input"></ion-input>
  <ion-input aria-label="Danger input" color="danger" placeholder="Danger input"></ion-input>
  <ion-input aria-label="Light input" color="light" placeholder="Light input"></ion-input>
  <ion-input aria-label="Medium input" color="medium" placeholder="Medium input"></ion-input>
  <ion-input aria-label="Dark input" color="dark" placeholder="Dark input"></ion-input>
</template>

<script setup lang="ts">
  import { IonInput } from '@ionic/vue';
</script>
```

### CSS Custom Properties

Input uses scoped encapsulation, which means it will automatically scope its CSS by appending each of the styles with an additional class at runtime. Overriding scoped selectors in CSS requires a [higher specificity](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity) selector. Targeting the `ion-input` for customization will not work; therefore we recommend adding a class and customizing it that way.

**JavaScript**

```html
<ion-input
  aria-label="Custom input"
  placeholder="Custom input"
  class="custom"
  helper-text="Helper text"
  counter="true"
  maxlength="20"
></ion-input>

<style>
  ion-input.custom {
    --background: #373737;
    --color: #fff;
    --placeholder-color: #ddd;
    --placeholder-opacity: 0.8;

    --padding-bottom: 10px;
    --padding-end: 10px;
    --padding-start: 10px;
    --padding-top: 10px;
  }

  ion-input.custom.ios .input-bottom .helper-text,
  ion-input.custom.ios .input-bottom .counter,
  ion-input.custom.md .input-bottom .helper-text,
  ion-input.custom.md .input-bottom .counter {
    color: var(--ion-color-primary);
  }
</style>
```

**Angular**

`src/app/example.component.html`

```html
<ion-input
  aria-label="Custom input"
  placeholder="Custom input"
  class="custom"
  helperText="Helper text"
  [counter]="true"
  [maxlength]="20"
></ion-input>
```

`src/app/example.component.css`

```css
ion-input.custom {
  --background: #373737;
  --color: #fff;
  --placeholder-color: #ddd;
  --placeholder-opacity: 0.8;

  --padding-bottom: 10px;
  --padding-end: 10px;
  --padding-start: 10px;
  --padding-top: 10px;
}
```

`src/app/example.component.ts`

```ts
import { Component } from '@angular/core';
import { IonInput } from '@ionic/angular';

@Component({
  selector: 'app-example',
  templateUrl: 'example.component.html',
  styleUrls: ['example.component.css'],
  imports: [IonInput],
})
export class ExampleComponent {}
```

`src/global.css`

```css
/**
 * Since we are styling elements inside of ion-input
 * we need to move this CSS to a global stylesheet. Otherwise,
 * Angular components with encapsulation enabled will add
 * scoped style attributes to these selectors.
 */
ion-input.custom.ios .input-bottom .helper-text,
ion-input.custom.ios .input-bottom .counter,
ion-input.custom.md .input-bottom .helper-text,
ion-input.custom.md .input-bottom .counter {
  color: var(--ion-color-primary);
}
```

**React**

`src/main.tsx`

```tsx
import React from 'react';
import { IonInput } from '@ionic/react';

import './main.css';

function Example() {
  return (
    <IonInput
      aria-label="Custom input"
      placeholder="Custom input"
      class="custom"
      helperText="Helper text"
      counter={true}
      maxlength={20}
    ></IonInput>
  );
}
export default Example;
```

`src/main.css`

```css
ion-input.custom {
  --background: #373737;
  --color: #fff;
  --placeholder-color: #ddd;
  --placeholder-opacity: 0.8;

  --padding-bottom: 10px;
  --padding-end: 10px;
  --padding-start: 10px;
  --padding-top: 10px;
}

ion-input.custom.ios .input-bottom .helper-text,
ion-input.custom.ios .input-bottom .counter,
ion-input.custom.md .input-bottom .helper-text,
ion-input.custom.md .input-bottom .counter {
  color: var(--ion-color-primary);
}
```

**Vue**

```html
<template>
  <ion-input
    aria-label="Custom input"
    placeholder="Custom input"
    class="custom"
    helper-text="Helper text"
    :counter="true"
    :maxlength="20"
  ></ion-input>
</template>

<script setup lang="ts">
  import { IonInput } from '@ionic/vue';
</script>

<style>
  ion-input.custom {
    --background: #373737;
    --color: #fff;
    --placeholder-color: #ddd;
    --placeholder-opacity: 0.8;

    --padding-bottom: 10px;
    --padding-end: 10px;
    --padding-start: 10px;
    --padding-top: 10px;
  }

  ion-input.custom.ios .input-bottom .helper-text,
  ion-input.custom.ios .input-bottom .counter,
  ion-input.custom.md .input-bottom .helper-text,
  ion-input.custom.md .input-bottom .counter {
    color: var(--ion-color-primary);
  }
</style>
```

## Interfaces

### InputChangeEventDetail

```typescript
interface InputChangeEventDetail {
  value: string | undefined | null;
}
```

### InputCustomEvent

While not required, this interface can be used in place of the `CustomEvent` interface for stronger typing with Ionic events emitted from this component.

```typescript
interface InputCustomEvent extends CustomEvent {
  detail: InputChangeEventDetail;
  target: HTMLIonInputElement;
}
```

## Properties

### autocapitalize

**Description**: Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`.

**Attribute**: `autocapitalize`

**Type**: `string`

**Default**: `'off'`

### autocomplete

**Description**: Indicates whether the value of the control can be automatically completed by the browser.

**Attribute**: `autocomplete`

**Type**: `"additional-name" | "address-level1" | "address-level2" | "address-level3" | "address-level4" | "address-line1" | "address-line2" | "address-line3" | "bday" | "bday-day" | "bday-month" | "bday-year" | "cc-additional-name" | "cc-csc" | "cc-exp" | "cc-exp-month" | "cc-exp-year" | "cc-family-name" | "cc-given-name" | "cc-name" | "cc-number" | "cc-type" | "country" | "country-name" | "current-password" | "email" | "family-name" | "given-name" | "honorific-prefix" | "honorific-suffix" | "impp" | "language" | "name" | "new-password" | "nickname" | "off" | "on" | "one-time-code" | "organization" | "organization-title" | "photo" | "postal-code" | "sex" | "street-address" | "tel" | "tel-area-code" | "tel-country-code" | "tel-extension" | "tel-local" | "tel-national" | "transaction-amount" | "transaction-currency" | "url" | "username"`

**Default**: `'off'`

### autocorrect

**Description**: Whether auto correction should be enabled when the user is entering/editing the text value.

**Attribute**: `autocorrect`

**Type**: `boolean`

**Default**: `false`

### autofocus

**Description**: Sets the [`autofocus` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus) on the native input element. This may not be sufficient for the element to be focused on page load. See [managing focus](/docs/developing/managing-focus.md) for more information.

**Attribute**: `autofocus`

**Type**: `boolean`

**Default**: `false`

### clearInput

**Description**: If `true`, a clear icon will appear in the input when there is a value. Clicking it clears the input.

**Attribute**: `clear-input`

**Type**: `boolean`

**Default**: `false`

### clearInputIcon

**Description**: The icon to use for the clear button. Only applies when `clearInput` is set to `true`.

**Attribute**: `clear-input-icon`

**Type**: `string | undefined`

**Default**: `undefined`

### clearOnEdit

**Description**: If `true`, the value will be cleared after focus upon edit. Defaults to `true` when `type` is `"password"`, `false` for all other types.

**Attribute**: `clear-on-edit`

**Type**: `boolean | undefined`

**Default**: `undefined`

### color

**Description**: The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics.md).

**Attribute**: `color`

**Type**: `"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string | undefined`

**Default**: `undefined`

### counter

**Description**: If `true`, a character counter will display the ratio of characters used and the total character limit. Developers must also set the `maxlength` property for the counter to be calculated correctly.

**Attribute**: `counter`

**Type**: `boolean`

**Default**: `false`

### counterFormatter

**Description**: A callback used to format the counter text. By default the counter text is set to "itemLength / maxLength". See [https://ionicframework.com/docs/troubleshooting/runtime#accessing-this](https://ionicframework.com/docs/troubleshooting/runtime#accessing-this) if you need to access `this` from within the callback.

**Attribute**: `undefined`

**Type**: `((inputLength: number, maxLength: number) => string) | undefined`

**Default**: `undefined`

### debounce

**Description**: Set the amount of time, in milliseconds, to wait to trigger the `ionInput` event after each keystroke.

**Attribute**: `debounce`

**Type**: `number | undefined`

**Default**: `undefined`

### disabled

**Description**: If `true`, the user cannot interact with the input.

**Attribute**: `disabled`

**Type**: `boolean`

**Default**: `false`

### enterkeyhint

**Description**: A hint to the browser for which enter key to display. Possible values: `"enter"`, `"done"`, `"go"`, `"next"`, `"previous"`, `"search"`, and `"send"`.

**Attribute**: `enterkeyhint`

**Type**: `"done" | "enter" | "go" | "next" | "previous" | "search" | "send" | undefined`

**Default**: `undefined`

### errorText

**Description**: Text that is placed under the input and displayed when an error is detected.

**Attribute**: `error-text`

**Type**: `string | undefined`

**Default**: `undefined`

### fill

**Description**: The fill for the item. If `"solid"` the item will have a background. If `"outline"` the item will be transparent with a border. Only available in `md` mode.

**Attribute**: `fill`

**Type**: `"outline" | "solid" | undefined`

**Default**: `undefined`

### helperText

**Description**: Text that is placed under the input and displayed when no error is detected.

**Attribute**: `helper-text`

**Type**: `string | undefined`

**Default**: `undefined`

### inputmode

**Description**: A hint to the browser for which keyboard to display. Possible values: `"none"`, `"text"`, `"tel"`, `"url"`, `"email"`, `"numeric"`, `"decimal"`, and `"search"`.

**Attribute**: `inputmode`

**Type**: `"decimal" | "email" | "none" | "numeric" | "search" | "tel" | "text" | "url" | undefined`

**Default**: `undefined`

### label

**Description**: The visible label associated with the input. Use this if you need to render a plaintext label. The `label` property will take priority over the `label` slot if both are used.

**Attribute**: `label`

**Type**: `string | undefined`

**Default**: `undefined`

### labelPlacement

**Description**: Where to place the label relative to the input. `"start"`: The label will appear to the left of the input in LTR and to the right in RTL. `"end"`: The label will appear to the right of the input in LTR and to the left in RTL. `"floating"`: The label will appear smaller and above the input when the input is focused or it has a value. Otherwise it will appear on top of the input. `"stacked"`: The label will appear smaller and above the input regardless even when the input is blurred or has no value. `"fixed"`: The label has the same behavior as `"start"` except it also has a fixed width. Long text will be truncated with ellipses ("...").

**Attribute**: `label-placement`

**Type**: `"end" | "fixed" | "floating" | "stacked" | "start"`

**Default**: `'start'`

### max

**Description**: The maximum value, which must not be less than its minimum (min attribute) value.

**Attribute**: `max`

**Type**: `number | string | undefined`

**Default**: `undefined`

### maxlength

**Description**: If the value of the type attribute is `text`, `email`, `search`, `password`, `tel`, or `url`, this attribute specifies the maximum number of characters that the user can enter.

**Attribute**: `maxlength`

**Type**: `number | undefined`

**Default**: `undefined`

### min

**Description**: The minimum value, which must not be greater than its maximum (max attribute) value. This is a [virtual property](/docs/core-concepts/fundamentals.md#virtual-properties) that is set once during initialization and will not update if you change its value after the initial render.

**Attribute**: `min`

**Type**: `number | string | undefined`

**Default**: `undefined`

### minlength

**Description**: If the value of the type attribute is `text`, `email`, `search`, `password`, `tel`, or `url`, this attribute specifies the minimum number of characters that the user can enter.

**Attribute**: `minlength`

**Type**: `number | undefined`

**Default**: `undefined`

### mode

**Description**: The mode determines which platform styles to use. This is a [virtual property](/docs/core-concepts/fundamentals.md#virtual-properties) that is set once during initialization and will not update if you change its value after the initial render.

**Attribute**: `mode`

**Type**: `"ios" | "md"`

**Default**: `undefined`

### multiple

**Description**: If `true`, the user can enter more than one value. This attribute applies when the type attribute is set to `"email"`, otherwise it is ignored.

**Attribute**: `multiple`

**Type**: `boolean | undefined`

**Default**: `undefined`

### name

**Description**: The name of the control, which is submitted with the form data.

**Attribute**: `name`

**Type**: `string`

**Default**: `this.inputId`

### pattern

**Description**: A regular expression that the value is checked against. The pattern must match the entire value, not just some subset. Use the title attribute to describe the pattern to help the user. This attribute applies when the value of the type attribute is `"text"`, `"search"`, `"tel"`, `"url"`, `"email"`, `"date"`, or `"password"`, otherwise it is ignored. When the type attribute is `"date"`, `pattern` will only be used in browsers that do not support the `"date"` input type natively. See [https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date) for more information.

**Attribute**: `pattern`

**Type**: `string | undefined`

**Default**: `undefined`

### placeholder

**Description**: Instructional text that shows before the input has a value. This property applies only when the `type` property is set to `"email"`, `"number"`, `"password"`, `"search"`, `"tel"`, `"text"`, or `"url"`, otherwise it is ignored.

**Attribute**: `placeholder`

**Type**: `string | undefined`

**Default**: `undefined`

### readonly

**Description**: If `true`, the user cannot modify the value.

**Attribute**: `readonly`

**Type**: `boolean`

**Default**: `false`

### required

**Description**: If `true`, the user must fill in a value before submitting a form.

**Attribute**: `required`

**Type**: `boolean`

**Default**: `false`

### shape

**Description**: The shape of the input. If "round" it will have an increased border radius.

**Attribute**: `shape`

**Type**: `"round" | undefined`

**Default**: `undefined`

### spellcheck

**Description**: If `true`, the element will have its spelling and grammar checked.

**Attribute**: `spellcheck`

**Type**: `boolean`

**Default**: `false`

### step

**Description**: Works with the min and max attributes to limit the increments at which a value can be set. Possible values are: `"any"` or a positive floating point number.

**Attribute**: `step`

**Type**: `string | undefined`

**Default**: `undefined`

### type

**Description**: The type of control to display. The default type is text.

**Attribute**: `type`

**Type**: `"date" | "datetime-local" | "email" | "month" | "number" | "password" | "search" | "tel" | "text" | "time" | "url" | "week"`

**Default**: `'text'`

### value

**Description**: The value of the input.

**Attribute**: `value`

**Type**: `null | number | string | undefined`

**Default**: `''`

## Events

| Name | Description | Bubbles |
| --- | --- | --- |
| `ionBlur` | Emitted when the input loses focus. | `true` |
| `ionChange` | The `ionChange` event is fired when the user modifies the input's value. Unlike the `ionInput` event, the `ionChange` event is only fired when changes are committed, not as the user types. Depending on the way the users interacts with the element, the `ionChange` event fires at a different moment: - When the user commits the change explicitly (e.g. by selecting a date from a date picker for `<ion-input type="date">`, pressing the "Enter" key, etc.). - When the element loses focus after its value has changed: for elements where the user's interaction is typing. This event will not emit when programmatically setting the `value` property. | `true` |
| `ionFocus` | Emitted when the input has focus. | `true` |
| `ionInput` | The `ionInput` event is fired each time the user modifies the input's value. Unlike the `ionChange` event, the `ionInput` event is fired for each alteration to the input's value. This typically happens for each keystroke as the user types. For elements that accept text input (`type=text`, `type=tel`, etc.), the interface is [`InputEvent`](https://developer.mozilla.org/en-US/docs/Web/API/InputEvent); for others, the interface is [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event). If the input is cleared on edit, the type is `null`. | `true` |

## Methods

### getInputElement

**Description**: Returns the native `<input>` element used under the hood.

**Signature**: `getInputElement() => Promise<HTMLInputElement>`

### setFocus

**Description**: Sets focus on the native `input` in `ion-input`. Use this method instead of the global `input.focus()`. Developers who wish to focus an input when a page enters should call `setFocus()` in the `ionViewDidEnter()` lifecycle method. Developers who wish to focus an input when an overlay is presented should call `setFocus` after `didPresent` has resolved. See [managing focus](/docs/developing/managing-focus.md) for more information.

**Signature**: `setFocus() => Promise<void>`

## CSS Shadow Parts

No CSS shadow parts available for this component.

## CSS Custom Properties

**iOS**

| Name | Description |
| --- | --- |
| `--background` | Background of the input |
| `--border-color` | Color of the border below the input when using helper text, error text, or counter |
| `--border-radius` | Radius of the input. A large radius may display unevenly when using fill="outline"; if needed, use shape="round" instead or increase --padding-start. |
| `--border-style` | Style of the border below the input when using helper text, error text, or counter |
| `--border-width` | Width of the border below the input when using helper text, error text, or counter |
| `--color` | Color of the input text |
| `--highlight-color-focused` | The color of the highlight on the input when focused |
| `--highlight-color-invalid` | The color of the highlight on the input when invalid |
| `--highlight-color-valid` | The color of the highlight on the input when valid |
| `--highlight-height` | The height of the highlight on the input. Only applies to md mode. |
| `--padding-bottom` | Bottom padding of the input |
| `--padding-end` | Right padding if direction is left-to-right, and left padding if direction is right-to-left of the input |
| `--padding-start` | Left padding if direction is left-to-right, and right padding if direction is right-to-left of the input |
| `--padding-top` | Top padding of the input |
| `--placeholder-color` | Color of the input placeholder text |
| `--placeholder-font-style` | Font style of the input placeholder text |
| `--placeholder-font-weight` | Font weight of the input placeholder text |
| `--placeholder-opacity` | Opacity of the input placeholder text |

**MD**

| Name | Description |
| --- | --- |
| `--background` | Background of the input |
| `--border-color` | Color of the border below the input when using helper text, error text, or counter |
| `--border-radius` | Radius of the input. A large radius may display unevenly when using fill="outline"; if needed, use shape="round" instead or increase --padding-start. |
| `--border-style` | Style of the border below the input when using helper text, error text, or counter |
| `--border-width` | Width of the border below the input when using helper text, error text, or counter |
| `--color` | Color of the input text |
| `--highlight-color-focused` | The color of the highlight on the input when focused |
| `--highlight-color-invalid` | The color of the highlight on the input when invalid |
| `--highlight-color-valid` | The color of the highlight on the input when valid |
| `--highlight-height` | The height of the highlight on the input. Only applies to md mode. |
| `--padding-bottom` | Bottom padding of the input |
| `--padding-end` | Right padding if direction is left-to-right, and left padding if direction is right-to-left of the input |
| `--padding-start` | Left padding if direction is left-to-right, and right padding if direction is right-to-left of the input |
| `--padding-top` | Top padding of the input |
| `--placeholder-color` | Color of the input placeholder text |
| `--placeholder-font-style` | Font style of the input placeholder text |
| `--placeholder-font-weight` | Font weight of the input placeholder text |
| `--placeholder-opacity` | Opacity of the input placeholder text |

## Slots

| Name | Description |
| --- | --- |
| `end` | Content to display at the trailing edge of the input. (EXPERIMENTAL) |
| `label` | The label text to associate with the input. Use the `labelPlacement` property to control where the label is placed relative to the input. Use this if you need to render a label with custom HTML. (EXPERIMENTAL) |
| `start` | Content to display at the leading edge of the input. (EXPERIMENTAL) |
