From d1b645ec306307069e347674286dcb9574307512 Mon Sep 17 00:00:00 2001
From: Rajan <139620395+Rajankannaujiya@users.noreply.github.com>
Date: Tue, 25 Nov 2025 07:51:58 +0000
Subject: [PATCH 1/5] feat: implement copy button UI (#7698)
---
.../CodeBlockWithCopy/CodeBlockWithCopy.jsx | 72 ++++++++++++++++++
.../CodeBlockWithCopy/CodeBlockWithCopy.scss | 74 +++++++++++++++++++
src/mdx-components.js | 2 +
3 files changed, 148 insertions(+)
create mode 100644 src/components/CodeBlockWithCopy/CodeBlockWithCopy.jsx
create mode 100644 src/components/CodeBlockWithCopy/CodeBlockWithCopy.scss
diff --git a/src/components/CodeBlockWithCopy/CodeBlockWithCopy.jsx b/src/components/CodeBlockWithCopy/CodeBlockWithCopy.jsx
new file mode 100644
index 000000000000..07dd90a377f4
--- /dev/null
+++ b/src/components/CodeBlockWithCopy/CodeBlockWithCopy.jsx
@@ -0,0 +1,72 @@
+import { useRef, useState } from 'react';
+import PropTypes from 'prop-types';
+import './CodeBlockWithCopy.scss';
+
+export default function CodeBlockWithCopy({ children }) {
+ const preRef = useRef(null);
+ const [copyStatus, setCopyStatus] = useState('copy');
+
+ const handleCopy = async () => {
+ if (!preRef.current) return;
+
+ const codeElement = preRef.current.querySelector('code');
+ if (!codeElement) return;
+
+ const codeText = codeElement.textContent;
+ let successfulCopy = false;
+
+ // Try modern API (navigator.clipboard) -> as document.execCommand() deprecated
+ try {
+ if (navigator.clipboard && window.isSecureContext) {
+ await navigator.clipboard.writeText(codeText);
+ successfulCopy = true;
+ }
+ } catch (err) {
+ console.log(err);
+ }
+
+ // If modern API failed, fall back to deprecated document.execCommand('copy')
+ if (!successfulCopy) {
+ const textarea = document.createElement('textarea');
+ textarea.value = codeText;
+ textarea.style.position = 'fixed';
+ textarea.style.opacity = '0';
+
+ document.body.appendChild(textarea);
+ textarea.select();
+
+ try {
+ // This deprecated method is kept as a fallback for compatibility/iframe environments.
+ successfulCopy = document.execCommand('copy');
+ } catch (err) {
+ successfulCopy = false;
+ console.log(err);
+ }
+
+ document.body.removeChild(textarea);
+ }
+
+ setCopyStatus(successfulCopy ? 'copied' : 'error');
+ setTimeout(() => setCopyStatus('copy'), 2000);
+ };
+
+ return (
+
+
+
+
+ {children}
+
+
+ );
+}
+
+CodeBlockWithCopy.propTypes = {
+ children: PropTypes.node.isRequired,
+};
diff --git a/src/components/CodeBlockWithCopy/CodeBlockWithCopy.scss b/src/components/CodeBlockWithCopy/CodeBlockWithCopy.scss
new file mode 100644
index 000000000000..18e59c74236d
--- /dev/null
+++ b/src/components/CodeBlockWithCopy/CodeBlockWithCopy.scss
@@ -0,0 +1,74 @@
+.code-block-wrapper {
+ position: relative;
+ margin-bottom: 1.5rem;
+}
+
+.code-block {
+ background-color: #2d3748;
+ color: #e2e8f0;
+ padding: 1rem;
+ padding-right: 3.5rem;
+ border-radius: 0.5rem;
+ overflow-x: auto;
+ font-size: 0.875rem;
+ line-height: 1.5;
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);
+
+ code {
+ font-family: monospace;
+ }
+}
+
+.copy-button {
+ position: absolute;
+ top: 0.32rem;
+ right: 0.5rem;
+ z-index: 10;
+
+ padding: 0.4rem 0.7rem;
+ border-radius: 0.35rem;
+
+ border: none;
+ cursor: pointer;
+
+ font-size: 0.75rem;
+ font-weight: 500;
+
+ /* Always visible */
+ opacity: 1;
+
+ background-color: #7c3aed;
+ color: #e2e8f0;
+
+ transition:
+ background-color 0.2s,
+ transform 0.1s;
+
+ &:hover {
+ background-color: #6d28d9;
+ }
+
+ /* Success */
+ &.copied {
+ background-color: #38a169;
+ }
+ &.copied:hover {
+ background-color: #2f855a;
+ }
+
+ /* Error */
+ &.error {
+ background-color: #e53e3e;
+ }
+ &.error:hover {
+ background-color: #c53030;
+ }
+
+ &:focus {
+ outline: none;
+ }
+
+ &:active {
+ transform: scale(0.95);
+ }
+}
diff --git a/src/mdx-components.js b/src/mdx-components.js
index 0a27d381804e..c6a120cec2d3 100644
--- a/src/mdx-components.js
+++ b/src/mdx-components.js
@@ -1,6 +1,7 @@
import Badge from './components/Badge/Badge';
import LinkComponent from './components/mdxComponents/Link';
import StackBlitzPreview from './components/StackBlitzPreview/StackBlitzPreview';
+import CodeBlockWithCopy from './components/CodeBlockWithCopy/CodeBlockWithCopy';
/** @returns {import('mdx/types.js').MDXComponents} */
export function useMDXComponents() {
@@ -8,5 +9,6 @@ export function useMDXComponents() {
a: LinkComponent,
Badge: Badge,
StackBlitzPreview: StackBlitzPreview,
+ pre: CodeBlockWithCopy,
};
}
From 6b5fdddc587c964832a7f16bbea4986bb0b45fa1 Mon Sep 17 00:00:00 2001
From: Rajan <139620395+Rajankannaujiya@users.noreply.github.com>
Date: Sat, 29 Nov 2025 12:11:00 +0530
Subject: [PATCH 2/5] wrapped output usage example with CodeBlockWithCopy
---
src/content/concepts/output.mdx | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/content/concepts/output.mdx b/src/content/concepts/output.mdx
index 71f599ccd84c..2af2a3d84741 100644
--- a/src/content/concepts/output.mdx
+++ b/src/content/concepts/output.mdx
@@ -17,6 +17,8 @@ The minimum requirement for the `output` property in your webpack configuration
**webpack.config.js**
+
+
```javascript
module.exports = {
output: {
@@ -24,6 +26,8 @@ module.exports = {
},
};
```
+
+
This configuration would output a single `bundle.js` file into the `dist` directory.
From 1da6b56bc9e079307a8864fd471593e28cf47a47 Mon Sep 17 00:00:00 2001
From: Rajan <139620395+Rajankannaujiya@users.noreply.github.com>
Date: Sat, 29 Nov 2025 12:25:40 +0530
Subject: [PATCH 3/5] import CodeBlockWithCopy component in output.mdx
---
src/content/concepts/output.mdx | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/content/concepts/output.mdx b/src/content/concepts/output.mdx
index 2af2a3d84741..4f0851665c34 100644
--- a/src/content/concepts/output.mdx
+++ b/src/content/concepts/output.mdx
@@ -9,6 +9,8 @@ contributors:
- EugeneHlushko
---
+import CodeBlockWithCopy from "../../../components/CodeBlockWithCopy"
+
Configuring the `output` configuration options tells webpack how to write the compiled files to disk. Note that, while there can be multiple `entry` points, only one `output` configuration is specified.
## Usage
@@ -26,7 +28,7 @@ module.exports = {
},
};
```
-
+
This configuration would output a single `bundle.js` file into the `dist` directory.
From dc4a4c3bebcb6e7dae111f3231076893950143e4 Mon Sep 17 00:00:00 2001
From: Rajan <139620395+Rajankannaujiya@users.noreply.github.com>
Date: Sat, 29 Nov 2025 12:28:16 +0530
Subject: [PATCH 4/5] fixed singlequote issue of string
---
src/content/concepts/output.mdx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/content/concepts/output.mdx b/src/content/concepts/output.mdx
index 4f0851665c34..cb97ff3de850 100644
--- a/src/content/concepts/output.mdx
+++ b/src/content/concepts/output.mdx
@@ -9,7 +9,7 @@ contributors:
- EugeneHlushko
---
-import CodeBlockWithCopy from "../../../components/CodeBlockWithCopy"
+import CodeBlockWithCopy from '../../../components/CodeBlockWithCopy'
Configuring the `output` configuration options tells webpack how to write the compiled files to disk. Note that, while there can be multiple `entry` points, only one `output` configuration is specified.
From f29e6e63cd829b0a76d22e9ce58accac959773fa Mon Sep 17 00:00:00 2001
From: Rajan <139620395+Rajankannaujiya@users.noreply.github.com>
Date: Sat, 29 Nov 2025 12:37:38 +0530
Subject: [PATCH 5/5] fixed file path of CodBlockWithCopy
---
src/content/concepts/output.mdx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/content/concepts/output.mdx b/src/content/concepts/output.mdx
index cb97ff3de850..ef87d4af7ba4 100644
--- a/src/content/concepts/output.mdx
+++ b/src/content/concepts/output.mdx
@@ -9,7 +9,7 @@ contributors:
- EugeneHlushko
---
-import CodeBlockWithCopy from '../../../components/CodeBlockWithCopy'
+import CodeBlockWithCopy from '../../components/CodeBlockWithCopy/CodeBlockWithCopy'
Configuring the `output` configuration options tells webpack how to write the compiled files to disk. Note that, while there can be multiple `entry` points, only one `output` configuration is specified.