In Playwright, how does locator.fill() differ from locator.pressSequentially() in what the page receives?
answer
- Two ways to get text into a field
- One replaces the value, one types it
- Key events versus a single insertion
- Typing does not clear what is there
- clear() is fill with an empty string
basics
~20 slocator.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 linesimport { 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
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.
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.
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.
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