diff --git a/package.json b/package.json
index 0d9ed6b57e38dc448a3da828e3acb5cc24a33035..5768aadf61d4b1c4c0b92037031798b7d2465f6c 100644
--- a/package.json
+++ b/package.json
@@ -29,6 +29,7 @@
     "aframe-teleport-controls": "^0.3.1",
     "aframe-xr": "github:brianpeiris/aframe-xr#3162aed",
     "classnames": "^2.2.5",
+    "copy-to-clipboard": "^3.0.8",
     "detect-browser": "^2.1.0",
     "event-target-shim": "^3.0.1",
     "form-urlencoded": "^2.0.4",
diff --git a/src/assets/stylesheets/hub.scss b/src/assets/stylesheets/hub.scss
index c10db8dd196ca6b3dce999605e002893cacd05e6..4cb7eab1c0959e8e8076f9fb6b658fe9a067baf2 100644
--- a/src/assets/stylesheets/hub.scss
+++ b/src/assets/stylesheets/hub.scss
@@ -6,6 +6,7 @@
 @import 'profile';
 @import 'entry';
 @import 'audio';
+@import 'info-dialog';
 
 .a-enter-vr {
   display: none;
diff --git a/src/assets/stylesheets/index.scss b/src/assets/stylesheets/index.scss
index 38833657208e9a58a19ef8b10add9e8a5ceaa25d..670bdf9975cddc36722718c8e6c5842a0df3507d 100644
--- a/src/assets/stylesheets/index.scss
+++ b/src/assets/stylesheets/index.scss
@@ -1,5 +1,6 @@
 @import 'shared';
 @import 'hub-create';
+@import 'info-dialog';
 
 * {
   box-sizing: border-box;
@@ -222,110 +223,3 @@ body {
   }
 }
 
-.overlay {
-  width: 100%;
-  height: 100%;
-  top: 0;
-  left: 0;
-  position: absolute;
-  pointer-events: none;
-  color: white;
-  z-index: 2;
-}
-
-.mailing-list-form {
-  display: flex;
-  height: 100%;
-  flex-direction: column;
-  justify-content: center;
-  text-align: center;
-  margin: 0;
-
-  &__first {
-    width: 100%;
-  }
-
-  &__email_field {
-    @extend %rounded-border;
-    @extend %default-font;
-    color: $light-text;
-    font-size: 1.2em;
-    background-color: transparent;
-    line-height: 2.0em;
-    padding-left: 1.25em;
-    padding-right: 1.25em;
-    margin: 0.5em 0;
-    width: 100%;
-  }
-
-  &__submit {
-    @extend %bottom-button;
-    border: 0;
-    margin-top: 16px;
-  }
-
-  &__privacy {
-    margin-top: 10px;
-    font-size: 0.7em;
-  }
-}
-
-.dialog {
-  display: grid;
-  grid-template-columns: 1fr 20px minmax(200px,500px) 20px 1fr;
-  grid-template-rows: 1fr 20px 275px 20px 1fr;
-  width: 100%;
-  height: 100%;
-  background-color: rgba(0,0,0,.6);
-
-  &__box {
-    grid-column: 3;
-    grid-row: 3;
-    position: relative;
-    pointer-events: auto;
-
-    &__contents {
-      background-color: rgba(0,0,0,0.8);
-      border-radius: 8px;
-      width: 100%;
-      height: 100%;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: center;
-      text-align: center;
-      position: relative;
-
-      &__title {
-	@extend %top-title;
-	margin-top: 20px;
-      }
-
-      &__body {
-	margin: 40px;
-	font-size: 1.1em;
-	margin-top: 20px;
-	color: $grey-text;
-	display: flex;
-	flex-direction: column;
-
-	a { color: white }
-      }
-
-      &__close {
-	position: absolute;
-	left: 12px;
-	top: 6px;
-	color: white;
-	font-size: 1.4em;
-
-	background: none;
-	cursor: pointer;
-	border: none;
-      }
-    }
-  }
-}
-
-
-
diff --git a/src/assets/stylesheets/info-dialog.scss b/src/assets/stylesheets/info-dialog.scss
new file mode 100644
index 0000000000000000000000000000000000000000..d3e860def686b2ebdf5c81382107a82b62bdf1c8
--- /dev/null
+++ b/src/assets/stylesheets/info-dialog.scss
@@ -0,0 +1,149 @@
+.dialog-overlay {
+  width: 100%;
+  height: 100%;
+  top: 0;
+  left: 0;
+  position: absolute;
+  pointer-events: none;
+  color: white;
+  z-index: 2;
+}
+
+.dialog {
+  display: grid;
+  grid-template-columns: 1fr 20px minmax(200px,500px) 20px 1fr;
+  grid-template-rows: 1fr 20px 275px 20px 1fr;
+  width: 100%;
+  height: 100%;
+  background-color: rgba(0,0,0,.6);
+
+  &__box {
+    grid-column: 3;
+    grid-row: 3;
+    position: relative;
+    pointer-events: auto;
+
+    &__contents {
+      background-color: rgba(0,0,0,0.8);
+      border-radius: 8px;
+      width: 100%;
+      height: 100%;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      justify-content: center;
+      text-align: center;
+      position: relative;
+
+      &__title {
+	@extend %top-title;
+	margin-top: 20px;
+      }
+
+      &__body {
+	margin: 40px;
+	font-size: 1.1em;
+	margin-top: 20px;
+	color: $grey-text;
+	display: flex;
+	flex-direction: column;
+
+	a { color: white }
+      }
+
+      &__close {
+	position: absolute;
+	left: 12px;
+	top: 6px;
+	color: white;
+	font-size: 1.4em;
+
+	background: none;
+	cursor: pointer;
+	border: none;
+      }
+    }
+  }
+}
+
+.invite-form {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  text-align: center;
+  margin: 0;
+
+  &__buttons {
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+  }
+
+  &__link {
+    display: flex;
+    flex-direction: row;
+  }
+
+  &__link_field {
+    @extend %rounded-border;
+    @extend %default-font;
+    color: $light-text;
+    font-size: 1.2em;
+    background-color: transparent;
+    line-height: 2.0em;
+    padding-left: 1.25em;
+    padding-right: 1.25em;
+    margin: 0.5em 0;
+  }
+
+  &__action-button {
+    @extend %bottom-button;
+    margin-left: 6px;
+    margin-right: 6px;
+    appearance: none;
+    width: 128px;
+    text-align: center;
+    -moz-appearance: none;
+    -webkit-appearance: none;
+  }
+}
+
+.mailing-list-form {
+  display: flex;
+  height: 100%;
+  flex-direction: column;
+  justify-content: center;
+  text-align: center;
+  margin: 0;
+
+  &__first {
+    width: 100%;
+  }
+
+  &__email_field {
+    @extend %rounded-border;
+    @extend %default-font;
+    color: $light-text;
+    font-size: 1.2em;
+    background-color: transparent;
+    line-height: 2.0em;
+    padding-left: 1.25em;
+    padding-right: 1.25em;
+    margin: 0.5em 0;
+    width: 100%;
+  }
+
+  &__submit {
+    @extend %bottom-button;
+    border: 0;
+    margin-top: 16px;
+  }
+
+  &__privacy {
+    margin-top: 10px;
+    font-size: 0.7em;
+  }
+}
+
+
+
diff --git a/src/react-components/home-root.js b/src/react-components/home-root.js
index 231d8b747cbfda01c463310f716ca78d0438f53c..ed7d89a91d8b8773e9a9b634dcbf088690e9861a 100644
--- a/src/react-components/home-root.js
+++ b/src/react-components/home-root.js
@@ -4,9 +4,9 @@ import { IntlProvider, FormattedMessage, addLocaleData } from "react-intl";
 import en from "react-intl/locale-data/en";
 import homeVideo from "../assets/video/home.webm";
 import classNames from "classnames";
-import formurlencoded from "form-urlencoded";
 
 import HubCreatePanel from "./hub-create-panel.js";
+import InfoDialog from "./info-dialog.js";
 
 const navigatorLang = (navigator.languages && navigator.languages[0]) || navigator.language || navigator.userLanguage;
 
@@ -49,32 +49,6 @@ class HomeRoot extends Component {
     };
   };
 
-  closeDialog = () => {
-    this.setState({ dialogType: null });
-  };
-
-  signUpForMailingList = async e => {
-    e.preventDefault();
-    e.stopPropagation();
-    if (!this.state.mailingListPrivacy) return;
-
-    const url = "https://www.mozilla.org/en-US/newsletter/";
-
-    const payload = {
-      email: this.state.mailingListEmail,
-      newsletters: "mixed-reality",
-      privacy: true,
-      fmt: "H",
-      source_url: document.location.href
-    };
-
-    await fetch(url, {
-      body: formurlencoded(payload),
-      method: "POST",
-      headers: { "content-type": "application/x-www-form-urlencoded" }
-    }).then(() => this.setState({ dialogType: "email_submitted" }));
-  };
-
   loadEnvironments = () => {
     const environments = [];
 
@@ -91,93 +65,10 @@ class HomeRoot extends Component {
   };
 
   render() {
-    let dialogTitle = null;
-    let dialogBody = null;
-
-    switch (this.state.dialogType) {
-      // TODO i18n, FormattedMessage doesn't play nicely with links
-      case "slack":
-        dialogTitle = "Get in Touch";
-        dialogBody = (
-          <span>
-            Want to join the conversation?
-            <p />
-            Join us on the{" "}
-            <a href="https://webvr-slack.herokuapp.com/" target="_blank" rel="noopener noreferrer">
-              WebVR Slack
-            </a>{" "}
-            in the #social channel.<br />VR meetups every Friday at noon PST!
-            <p /> Or, tweet at{" "}
-            <a href="https://twitter.com/mozillareality" target="_blank" rel="noopener noreferrer">
-              @mozillareality
-            </a>{" "}
-            on Twitter.
-          </span>
-        );
-        break;
-      case "email_submitted":
-        dialogTitle = "";
-        dialogBody = "Great! Please check your e-mail to confirm your subscription.";
-        break;
-      case "updates":
-        dialogTitle = "";
-        dialogBody = (
-          <span>
-            Sign up to get release notes about new features.
-            <p />
-            <form onSubmit={this.signUpForMailingList}>
-              <div className="mailing-list-form">
-                <input
-                  type="email"
-                  value={this.state.mailingListEmail}
-                  onChange={e => this.setState({ mailingListEmail: e.target.value })}
-                  className="mailing-list-form__email_field"
-                  required
-                  placeholder="Your email here"
-                />
-                <label className="mailing-list-form__privacy">
-                  <input
-                    className="mailing-list-form__privacy_checkbox"
-                    type="checkbox"
-                    required
-                    value={this.state.mailingListPrivacy}
-                    onChange={e => this.setState({ mailingListPrivacy: e.target.checked })}
-                  />
-                  <span className="mailing-list-form__privacy_label">
-                    <FormattedMessage id="mailing_list.privacy_label" />{" "}
-                    <a target="_blank" rel="noopener noreferrer" href="https://www.mozilla.org/en-US/privacy/">
-                      <FormattedMessage id="mailing_list.privacy_link" />
-                    </a>
-                  </span>
-                </label>
-                <input className="mailing-list-form__submit" type="submit" value="Sign Up Now" />
-              </div>
-            </form>
-          </span>
-        );
-        break;
-      case "report":
-        dialogTitle = "Report an Issue";
-        dialogBody = (
-          <span>
-            Need to report a problem?
-            <p />
-            You can file a{" "}
-            <a href="https://github.com/mozilla/mr-social-client/issues" target="_blank" rel="noopener noreferrer">
-              Github Issue
-            </a>{" "}
-            or e-mail us for support at <a href="mailto:hubs@mozilla.com">hubs@mozilla.com</a>.
-            <p />
-            You can also find us in #social on the{" "}
-            <a href="http://webvr-slack.herokuapp.com/" target="_blank" rel="noopener noreferrer">
-              WebVR Slack
-            </a>.
-          </span>
-        );
-        break;
-    }
-
-    const mainContentClassNames = classNames({ "main-content": true, "main-content--noninteractive": !!dialogTitle });
+    const mainContentClassNames = classNames({
+      "main-content": true,
+      "main-content--noninteractive": !!this.state.dialogType
+    });
 
     return (
       <IntlProvider locale={lang} messages={messages}>
@@ -273,20 +164,11 @@ class HomeRoot extends Component {
             <source src={homeVideo} type="video/webm" />
           </video>
           {this.state.dialogType && (
-            <div className="overlay">
-              <div className="dialog">
-                <div className="dialog__box">
-                  <div className="dialog__box__contents">
-                    <button className="dialog__box__contents__close" onClick={this.closeDialog}>
-                      <span>🗙</span>
-                    </button>
-                    <div className="dialog__box__contents__title">{dialogTitle}</div>
-                    <div className="dialog__box__contents__body">{dialogBody}</div>
-                    <div className="dialog__box__contents__button-container" />
-                  </div>
-                </div>
-              </div>
-            </div>
+            <InfoDialog
+              dialogType={this.state.dialogType}
+              onCloseDialog={() => this.setState({ dialogType: null })}
+              onSubmittedEmail={() => this.setState({ dialogType: "email_submitted" })}
+            />
           )}
         </div>
       </IntlProvider>
diff --git a/src/react-components/info-dialog.js b/src/react-components/info-dialog.js
new file mode 100644
index 0000000000000000000000000000000000000000..e9b835dd26953d92ce9d38edd83b2c5fb03182b9
--- /dev/null
+++ b/src/react-components/info-dialog.js
@@ -0,0 +1,200 @@
+import React, { Component } from "react";
+import copy from "copy-to-clipboard";
+import PropTypes from "prop-types";
+import { FormattedMessage } from "react-intl";
+import formurlencoded from "form-urlencoded";
+
+// TODO i18n
+
+class InfoDialog extends Component {
+  static propTypes = {
+    dialogType: PropTypes.string,
+    onCloseDialog: PropTypes.func,
+    onSubmittedEmail: PropTypes.func
+  };
+
+  constructor(props) {
+    super(props);
+
+    const loc = document.location;
+    this.shareLink = `${loc.protocol}//${loc.host}${loc.pathname}`;
+  }
+
+  shareLinkClicked = () => {
+    navigator.share({
+      title: document.title,
+      url: this.shareLink
+    });
+  };
+
+  copyLinkClicked = () => {
+    copy(this.shareLink);
+    this.setState({ copyLinkButtonText: "Copied!" });
+  };
+
+  state = {
+    mailingListEmail: "",
+    mailingListPrivacy: false,
+    copyLinkButtonText: "Copy"
+  };
+
+  signUpForMailingList = async e => {
+    e.preventDefault();
+    e.stopPropagation();
+    if (!this.state.mailingListPrivacy) return;
+
+    const url = "https://www.mozilla.org/en-US/newsletter/";
+
+    const payload = {
+      email: this.state.mailingListEmail,
+      newsletters: "mixed-reality",
+      privacy: true,
+      fmt: "H",
+      source_url: document.location.href
+    };
+
+    await fetch(url, {
+      body: formurlencoded(payload),
+      method: "POST",
+      headers: { "content-type": "application/x-www-form-urlencoded" }
+    }).then(this.props.onSubmittedEmail);
+  };
+
+  render() {
+    if (!this.props.dialogType) {
+      return <div />;
+    }
+
+    let dialogTitle = null;
+    let dialogBody = null;
+
+    switch (this.props.dialogType) {
+      // TODO i18n, FormattedMessage doesn't play nicely with links
+      case "slack":
+        dialogTitle = "Get in Touch";
+        dialogBody = (
+          <span>
+            Want to join the conversation?
+            <p />
+            Join us on the{" "}
+            <a href="https://webvr-slack.herokuapp.com/" target="_blank" rel="noopener noreferrer">
+              WebVR Slack
+            </a>{" "}
+            in the #social channel.<br />VR meetups every Friday at noon PST!
+            <p /> Or, tweet at{" "}
+            <a href="https://twitter.com/mozillareality" target="_blank" rel="noopener noreferrer">
+              @mozillareality
+            </a>{" "}
+            on Twitter.
+          </span>
+        );
+        break;
+      case "email_submitted":
+        dialogTitle = "";
+        dialogBody = "Great! Please check your e-mail to confirm your subscription.";
+        break;
+      case "invite":
+        dialogTitle = "Invite Others";
+        dialogBody = (
+          <div>
+            <div>Just share the link to have others join you.</div>
+            <div className="invite-form">
+              <input
+                type="text"
+                readOnly
+                onFocus={e => e.target.select()}
+                value={this.shareLink}
+                className="invite-form__link_field"
+              />
+              <div className="invite-form__buttons">
+                {navigator.share && (
+                  <button className="invite-form__action-button" onClick={this.shareLinkClicked}>
+                    <span>Share</span>
+                  </button>
+                )}
+                <button className="invite-form__action-button" onClick={this.copyLinkClicked}>
+                  <span>{this.state.copyLinkButtonText}</span>
+                </button>
+              </div>
+            </div>
+          </div>
+        );
+        break;
+      case "updates":
+        dialogTitle = "";
+        dialogBody = (
+          <span>
+            Sign up to get release notes about new features.
+            <p />
+            <form onSubmit={this.signUpForMailingList}>
+              <div className="mailing-list-form">
+                <input
+                  type="email"
+                  value={this.state.mailingListEmail}
+                  onChange={e => this.setState({ mailingListEmail: e.target.value })}
+                  className="mailing-list-form__email_field"
+                  required
+                  placeholder="Your email here"
+                />
+                <label className="mailing-list-form__privacy">
+                  <input
+                    className="mailing-list-form__privacy_checkbox"
+                    type="checkbox"
+                    required
+                    value={this.state.mailingListPrivacy}
+                    onChange={e => this.setState({ mailingListPrivacy: e.target.checked })}
+                  />
+                  <span className="mailing-list-form__privacy_label">
+                    <FormattedMessage id="mailing_list.privacy_label" />{" "}
+                    <a target="_blank" rel="noopener noreferrer" href="https://www.mozilla.org/en-US/privacy/">
+                      <FormattedMessage id="mailing_list.privacy_link" />
+                    </a>
+                  </span>
+                </label>
+                <input className="mailing-list-form__submit" type="submit" value="Sign Up Now" />
+              </div>
+            </form>
+          </span>
+        );
+        break;
+      case "report":
+        dialogTitle = "Report an Issue";
+        dialogBody = (
+          <span>
+            Need to report a problem?
+            <p />
+            You can file a{" "}
+            <a href="https://github.com/mozilla/mr-social-client/issues" target="_blank" rel="noopener noreferrer">
+              Github Issue
+            </a>{" "}
+            or e-mail us for support at <a href="mailto:hubs@mozilla.com">hubs@mozilla.com</a>.
+            <p />
+            You can also find us in #social on the{" "}
+            <a href="http://webvr-slack.herokuapp.com/" target="_blank" rel="noopener noreferrer">
+              WebVR Slack
+            </a>.
+          </span>
+        );
+        break;
+    }
+
+    return (
+      <div className="dialog-overlay">
+        <div className="dialog">
+          <div className="dialog__box">
+            <div className="dialog__box__contents">
+              <button className="dialog__box__contents__close" onClick={this.props.onCloseDialog}>
+                <span>×</span>
+              </button>
+              <div className="dialog__box__contents__title">{dialogTitle}</div>
+              <div className="dialog__box__contents__body">{dialogBody}</div>
+              <div className="dialog__box__contents__button-container" />
+            </div>
+          </div>
+        </div>
+      </div>
+    );
+  }
+}
+
+export default InfoDialog;
diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js
index 2883d23f066498e941d561b6722b9fc5b434590c..95f215ed45e75180de0878275cedc6a1616e03a3 100644
--- a/src/react-components/ui-root.js
+++ b/src/react-components/ui-root.js
@@ -12,6 +12,7 @@ import AutoExitWarning from "./auto-exit-warning";
 import { TwoDEntryButton, GenericEntryButton, GearVREntryButton, DaydreamEntryButton } from "./entry-buttons.js";
 import { ProfileInfoHeader } from "./profile-info-header.js";
 import ProfileEntryPanel from "./profile-entry-panel";
+import InfoDialog from "./info-dialog.js";
 import TwoDHUD from "./2d-hud";
 
 const mobiledetect = new MobileDetect(navigator.userAgent);
@@ -70,6 +71,7 @@ class UIRoot extends Component {
   state = {
     entryStep: ENTRY_STEPS.start,
     enterInVR: false,
+    infoDialogType: null,
 
     shareScreen: false,
     requestedScreen: false,
@@ -738,7 +740,7 @@ class UIRoot extends Component {
       "ui-dialog--darkened": this.state.entryStep !== ENTRY_STEPS.finished
     });
 
-    const dialogBoxClassNames = classNames("ui-interactive", "ui-dialog-box");
+    const dialogBoxClassNames = classNames({ "ui-interactive": !this.state.infoDialogType, "ui-dialog-box": true });
 
     const dialogBoxContentsClassNames = classNames({
       "ui-dialog-box-contents": true,
@@ -748,6 +750,11 @@ class UIRoot extends Component {
     return (
       <IntlProvider locale={lang} messages={messages}>
         <div className="ui">
+          <InfoDialog
+            dialogType={this.state.infoDialogType}
+            onCloseDialog={() => this.setState({ infoDialogType: null })}
+          />
+
           <div className={dialogClassNames}>
             {(this.state.entryStep !== ENTRY_STEPS.finished || this.isWaitingForAutoExit()) && (
               <div className={dialogBoxClassNames}>
diff --git a/yarn.lock b/yarn.lock
index 6ae1ce2d8e2b6fb237724d975e27a2145a189ca7..458a91baac73a15f97334a37bee2396ce12bb8c9 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2166,6 +2166,12 @@ copy-descriptor@^0.1.0:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
 
+copy-to-clipboard@^3.0.8:
+  version "3.0.8"
+  resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.0.8.tgz#f4e82f4a8830dce4666b7eb8ded0c9bcc313aba9"
+  dependencies:
+    toggle-selection "^1.0.3"
+
 core-js@^1.0.0:
   version "1.2.7"
   resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
@@ -7862,6 +7868,10 @@ to-regex@^3.0.1:
     extend-shallow "^2.0.1"
     regex-not "^1.0.0"
 
+toggle-selection@^1.0.3:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32"
+
 toposort@^1.0.0:
   version "1.0.6"
   resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.6.tgz#c31748e55d210effc00fdcdc7d6e68d7d7bb9cec"