diff --git a/packages/desktop-client/src/components/ManageRules.js b/packages/desktop-client/src/components/ManageRules.js
index ca9b979cace1bedca795c5eae5d40832133c5893..dc4fdf4068f91639af3796cd1e6be26318babf69 100644
--- a/packages/desktop-client/src/components/ManageRules.js
+++ b/packages/desktop-client/src/components/ManageRules.js
@@ -329,8 +329,8 @@ let Rule = memo(
         <SelectCell
           exposed={hovered || selected || editing}
           focused={focusedField === 'select'}
-          onSelect={() => {
-            dispatchSelected({ type: 'select', id: rule.id });
+          onSelect={e => {
+            dispatchSelected({ type: 'select', id: rule.id, event: e });
           }}
           onEdit={() => onEdit(rule.id, 'select')}
           selected={selected}
@@ -478,7 +478,7 @@ function RulesHeader() {
         exposed={true}
         focused={false}
         selected={selectedItems.size > 0}
-        onSelect={() => dispatchSelected({ type: 'select-all' })}
+        onSelect={e => dispatchSelected({ type: 'select-all', event: e })}
       />
       <Cell value="Stage" width={50} />
       <Cell value="Rule" width="flex" />
diff --git a/packages/desktop-client/src/components/accounts/SimpleTransactionsTable.js b/packages/desktop-client/src/components/accounts/SimpleTransactionsTable.js
index dad8e1afd453cd0f2a02f27cad05de255031cbbb..d7db23f5a56222df976d11e6dabfe7884b34f5bf 100644
--- a/packages/desktop-client/src/components/accounts/SimpleTransactionsTable.js
+++ b/packages/desktop-client/src/components/accounts/SimpleTransactionsTable.js
@@ -51,8 +51,8 @@ const TransactionRow = memo(function TransactionRow({
       <SelectCell
         exposed={true}
         focused={false}
-        onSelect={() => {
-          dispatchSelected({ type: 'select', id: transaction.id });
+        onSelect={e => {
+          dispatchSelected({ type: 'select', id: transaction.id, event: e });
         }}
         selected={selected}
       />
@@ -185,7 +185,7 @@ export default function SimpleTransactionsTable({
             focused={false}
             selected={selectedItems.size > 0}
             width={20}
-            onSelect={() => dispatchSelected({ type: 'select-all' })}
+            onSelect={e => dispatchSelected({ type: 'select-all', event: e })}
           />
           {fields.map((field, i) => {
             switch (field) {
diff --git a/packages/desktop-client/src/components/accounts/TransactionsTable.js b/packages/desktop-client/src/components/accounts/TransactionsTable.js
index 1430124989b823c94c47d9301b7a13322a462c22..76fe8dde030485a4aef1729d3a858fc3c1a3cb81 100644
--- a/packages/desktop-client/src/components/accounts/TransactionsTable.js
+++ b/packages/desktop-client/src/components/accounts/TransactionsTable.js
@@ -255,7 +255,7 @@ export const TransactionHeader = memo(
           focused={false}
           selected={hasSelected}
           width={20}
-          onSelect={() => dispatchSelected({ type: 'select-all' })}
+          onSelect={e => dispatchSelected({ type: 'select-all', event: e })}
         />
         <Cell value="Date" width={110} />
         {showAccount && <Cell value="Account" width="flex" />}
@@ -690,8 +690,8 @@ export const Transaction = memo(function Transaction(props) {
         <SelectCell
           exposed={hovered || selected || editing}
           focused={focusedField === 'select'}
-          onSelect={() => {
-            dispatchSelected({ type: 'select', id: transaction.id });
+          onSelect={e => {
+            dispatchSelected({ type: 'select', id: transaction.id, event: e });
           }}
           onEdit={() => onEdit(id, 'select')}
           selected={selected}
diff --git a/packages/desktop-client/src/components/payees/index.js b/packages/desktop-client/src/components/payees/index.js
index 09da634f91bb091373d54035228150420b72881c..4a75e3f909ef02dfca75458e83b8ccc6e23fc4a9 100644
--- a/packages/desktop-client/src/components/payees/index.js
+++ b/packages/desktop-client/src/components/payees/index.js
@@ -138,8 +138,8 @@ let Payee = memo(
           }
           focused={focusedField === 'select'}
           selected={selected}
-          onSelect={() => {
-            dispatchSelected({ type: 'select', id: payee.id });
+          onSelect={e => {
+            dispatchSelected({ type: 'select', id: payee.id, event: e });
           }}
         />
         <InputCell
@@ -247,7 +247,7 @@ function PayeeTableHeader() {
           exposed={true}
           focused={false}
           selected={selectedItems.size > 0}
-          onSelect={() => dispatchSelected({ type: 'select-all' })}
+          onSelect={e => dispatchSelected({ type: 'select-all', event: e })}
         />
         <Cell value="Name" width="flex" />
       </TableHeader>
diff --git a/packages/desktop-client/src/components/schedules/DiscoverSchedules.js b/packages/desktop-client/src/components/schedules/DiscoverSchedules.js
index 9e8965fb60802c512744072134f0fdedd6dc8021..939165e4d48d8be91735ec172d3b23a65d3d2a3c 100644
--- a/packages/desktop-client/src/components/schedules/DiscoverSchedules.js
+++ b/packages/desktop-client/src/components/schedules/DiscoverSchedules.js
@@ -35,8 +35,8 @@ function DiscoverSchedulesTable({ schedules, loading }) {
         height={ROW_HEIGHT}
         inset={15}
         backgroundColor="transparent"
-        onClick={() => {
-          dispatchSelected({ type: 'select', id: item.id });
+        onClick={e => {
+          dispatchSelected({ type: 'select', id: item.id, event: e });
         }}
         borderColor={selected ? colors.b8 : colors.border}
         style={{
@@ -51,8 +51,8 @@ function DiscoverSchedulesTable({ schedules, loading }) {
           exposed={true}
           focused={false}
           selected={selected}
-          onSelect={() => {
-            dispatchSelected({ type: 'select', id: item.id });
+          onSelect={e => {
+            dispatchSelected({ type: 'select', id: item.id, event: e });
           }}
         />
         <Field width="flex">
@@ -76,7 +76,7 @@ function DiscoverSchedulesTable({ schedules, loading }) {
           exposed={true}
           focused={false}
           selected={selectedItems.size > 0}
-          onSelect={() => dispatchSelected({ type: 'select-all' })}
+          onSelect={e => dispatchSelected({ type: 'select-all', event: e })}
         />
         <Field width="flex">Payee</Field>
         <Field width="flex">Account</Field>
diff --git a/packages/desktop-client/src/components/table.js b/packages/desktop-client/src/components/table.js
index 001b4c8fbb00d75073bafc30d4d436605f7cc5aa..585d57eb120e42e55bde67bbf5caef1623a402a6 100644
--- a/packages/desktop-client/src/components/table.js
+++ b/packages/desktop-client/src/components/table.js
@@ -491,7 +491,7 @@ export const CellButton = forwardRef(
           if (e.key === 'x' || e.key === ' ') {
             e.preventDefault();
             if (!disabled) {
-              onSelect && onSelect();
+              onSelect && onSelect(e);
             }
           }
         }}
@@ -513,9 +513,9 @@ export const CellButton = forwardRef(
         onClick={
           clickBehavior === 'none'
             ? null
-            : () => {
+            : e => {
                 if (!disabled) {
-                  onSelect && onSelect();
+                  onSelect && onSelect(e);
                   onEdit && onEdit();
                 }
               }
@@ -545,7 +545,7 @@ export function SelectCell({
       style={[{ alignItems: 'center', userSelect: 'none' }, style]}
       onClick={e => {
         e.stopPropagation();
-        onSelect && onSelect();
+        onSelect && onSelect(e);
         onEdit && onEdit();
       }}
     >
diff --git a/packages/desktop-client/src/hooks/useSelected.js b/packages/desktop-client/src/hooks/useSelected.js
index ef031056ebef642367ad606baa8c977f2592804b..0db6c65b4b62f73e0e6b14c1dcc67359fdc7d045 100644
--- a/packages/desktop-client/src/hooks/useSelected.js
+++ b/packages/desktop-client/src/hooks/useSelected.js
@@ -10,8 +10,7 @@ import { useSelector } from 'react-redux';
 
 import { listen } from 'loot-core/src/platform/client/fetch';
 import * as undo from 'loot-core/src/platform/client/undo';
-
-import { hasModifierKey } from '../util/keys';
+import { isNonProductionEnvironment } from 'loot-core/src/shared/environment';
 
 function iterateRange(range, func) {
   let from = Math.min(range.start, range.end);
@@ -29,9 +28,9 @@ export default function useSelected(name, items, initialSelectedIds) {
         case 'select': {
           let { selectedRange } = state;
           let selectedItems = new Set(state.selectedItems);
-          let { id } = action;
+          let { id, event } = action;
 
-          if (hasModifierKey('shift') && selectedRange) {
+          if (event.shiftKey && selectedRange) {
             let idx = items.findIndex(p => p.id === id);
             let startIdx = items.findIndex(p => p.id === selectedRange.start);
             let endIdx = items.findIndex(p => p.id === selectedRange.end);
@@ -224,17 +223,24 @@ export function SelectedProvider({ instance, fetchAllIds, children }) {
 
   let dispatch = useCallback(
     async action => {
+      if (!action.event && isNonProductionEnvironment()) {
+        throw new Error('SelectedDispatch actions must have an event');
+      }
       if (action.type === 'select-all') {
         if (latestItems.current && latestItems.current.size > 0) {
-          return instance.dispatch({ type: 'select-none' });
+          return instance.dispatch({
+            type: 'select-none',
+            event: action.event,
+          });
         } else {
           if (fetchAllIds) {
             return instance.dispatch({
               type: 'select-all',
               ids: await fetchAllIds(),
+              event: action.event,
             });
           }
-          return instance.dispatch({ type: 'select-all' });
+          return instance.dispatch({ type: 'select-all', event: action.event });
         }
       }
       return instance.dispatch(action);
diff --git a/packages/desktop-client/src/util/keys.js b/packages/desktop-client/src/util/keys.js
deleted file mode 100644
index e9db15a5c77d839c18a36a74c66e34dec3c3e3b5..0000000000000000000000000000000000000000
--- a/packages/desktop-client/src/util/keys.js
+++ /dev/null
@@ -1,65 +0,0 @@
-// TODO: This is a barebones module for now, need to think about a
-// generic way keys are handled across the app
-
-let _keyHandlers = {};
-
-let _modifierState = {
-  shift: false,
-  ctrl: false,
-  alt: false,
-  meta: false,
-};
-
-export function hasModifierKey(modifier) {
-  return !!_modifierState[modifier];
-}
-
-export function registerKeyHandler(key, func) {
-  if (!_keyHandlers[key]) {
-    _keyHandlers[key] = [];
-  }
-  _keyHandlers[key].push(func);
-
-  return () => {
-    _keyHandlers[key] = _keyHandlers[key].filter(f => f !== func);
-  };
-}
-
-document.addEventListener('keydown', e => {
-  if (e.key === 'Shift') {
-    _modifierState.shift = true;
-  }
-  if (e.key === 'Control') {
-    _modifierState.ctrl = true;
-  }
-  if (e.key === 'Alt') {
-    _modifierState.alt = true;
-  }
-  if (e.key === 'Meta') {
-    _modifierState.meta = true;
-  }
-
-  if (!(e.target && e.target.matches('input'))) {
-    let handlers = _keyHandlers[e.key.toUpperCase()];
-    if (handlers && handlers.length > 0) {
-      handlers[handlers.length - 1](_modifierState);
-      e.preventDefault();
-      e.stopPropagation();
-    }
-  }
-});
-
-document.addEventListener('keyup', e => {
-  if (e.key === 'Shift') {
-    _modifierState.shift = false;
-  }
-  if (e.key === 'Control') {
-    _modifierState.ctrl = false;
-  }
-  if (e.key === 'Alt') {
-    _modifierState.alt = false;
-  }
-  if (e.key === 'Meta') {
-    _modifierState.meta = false;
-  }
-});
diff --git a/upcoming-release-notes/1022.md b/upcoming-release-notes/1022.md
new file mode 100644
index 0000000000000000000000000000000000000000..a86afe93f77f9b704ca7a8b0f8baf13e8ac5406d
--- /dev/null
+++ b/upcoming-release-notes/1022.md
@@ -0,0 +1,6 @@
+---
+category: Maintenance
+authors: [j-f1]
+---
+
+Improve behavior of shift-clicking checkboxes to select multiple transactions.