diff --git a/packages/core/src/components/tag-input/tag-input.md b/packages/core/src/components/tag-input/tag-input.md
index b3a748ae635..aae86e4ceb3 100644
--- a/packages/core/src/components/tag-input/tag-input.md
+++ b/packages/core/src/components/tag-input/tag-input.md
@@ -25,7 +25,8 @@ new items. A `separator` prop is supported to allow multiple items to be added
at once; the default splits on commas and newlines.
**Tags can be removed** by clicking their
-buttons, or by pressing backspace repeatedly.
+buttons, or by pressing either backspace or delete repeatedly.
+Pressing delete mimics the behavior of deleting in a text editor, where trying to delete at the end of the line will do nothing.
Arrow keys can also be used to focus on a particular tag before removing it. The
cursor must be at the beginning of the text input for these interactions.
diff --git a/packages/core/src/components/tag-input/tagInput.tsx b/packages/core/src/components/tag-input/tagInput.tsx
index 84824cad93f..10f3ecd6225 100644
--- a/packages/core/src/components/tag-input/tagInput.tsx
+++ b/packages/core/src/components/tag-input/tagInput.tsx
@@ -405,6 +405,8 @@ export class TagInput extends AbstractPureComponent2) {
+ const { activeIndex } = this.state;
+ if (this.isValidIndex(activeIndex)) {
+ event.stopPropagation();
+ this.removeIndexFromValues(activeIndex);
+ }
+ }
+
/** Remove the item at the given index by invoking `onRemove` and `onChange` accordingly. */
private removeIndexFromValues(index: number) {
const { onChange, onRemove, values } = this.props;
diff --git a/packages/core/test/tag-input/tagInputTests.tsx b/packages/core/test/tag-input/tagInputTests.tsx
index d7fe8e08f0c..c452b3f9783 100644
--- a/packages/core/test/tag-input/tagInputTests.tsx
+++ b/packages/core/test/tag-input/tagInputTests.tsx
@@ -315,6 +315,33 @@ describe("", () => {
assert.sameMembers(onRemove.args[0], [VALUES[1], 1]);
});
+ it("pressing left arrow key navigates active item and delete removes it", () => {
+ const onRemove = sinon.spy();
+ const wrapper = mount();
+ // select and remove middle item
+ wrapper
+ .find("input")
+ .simulate("keydown", { which: Keys.ARROW_LEFT })
+ .simulate("keydown", { which: Keys.ARROW_LEFT })
+ .simulate("keydown", { which: Keys.DELETE });
+
+ // in this case we're not moving into the previous item but
+ // we rather "take the place" of the item we just removed
+ assert.equal(wrapper.state("activeIndex"), 1);
+ assert.isTrue(onRemove.calledOnce);
+ assert.sameMembers(onRemove.args[0], [VALUES[1], 1]);
+ });
+
+ it("pressing delete with no selection does nothing", () => {
+ const onRemove = sinon.spy();
+ const wrapper = mount();
+
+ wrapper.find("input").simulate("keydown", { which: Keys.DELETE });
+
+ assert.equal(wrapper.state("activeIndex"), -1);
+ assert.isTrue(onRemove.notCalled);
+ });
+
it("pressing right arrow key in initial state does nothing", () => {
const wrapper = mount();
wrapper.find("input").simulate("keydown", { which: Keys.ARROW_RIGHT });