diff --git a/docs/ecosystem-user-event.mdx b/docs/ecosystem-user-event.mdx
index e2fcda016..2f5478af7 100644
--- a/docs/ecosystem-user-event.mdx
+++ b/docs/ecosystem-user-event.mdx
@@ -30,8 +30,9 @@ test('types inside textarea', () => {
## API
Note: All userEvent methods are synchronous with one exception: when `delay`
-option used with `userEvent.type` as described below. We also discourage using `userEvent`
-inside `before/after` blocks at all, for important reasons described in
+option used with `userEvent.type` as described below. We also discourage using
+`userEvent` inside `before/after` blocks at all, for important reasons described
+in
["Avoid Nesting When You're Testing"](https://kentcdodds.com/blog/avoid-nesting-when-youre-testing).
### `click(element, eventInit, options)`
@@ -203,6 +204,76 @@ test('types into the input', () => {
})
```
+### `keyboard(text, options)`
+
+Simulates the keyboard events described by `text`. This is similar to
+`userEvent.type()` but without any clicking or changing the selection range.
+
+> You should use `userEvent.keyboard` if you want to just simulate pressing
+> buttons on the keyboard. You should use `userEvent.type` if you just want to
+> conveniently insert some text into an input field or textarea.
+
+Keystrokes can be described:
+
+- Per printable character
+ ```js
+ userEvent.keyboard('foo') // translates to: f, o, o
+ ```
+ The brackets `{` and `[` are used as special character and can be referenced
+ by doubling them.
+ ```js
+ userEvent.keyboard('{{a[[') // translates to: {, a, [
+ ```
+- Per
+ [KeyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key)
+ (only supports alphanumeric values of `key`)
+ ```js
+ userEvent.keyboard('{Shift}{f}{o}{o}') // translates to: Shift, f, o, o
+ ```
+ This does not keep any key pressed. So `Shift` will be lifted before pressing
+ `f`.
+- Per
+ [KeyboardEvent.code](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code)
+ ```js
+ userEvent.keyboard('[ShiftLeft][KeyF][KeyO][KeyO]') // translates to: Shift, f, o, o
+ ```
+- Per legacy `userEvent.type` modifier/specialChar The modifiers like `{shift}`
+ (note the lowercase) will automatically be kept pressed, just like before. You
+ can cancel this behavior by adding a `/` to the end of the descriptor.
+ ```js
+ userEvent.keyboard('{shift}{ctrl/}a{/shift}') // translates to: Shift(down), Control(down+up), a, Shift(up)
+ ```
+
+Keys can be kept pressed by adding a `>` to the end of the descriptor - and
+lifted by adding a `/` to the beginning of the descriptor:
+
+```js
+userEvent.keyboard('{Shift>}A{/Shift}') // translates to: Shift(down), A, Shift(up)
+```
+
+`userEvent.keyboard` returns a keyboard state that can be used to continue
+keyboard operations.
+
+```js
+const keyboardState = userEvent.keyboard('[ControlLeft>]') // keydown [ControlLeft]
+// ... inspect some changes ...
+userEvent.keyboard('a', { keyboardState }) // press [KeyA] with active ctrlKey modifier
+```
+
+The mapping of `key` to `code` is performed by a
+[default key map](https://github.com/testing-library/user-event/blob/master/src/keyboard/keyMap.ts)
+portraying a "default" US-keyboard. You can provide your own local keyboard
+mapping per option.
+
+```js
+userEvent.keyboard('?', { keyboardMap: myOwnLocaleKeyboardMap })
+```
+
+> Future versions might try to interpolate the modifiers needed to reach a
+> printable key on the keyboard. E.g. Automatically pressing `{Shift}` when
+> CapsLock is not active and `A` is referenced. If you don't wish this behavior,
+> you can pass `autoModify: false` when using `userEvent.keyboard` in your code.
+
### `upload(element, file, [{ clickInit, changeInit }])`
Uploads file to an ``. For uploading multiple files use `` with