diff --git a/app/assets/javascripts/discourse/components/invite-panel.js.es6 b/app/assets/javascripts/discourse/components/invite-panel.js.es6
index 8056a92dcd0..8622e97ac58 100644
--- a/app/assets/javascripts/discourse/components/invite-panel.js.es6
+++ b/app/assets/javascripts/discourse/components/invite-panel.js.es6
@@ -1,5 +1,6 @@
 import discourseComputed from "discourse-common/utils/decorators";
 import { isEmpty } from "@ember/utils";
+import { computed } from "@ember/object";
 import { alias, and, equal } from "@ember/object/computed";
 import EmberObject from "@ember/object";
 import Component from "@ember/component";
@@ -7,6 +8,7 @@ import { emailValid } from "discourse/lib/utilities";
 import Group from "discourse/models/group";
 import Invite from "discourse/models/invite";
 import { i18n } from "discourse/lib/computed";
+import { getNativeContact } from "discourse/lib/pwa-utils";
 
 export default Component.extend({
   tagName: null,
@@ -180,6 +182,10 @@ export default Component.extend({
     );
   },
 
+  showContactPicker: computed(function() {
+    return this.capabilities.hasContactPicker;
+  }),
+
   @discourseComputed("emailOrUsername")
   showCustomMessage(emailOrUsername) {
     return this.inviteModel === this.currentUser || emailValid(emailOrUsername);
@@ -433,6 +439,12 @@ export default Component.extend({
       } else {
         this.set("customMessage", null);
       }
+    },
+
+    searchContact() {
+      getNativeContact(["email"], false).then(result => {
+        this.set("emailOrUsername", result[0].email[0]);
+      });
     }
   }
 });
diff --git a/app/assets/javascripts/discourse/lib/pwa-utils.js.es6 b/app/assets/javascripts/discourse/lib/pwa-utils.js.es6
index 1e2c901bd65..23746a0cb35 100644
--- a/app/assets/javascripts/discourse/lib/pwa-utils.js.es6
+++ b/app/assets/javascripts/discourse/lib/pwa-utils.js.es6
@@ -26,3 +26,18 @@ export function nativeShare(data) {
     }
   });
 }
+
+export function getNativeContact(properties, multiple) {
+  const caps = Discourse.__container__.lookup("capabilities:main");
+  return new Promise((resolve, reject) => {
+    if (!caps.hasContactPicker) {
+      reject();
+      return;
+    }
+
+    navigator.contacts
+      .select(properties, { multiple })
+      .then(resolve)
+      .catch(reject);
+  });
+}
diff --git a/app/assets/javascripts/discourse/pre-initializers/sniff-capabilities.js.es6 b/app/assets/javascripts/discourse/pre-initializers/sniff-capabilities.js.es6
index 0cfc4f2c619..1e111506e8b 100644
--- a/app/assets/javascripts/discourse/pre-initializers/sniff-capabilities.js.es6
+++ b/app/assets/javascripts/discourse/pre-initializers/sniff-capabilities.js.es6
@@ -43,6 +43,9 @@ export default {
       caps.isIOS =
         (/iPhone|iPod/.test(navigator.userAgent) || caps.isIpadOS) &&
         !window.MSStream;
+
+      caps.hasContactPicker =
+        "contacts" in navigator && "ContactsManager" in window;
     }
 
     // We consider high res a device with 1280 horizontal pixels. High DPI tablets like
diff --git a/app/assets/javascripts/discourse/templates/components/invite-panel.hbs b/app/assets/javascripts/discourse/templates/components/invite-panel.hbs
index c72a0d188d9..a83435600ba 100644
--- a/app/assets/javascripts/discourse/templates/components/invite-panel.hbs
+++ b/app/assets/javascripts/discourse/templates/components/invite-panel.hbs
@@ -19,25 +19,34 @@
   {{else}}
     <div class="invite-user-control">
       <label class="instructions">{{inviteInstructions}}</label>
-      {{#if allowExistingMembers}}
-        {{user-selector
-          fullWidthWrap=true
-          single=true
-          allowAny=true
-          excludeCurrentUser=true
-          includeMessageableGroups=isPM
-          hasGroups=hasGroups
-          usernames=emailOrUsername
-          placeholderKey=placeholderKey
-          allowEmails=true
-          class="invite-user-input"
-          autocomplete="discourse"}}
-      {{else}}
-        {{text-field
-          class="email-or-username-input"
-          value=emailOrUsername
-          placeholderKey="topic.invite_reply.email_placeholder"}}
-      {{/if}}
+      <div class="invite-user-input-wrapper">
+        {{#if allowExistingMembers}}
+          {{user-selector
+            fullWidthWrap=true
+            single=true
+            allowAny=true
+            excludeCurrentUser=true
+            includeMessageableGroups=isPM
+            hasGroups=hasGroups
+            usernames=emailOrUsername
+            placeholderKey=placeholderKey
+            allowEmails=true
+            canReceiveUpdates=(if showContactPicker "true" "false")
+            class="invite-user-input"
+            autocomplete="discourse"}}
+        {{else}}
+          {{text-field
+            class="email-or-username-input"
+            value=emailOrUsername
+            placeholderKey="topic.invite_reply.email_placeholder"}}
+        {{/if}}
+          {{#if showContactPicker}}
+            {{d-button
+            icon="address-book"
+            action=(action "searchContact")
+            class="btn-primary open-contact-picker"}}
+          {{/if}}
+      </div>
     </div>
 
     {{#if showGroups}}
diff --git a/app/assets/stylesheets/common/components/share-and-invite-modal.scss b/app/assets/stylesheets/common/components/share-and-invite-modal.scss
index f38c0c1eab7..c5177006f90 100644
--- a/app/assets/stylesheets/common/components/share-and-invite-modal.scss
+++ b/app/assets/stylesheets/common/components/share-and-invite-modal.scss
@@ -85,6 +85,13 @@
     .email-or-username-input {
       width: 100%;
     }
+
+    .invite-user-input-wrapper {
+      display: flex;
+      div.ac-wrap {
+        flex: 1;
+      }
+    }
   }
 
   .footer {
diff --git a/lib/svg_sprite/svg_sprite.rb b/lib/svg_sprite/svg_sprite.rb
index 35ef39aed05..33d51ca4844 100644
--- a/lib/svg_sprite/svg_sprite.rb
+++ b/lib/svg_sprite/svg_sprite.rb
@@ -3,6 +3,7 @@
 module SvgSprite
   SVG_ICONS ||= Set.new([
     "adjust",
+    "address-book",
     "ambulance",
     "anchor",
     "angle-double-down",