---
title: Interactivity API | Browser Mode
---
# Interactivity API
Vitest implements a subset of [`@testing-library/user-event`](https://testing-library.com/docs/user-event/intro) APIs using [Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/) or [webdriver](https://www.w3.org/TR/webdriver/) instead of faking events which makes the browser behaviour more reliable and consistent with how users interact with a page.
```ts
import { userEvent } from '@vitest/browser/context'
await userEvent.click(document.querySelector('.button'))
```
Almost every `userEvent` method inherits its provider options. To see all available options in your IDE, add `webdriver` or `playwright` types (depending on your provider) to your [setup file](/config/#setupfile) or a [config file](/config/) (depending on what is in `included` in your `tsconfig.json`):
::: code-group
```ts [playwright]
///
```
```ts [webdriverio]
///
```
:::
## userEvent.setup
```ts
function setup(): UserEvent
```
Creates a new user event instance. This is useful if you need to keep the state of keyboard to press and release buttons correctly.
::: warning
Unlike `@testing-library/user-event`, the default `userEvent` instance from `@vitest/browser/context` is created once, not every time its methods are called! You can see the difference in how it works in this snippet:
```ts
import { userEvent as vitestUserEvent } from '@vitest/browser/context'
import { userEvent as originalUserEvent } from '@testing-library/user-event'
await vitestUserEvent.keyboard('{Shift}') // press shift without releasing
await vitestUserEvent.keyboard('{/Shift}') // releases shift
await originalUserEvent.keyboard('{Shift}') // press shift without releasing
await originalUserEvent.keyboard('{/Shift}') // DID NOT release shift because the state is different
```
This behaviour is more useful because we do not emulate the keyboard, we actually press the Shift, so keeping the original behaviour would cause unexpected issues when typing in the field.
:::
## userEvent.click
```ts
function click(
element: Element | Locator,
options?: UserEventClickOptions,
): Promise
```
Click on an element. Inherits provider's options. Please refer to your provider's documentation for detailed explanation about how this method works.
```ts
import { page, userEvent } from '@vitest/browser/context'
test('clicks on an element', async () => {
const logo = page.getByRole('img', { name: /logo/ })
await userEvent.click(logo)
// or you can access it directly on the locator
await logo.click()
})
```
References:
- [Playwright `locator.click` API](https://playwright.dev/docs/api/class-locator#locator-click)
- [WebdriverIO `element.click` API](https://webdriver.io/docs/api/element/click/)
- [testing-library `click` API](https://testing-library.com/docs/user-event/convenience/#click)
## userEvent.dblClick
```ts
function dblClick(
element: Element | Locator,
options?: UserEventDoubleClickOptions,
): Promise
```
Triggers a double click event on an element.
Please refer to your provider's documentation for detailed explanation about how this method works.
```ts
import { page, userEvent } from '@vitest/browser/context'
test('triggers a double click on an element', async () => {
const logo = page.getByRole('img', { name: /logo/ })
await userEvent.dblClick(logo)
// or you can access it directly on the locator
await logo.dblClick()
})
```
References:
- [Playwright `locator.dblclick` API](https://playwright.dev/docs/api/class-locator#locator-dblclick)
- [WebdriverIO `element.doubleClick` API](https://webdriver.io/docs/api/element/doubleClick/)
- [testing-library `dblClick` API](https://testing-library.com/docs/user-event/convenience/#dblClick)
## userEvent.tripleClick
```ts
function tripleClick(
element: Element | Locator,
options?: UserEventTripleClickOptions,
): Promise
```
Triggers a triple click event on an element. Since there is no `tripleclick` in browser api, this method will fire three click events in a row, and so you must check [click event detail](https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event#usage_notes) to filter the event: `evt.detail === 3`.
Please refer to your provider's documentation for detailed explanation about how this method works.
```ts
import { page, userEvent } from '@vitest/browser/context'
test('triggers a triple click on an element', async () => {
const logo = page.getByRole('img', { name: /logo/ })
let tripleClickFired = false
logo.addEventListener('click', (evt) => {
if (evt.detail === 3) {
tripleClickFired = true
}
})
await userEvent.tripleClick(logo)
// or you can access it directly on the locator
await logo.tripleClick()
expect(tripleClickFired).toBe(true)
})
```
References:
- [Playwright `locator.click` API](https://playwright.dev/docs/api/class-locator#locator-click): implemented via `click` with `clickCount: 3` .
- [WebdriverIO `browser.action` API](https://webdriver.io/docs/api/browser/action/): implemented via actions api with `move` plus three `down + up + pause` events in a row
- [testing-library `tripleClick` API](https://testing-library.com/docs/user-event/convenience/#tripleClick)
## userEvent.fill
```ts
function fill(
element: Element | Locator,
text: string,
): Promise
```
Set a value to the `input`/`textarea`/`contenteditable` field. This will remove any existing text in the input before setting the new value.
```ts
import { page, userEvent } from '@vitest/browser/context'
test('update input', async () => {
const input = page.getByRole('input')
await userEvent.fill(input, 'foo') // input.value == foo
await userEvent.fill(input, '{{a[[') // input.value == {{a[[
await userEvent.fill(input, '{Shift}') // input.value == {Shift}
// or you can access it directly on the locator
await input.fill('foo') // input.value == foo
})
```
This methods focuses the element, fills it and triggers an `input` event after filling. You can use an empty string to clear the field.
::: tip
This API is faster than using [`userEvent.type`](#userevent-type) or [`userEvent.keyboard`](#userevent-keyboard), but it **doesn't support** [user-event `keyboard` syntax](https://testing-library.com/docs/user-event/keyboard) (e.g., `{Shift}{selectall}`).
We recommend using this API over [`userEvent.type`](#userevent-type) in situations when you don't need to enter special characters or have granular control over keypress events.
:::
References:
- [Playwright `locator.fill` API](https://playwright.dev/docs/api/class-locator#locator-fill)
- [WebdriverIO `element.setValue` API](https://webdriver.io/docs/api/element/setValue)
- [testing-library `type` API](https://testing-library.com/docs/user-event/utility/#type)
## userEvent.keyboard
```ts
function keyboard(text: string): Promise
```
The `userEvent.keyboard` allows you to trigger keyboard strokes. If any input has a focus, it will type characters into that input. Otherwise, it will trigger keyboard events on the currently focused element (`document.body` if there are no focused elements).
This API supports [user-event `keyboard` syntax](https://testing-library.com/docs/user-event/keyboard).
```ts
import { userEvent } from '@vitest/browser/context'
test('trigger keystrokes', async () => {
await userEvent.keyboard('foo') // translates to: f, o, o
await userEvent.keyboard('{{a[[') // translates to: {, a, [
await userEvent.keyboard('{Shift}{f}{o}{o}') // translates to: Shift, f, o, o
await userEvent.keyboard('{a>5}') // press a without releasing it and trigger 5 keydown
await userEvent.keyboard('{a>5/}') // press a for 5 keydown and then release it
})
```
References:
- [Playwright `Keyboard` API](https://playwright.dev/docs/api/class-keyboard)
- [WebdriverIO `action('key')` API](https://webdriver.io/docs/api/browser/action#key-input-source)
- [testing-library `type` API](https://testing-library.com/docs/user-event/utility/#type)
## userEvent.tab
```ts
function tab(options?: UserEventTabOptions): Promise
```
Sends a `Tab` key event. This is a shorthand for `userEvent.keyboard('{tab}')`.
```ts
import { page, userEvent } from '@vitest/browser/context'
test('tab works', async () => {
const [input1, input2] = page.getByRole('input').elements()
expect(input1).toHaveFocus()
await userEvent.tab()
expect(input2).toHaveFocus()
await userEvent.tab({ shift: true })
expect(input1).toHaveFocus()
})
```
References:
- [Playwright `Keyboard` API](https://playwright.dev/docs/api/class-keyboard)
- [WebdriverIO `action('key')` API](https://webdriver.io/docs/api/browser/action#key-input-source)
- [testing-library `tab` API](https://testing-library.com/docs/user-event/convenience/#tab)
## userEvent.type
```ts
function type(
element: Element | Locator,
text: string,
options?: UserEventTypeOptions,
): Promise
```
::: warning
If you don't rely on [special characters](https://testing-library.com/docs/user-event/keyboard) (e.g., `{shift}` or `{selectall}`), it is recommended to use [`userEvent.fill`](#userevent-fill) instead for better performance.
:::
The `type` method implements `@testing-library/user-event`'s [`type`](https://testing-library.com/docs/user-event/utility/#type) utility built on top of [`keyboard`](https://testing-library.com/docs/user-event/keyboard) API.
This function allows you to type characters into an `input`/`textarea`/`contenteditable` element. It supports [user-event `keyboard` syntax](https://testing-library.com/docs/user-event/keyboard).
If you just need to press characters without an input, use [`userEvent.keyboard`](#userevent-keyboard) API.
```ts
import { page, userEvent } from '@vitest/browser/context'
test('update input', async () => {
const input = page.getByRole('input')
await userEvent.type(input, 'foo') // input.value == foo
await userEvent.type(input, '{{a[[') // input.value == foo{a[
await userEvent.type(input, '{Shift}') // input.value == foo{a[
})
```
::: info
Vitest doesn't expose `.type` method on the locator like `input.type` because it exists only for compatibility with the `userEvent` library. Consider using `.fill` instead as it is faster.
:::
References:
- [Playwright `locator.press` API](https://playwright.dev/docs/api/class-locator#locator-press)
- [WebdriverIO `action('key')` API](https://webdriver.io/docs/api/browser/action#key-input-source)
- [testing-library `type` API](https://testing-library.com/docs/user-event/utility/#type)
## userEvent.clear
```ts
function clear(element: Element | Locator, options?: UserEventClearOptions): Promise
```
This method clears the input element content.
```ts
import { page, userEvent } from '@vitest/browser/context'
test('clears input', async () => {
const input = page.getByRole('input')
await userEvent.fill(input, 'foo')
expect(input).toHaveValue('foo')
await userEvent.clear(input)
// or you can access it directly on the locator
await input.clear()
expect(input).toHaveValue('')
})
```
References:
- [Playwright `locator.clear` API](https://playwright.dev/docs/api/class-locator#locator-clear)
- [WebdriverIO `element.clearValue` API](https://webdriver.io/docs/api/element/clearValue)
- [testing-library `clear` API](https://testing-library.com/docs/user-event/utility/#clear)
## userEvent.selectOptions
```ts
function selectOptions(
element: Element | Locator,
values:
| HTMLElement
| HTMLElement[]
| Locator
| Locator[]
| string
| string[],
options?: UserEventSelectOptions,
): Promise
```
The `userEvent.selectOptions` allows selecting a value in a `