diff --git a/develop-docs/sdk/getting-started/templates/saved-replies/index.mdx b/develop-docs/sdk/getting-started/templates/saved-replies/index.mdx
index ad7c321c7a6dd5..7d1c51852cad3d 100644
--- a/develop-docs/sdk/getting-started/templates/saved-replies/index.mdx
+++ b/develop-docs/sdk/getting-started/templates/saved-replies/index.mdx
@@ -12,31 +12,46 @@ To set up saved replies go to [GitHub > Settings > Saved replies](https://github
---
-## Open an issue first (behavior/refactor -> close)
+ Thanks for the contribution! We ask that behavioral changes and refactors have a linked issue so we can discuss the approach before a PR is opened. Could you open an issue describing the problem you're solving and your proposed approach? Closing this for now - happy to revisit once there's an issue to reference.
->
-> Please also have a look at our CONTRIBUTING.md for more PR guidelines.
+Please also have a look at our CONTRIBUTING.md for more PR guidelines.`}>
----
+Thanks for the contribution! We ask that behavioral changes and refactors have a linked issue so we can discuss the approach before a PR is opened. Could you open an issue describing the problem you're solving and your proposed approach? Closing this for now - happy to revisit once there's an issue to reference.
-## Let's discuss the approach first (idea -> close)
+Please also have a look at our CONTRIBUTING.md for more PR guidelines.
-> This is an interesting idea! We'd like to align on the approach before reviewing code - could you open an issue describing the problem and your proposed solution? That way we can agree on direction first. Closing this PR for now.
->
-> Please also have a look at our CONTRIBUTING.md for more PR guidelines.
+
----
+
+
+This is an interesting idea! We'd like to align on the approach before reviewing code - could you open an issue describing the problem and your proposed solution? That way we can agree on direction first. Closing this PR for now.
+
+Please also have a look at our CONTRIBUTING.md for more PR guidelines.
+
+
+
+
+
+Thanks for the contribution! This PR needs some updates before we can review:
+
+- [ ] CI checks are passing
+- [ ] PR description explains what and why
+- [ ] Linked issue exists
+- [ ] Tests are included
+
+We marked it as draft for now. Please update and we'll take another look.
+
+Please also have a look at our CONTRIBUTING.md for more PR guidelines.
-## Not ready for review (request changes / mark as draft)
-
-> Thanks for the contribution! This PR needs some updates before we can review:
->
-> - [ ] CI checks are passing
-> - [ ] PR description explains what and why
-> - [ ] Linked issue exists
-> - [ ] Tests are included
->
-> We marked it as draft for now. Please update and we'll take another look.
->
-> Please also have a look at our CONTRIBUTING.md for more PR guidelines.
+
diff --git a/src/components/copyableCard.tsx b/src/components/copyableCard.tsx
new file mode 100644
index 00000000000000..45112bd6c27e28
--- /dev/null
+++ b/src/components/copyableCard.tsx
@@ -0,0 +1,149 @@
+'use client';
+
+import {Fragment, useEffect, useRef, useState} from 'react';
+import {createPortal} from 'react-dom';
+import {Clipboard} from 'react-feather';
+
+import Chevron from 'sentry-docs/icons/Chevron';
+
+interface CopyableCardProps {
+ children: React.ReactNode;
+ description: string;
+ title: string;
+}
+
+export function CopyableCard({title, description, children}: CopyableCardProps) {
+ const [copiedItem, setCopiedItem] = useState<'title' | 'description' | null>(null);
+ const [isOpen, setIsOpen] = useState(false);
+ const [isMounted, setIsMounted] = useState(false);
+ const buttonRef = useRef(null);
+ const dropdownRef = useRef(null);
+
+ useEffect(() => {
+ setIsMounted(true);
+
+ const handleClickOutside = (event: MouseEvent) => {
+ if (
+ buttonRef.current &&
+ !buttonRef.current.contains(event.target as Node) &&
+ dropdownRef.current &&
+ !dropdownRef.current.contains(event.target as Node)
+ ) {
+ setIsOpen(false);
+ }
+ };
+
+ document.addEventListener('mousedown', handleClickOutside);
+ return () => {
+ document.removeEventListener('mousedown', handleClickOutside);
+ };
+ }, []);
+
+ async function copyText(text: string, item: 'title' | 'description') {
+ try {
+ await navigator.clipboard.writeText(text.trim());
+ setCopiedItem(item);
+ setIsOpen(false);
+ setTimeout(() => setCopiedItem(null), 1500);
+ } catch (error) {
+ // eslint-disable-next-line no-console
+ console.error('Failed to copy:', error);
+ }
+ }
+
+ const getDropdownPosition = () => {
+ if (!buttonRef.current) {
+ return {top: 0, left: 0};
+ }
+ const rect = buttonRef.current.getBoundingClientRect();
+ return {
+ top: rect.bottom + 8,
+ left: rect.right - 160,
+ };
+ };
+
+ const buttonClass =
+ 'inline-flex items-center text-nowrap h-full text-gray-700 dark:text-[var(--foreground)] bg-transparent border-none cursor-pointer transition-colors duration-150 hover:bg-gray-50 dark:hover:bg-[var(--gray-a4)] active:bg-gray-100 dark:active:bg-[var(--gray-5)] focus:bg-gray-50 dark:focus:bg-[var(--gray-a4)] outline-none';
+ const dropdownItemClass =
+ 'w-full p-2 px-3 text-left text-sm bg-transparent border-none rounded-md transition-colors hover:bg-gray-100 dark:hover:bg-[var(--gray-a4)] font-sans text-gray-900 dark:text-[var(--foreground)] cursor-pointer';
+
+ const getButtonLabel = () => {
+ if (copiedItem === 'title') {
+ return 'Reply title copied!';
+ }
+ if (copiedItem === 'description') {
+ return 'Reply description copied!';
+ }
+ return 'Copy';
+ };
+
+ return (
+
+
+
+ {title}
+
+
+ {isMounted && (
+
+
+
+
+
+
+
+
+
+
+
+ {isOpen &&
+ createPortal(
+
+
+
+
+
+
,
+ document.body
+ )}
+
+ )}
+
+
+
{children}
+
+
+ );
+}
diff --git a/src/mdxComponents.ts b/src/mdxComponents.ts
index 508cc8c9b14448..29cae613ee3fb2 100644
--- a/src/mdxComponents.ts
+++ b/src/mdxComponents.ts
@@ -10,6 +10,7 @@ import {CommunitySupportedPlatforms} from './components/communitySupportedPlatfo
import {ConfigKey} from './components/configKey';
import {ConfigValue} from './components/configValue';
import {ContentSeparator} from './components/contentSeparator';
+import {CopyableCard} from './components/copyableCard';
import {CreateGitHubAppForm} from './components/createGitHubAppForm';
import {DefinitionList} from './components/definitionList';
import {DevDocsCardGrid} from './components/devDocsCardGrid';
@@ -114,6 +115,7 @@ export function mdxComponents(
OnboardingSteps,
RelayMetrics,
SandboxLink,
+ CopyableCard,
SignInNote,
SplitLayout,
SplitSection,