skip to content

Filling Form Controls

How a value gets into a field or an option gets picked, and why fill and pressSequentially are two different tools rather than a fast one and a slow one.

on this pageshow

explore

questions

5

In Playwright, how does locator.fill() differ from locator.pressSequentially() in what the page receives?

level: juniorimportance: must knowfreq 82%

answer

  1. Two ways to get text into a field
  2. One replaces the value, one types it
  3. Key events versus a single insertion
  4. Typing does not clear what is there
  5. clear() is fill with an empty string

basics

~20 s

locator.fill() focuses the field, selects any existing text and replaces it in one insertion, firing a single input event. locator.pressSequentially() sends keydown, keypress, input and keyup per character and does not clear the field first.

solid answer

~40 s

`locator.fill('Crash on board drag')` is the Playwright default. It waits for the field to be ready, focuses it, selects whatever is already there and inserts the whole string in one step, so the page sees one `input` event and no key events at all. `locator.pressSequentially('@dana')` instead dispatches `keydown`, `keypress`/`input` and `keyup` for each character, optionally spaced by `delay`, and it does not clear first — the characters land at the caret alongside the existing value, so pair it with `locator.clear()` when you mean replace. Use `pressSequentially` only when the page has real keyboard handling: a mention menu, an input mask, a key-driven search. Otherwise `fill` is one round trip instead of one per character. `locator.clear()` is just `fill('')`, and `locator.type()` is the deprecated predecessor of `pressSequentially`, which arrived in 1.38.

code

typescript · 14 lines
typescript
import { test, expect } from '@playwright/test';

test('fill replaces, pressSequentially types', async ({ page }) => {
  await page.goto('https://tracker.example.com/issues/DEV-114');

  const title = page.getByLabel('Issue title');
  await title.fill('Crash on board drag');
  await expect(title).toHaveValue('Crash on board drag');

  const comment = page.getByLabel('Add a comment');
  await comment.clear();
  await comment.pressSequentially('@dana', { delay: 50 });
  await expect(page.getByRole('listbox', { name: 'Mentions' })).toBeVisible();
});

go deeper

for a junior

Remember that fill is the everyday way to put text in a field and that it wipes whatever was there first. Typing character by character is the exception, not the default.

for a middle

Be able to say what each verb dispatches: fill produces one input event and no key events, pressSequentially produces keydown, keypress, input and keyup per character and clears nothing.

for a senior

Show you can pick per field rather than per suite, and name the cost: one protocol round trip per character, plus a re-render window between every keystroke on a busy page.

for a principal

Own the convention. Decide when a page's key-driven widget is worth typing into and when it should be tested closer to the unit, so the end-to-end suite does not pay keystroke latency everywhere.

## Two write paths, not a fast one and a slow one Playwright offers a text field two ways to receive a value, and the difference is not speed — it is what the page actually observes. `locator.fill(value)` waits for the field to be ready, focuses it, selects whatever text is already in it, and inserts the whole string in one step. The page sees a single `input` event. It sees no `keydown`, no `keypress` and no `keyup`, because no key was pressed: the text arrives the way a paste or an IME commit arrives. `locator.pressSequentially(text)` focuses the element and then dispatches `keydown`, `keypress`/`input` and `keyup` for every character in the string, optionally spaced by the `delay` option in milliseconds. Crucially it does **not** clear the field first — the characters go in at the caret, around whatever is already there. When you mean *replace*, put `locator.clear()` in front of it. ## What each verb guarantees - **`fill`** replaces the value wholesale and costs one round trip no matter how long the string is. - **`fill('')`** clears the field; `locator.clear()` is exactly that call under a nicer name. - **`pressSequentially`** produces per-character key events in order, at whatever `delay` you ask for. - **`pressSequentially` does not clear**, so a field with a default value ends up with both strings. - **Neither one** presses a named key such as `Enter` or `ArrowDown` — that is `locator.press()`. - **Both** retarget through a `<label>` to its associated control, so a label locator still works. ## Side by side | | `locator.fill()` | `locator.pressSequentially()` | |---|---|---| | Existing value | replaced | kept, typed into | | Events the page sees | one `input` | `keydown`, `keypress`/`input`, `keyup` per character | | Cost | one step | one step per character | | Pacing option | none | `delay` in milliseconds | | Good for | ordinary inputs, textareas, contenteditable | masks, mention menus, key-driven search | ## Where fill refuses to work `fill` accepts `<input>`, `<textarea>` and `[contenteditable]` only. On anything else it throws `Element is not an <input>, <textarea> or [contenteditable] element`. On an input whose `type` is not fillable — `button`, `checkbox`, `file`, `image`, `radio`, `reset`, `submit` — it throws `Input of type "checkbox" cannot be filled`. On `input[type=number]` a non-numeric string throws `Cannot type text into input[type=number]`. For the value-like family (`color`, `date`, `time`, `datetime-local`, `month`, `range`, `week`) Playwright sets the value directly and dispatches both `input` and `change`, because there is no sensible keystroke sequence for a colour picker. A checkbox is therefore not a `fill` target at all: its verbs are `check()`, `uncheck()` and `setChecked()`, and a `<select>` has `selectOption()`. ## Choosing between them on a real suite 1. **Default to `fill`.** On an issue tracker's title field, description or search box, the application reads `value` and reacts to `input`; `fill` delivers both. 2. **Reach for `pressSequentially` only when the page has genuine keyboard handling** — an `@mention` menu wired to `keydown`, an input mask that rewrites the value key by key, or a widget that counts keystrokes. 3. **Scope the switch to the one field that needs it.** Fill the bulk of a comment with `fill`, then `pressSequentially` just the few characters that must arrive as keys. ## The misconception worth killing The common wrong belief is that `fill` "sets the value in JavaScript" and therefore leaves framework state stale, so tests must type instead. That is false in Playwright: `fill` goes through the browser's real input pipeline and the `input` event it produces is the same event a human typing produces, so a React or Vue binding updates normally. What `fill` genuinely does not produce is *key* events — and that, not framework state, is the only reason to type character by character. The mirror-image mistake is treating `pressSequentially` as strictly more realistic and therefore always better. It is one protocol round trip per character; a forty-character description becomes forty steps, each one a chance for a re-render to steal focus. Realism you do not need is just runtime and flakiness you pay for. ## Versions `pressSequentially` arrived in Playwright 1.38 and is current through 1.63. The older `locator.type()` does the same keystroke-by-keystroke work but is deprecated: new code should use `fill` by default and `pressSequentially` where keys genuinely matter.

  • What happens if you call pressSequentially on a field that already holds a value?
    The old value stays. `pressSequentially` focuses the element and types at the caret; it never selects or deletes what is there, so you get both strings mixed together. Call `locator.clear()` first — or use `fill`, which selects the existing text before inserting.
  • Does locator.fill() work on a checkbox or a select?
    No. `fill` throws `Input of type "checkbox" cannot be filled` on a checkbox and `Element is not an <input>, <textarea> or [contenteditable] element` on a `<select>`. Checkboxes and radios use `check()`, `uncheck()` or `setChecked()`; a `<select>` uses `selectOption()`.
  • Why does the delay option exist on pressSequentially if faster is better?
    `delay` spaces the key events in milliseconds so a debounced handler, an autocomplete request or an input mask has time to run between characters. It is a deliberate slowdown for pages whose behaviour depends on human typing speed, not a general-purpose stabiliser.

saying these in an interview costs you the question

  • fill does not fire events, so framework state stays stale
  • pressSequentially is just a slower version of fill
  • pressSequentially clears the field before it types
  • fill sends real keystrokes, so keydown handlers run
  • Always type character by character because it is more realistic
  • fill works on any form control including checkboxes
open as a page

In Playwright, which ways can locator.selectOption() identify the option it should select?

level: middleimportance: must knowfreq 66%

basics

~20 s

A plain string matches an option by its value or its label. An object narrows by value, label or index, and every property you state must match. An array selects several options in a multiple select.

open as a page

In Playwright, what does locator.setChecked(true) do that a plain click on the checkbox does not?

level: middleimportance: should knowfreq 54%

basics

~20 s

setChecked reads the control's current state first, clicks only if it differs, and then verifies the new state. A plain click toggles blindly, so an already-checked box ends up unchecked and an intercepted click passes silently.

open as a page

In Playwright, why might a comment box's @mention autocomplete never open when the test uses locator.fill()?

level: seniorimportance: should knowfreq 41%

basics

~20 s

fill inserts the whole string in one step and dispatches only an input event, never keydown or keyup. A menu that opens on key handlers therefore never sees a trigger. Type the trigger characters with pressSequentially instead.

open as a page

A team wants every Playwright form interaction to use pressSequentially for realism. How do you evaluate that?

level: principalimportance: nice to knowfreq 28%

basics

~20 s

Replace the blanket rule with a criterion. Typing costs one round trip per character and adds a re-render window between each, so reserve it for fields whose own behaviour depends on key events and use fill everywhere else.

open as a page