diff --git a/docs-ui/components/textCopyInput.stories.js b/docs-ui/components/textCopyInput.stories.js
new file mode 100644
index 00000000000000..2b8507234a8678
--- /dev/null
+++ b/docs-ui/components/textCopyInput.stories.js
@@ -0,0 +1,13 @@
+import React from 'react';
+import {storiesOf} from '@storybook/react';
+import {withInfo} from '@storybook/addon-info';
+import {action} from '@storybook/addon-actions';
+
+import TextCopyInput from 'application-root/views/settings/components/forms/textCopyInput';
+
+storiesOf('TextCopyInput', module).add(
+ 'default',
+ withInfo('Description')(() => (
+ Value to be copied
+ ))
+);
diff --git a/src/sentry/static/sentry/app/views/settings/components/forms/textCopyInput.jsx b/src/sentry/static/sentry/app/views/settings/components/forms/textCopyInput.jsx
new file mode 100644
index 00000000000000..0de9f99a761748
--- /dev/null
+++ b/src/sentry/static/sentry/app/views/settings/components/forms/textCopyInput.jsx
@@ -0,0 +1,93 @@
+import {Flex} from 'grid-emotion';
+import PropTypes from 'prop-types';
+import React from 'react';
+import ReactDOM from 'react-dom';
+import styled from 'react-emotion';
+
+import {inputStyles} from './styled/styles';
+import {selectText} from '../../../../utils/selectText';
+import AutoSelectText from '../../../../components/autoSelectText';
+import Button from '../../../../components/buttons/button';
+import Clipboard from '../../../../components/clipboard';
+import InlineSvg from '../../../../components/inlineSvg';
+
+const StyledAutoSelectText = styled(AutoSelectText)`
+ ${inputStyles};
+ display: inline-block;
+ width: auto;
+ padding: 0;
+`;
+
+const OverflowContainer = styled(Flex)`
+ overflow: hidden;
+ text-overflow: ellipsis;
+`;
+
+const Wrapper = styled(Flex)`
+ overflow: hidden;
+`;
+
+class TextCopyInput extends React.Component {
+ static propTypes = {
+ /**
+ * Text to copy
+ */
+ children: PropTypes.string.isRequired,
+ /**
+ * CSS style object
+ */
+ style: PropTypes.object,
+ onCopy: PropTypes.func,
+ };
+
+ static defaultProps = {
+ onCopy: () => {},
+ };
+
+ constructor(props) {
+ super(props);
+ }
+
+ // Select text when copy button is clicked
+ handleCopyClick = e => {
+ if (!this.textRef) return;
+
+ let {onCopy} = this.props;
+
+ // We use findDOMNode here because `this.textRef` is not a dom node,
+ // it's a ref to AutoSelectText
+ // eslint-disable-next-line react/no-find-dom-node
+ selectText(ReactDOM.findDOMNode(this.textRef));
+
+ onCopy(this.props.children, e);
+
+ e.stopPropagation();
+ };
+
+ handleAutoMount = ref => {
+ this.textRef = ref;
+ };
+
+ render() {
+ let {style, children} = this.props;
+
+ return (
+
+
+
+ {children}
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+export default TextCopyInput;
diff --git a/tests/js/spec/components/__snapshots__/textCopyInput.spec.jsx.snap b/tests/js/spec/components/__snapshots__/textCopyInput.spec.jsx.snap
new file mode 100644
index 00000000000000..1902342e4be3d2
--- /dev/null
+++ b/tests/js/spec/components/__snapshots__/textCopyInput.spec.jsx.snap
@@ -0,0 +1,38 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`TextCopyInput renders 1`] = `
+
+
+
+ Text to Copy
+
+
+
+
+
+
+
+
+`;
diff --git a/tests/js/spec/components/textCopyInput.spec.jsx b/tests/js/spec/components/textCopyInput.spec.jsx
new file mode 100644
index 00000000000000..55a2f6ad2a3d97
--- /dev/null
+++ b/tests/js/spec/components/textCopyInput.spec.jsx
@@ -0,0 +1,10 @@
+import React from 'react';
+import {shallow} from 'enzyme';
+import TextCopyInput from 'app/views/settings/components/forms/textCopyInput';
+
+describe('TextCopyInput', function() {
+ it('renders', function() {
+ let wrapper = shallow(Text to Copy);
+ expect(wrapper).toMatchSnapshot();
+ });
+});