Implementing a One-Click Copy Button for MDX Code Blocks in Next.js
A detailed guide on adding a copy button to MDX code blocks in your technical blog using React and modern best practices.
Implementing a One-Click Copy Button for MDX Code Blocks
This document describes how to add a one-click copy button to code blocks in an MDX technical documentation blog built with Next.js.
Background
In many blogs, code blocks are typically wrapped with triple backticks and rendered as <pre><code>
elements. To enhance user experience, we aim to place a copy button at the top-right corner of each code block, enabling users to copy the code with a single click.
Initial Approach
Initially, we used a useEffect
hook (or custom hook) on the client side to scan all <pre>
elements and dynamically inject the copy button. This required minimal changes to the existing code structure.
Improved Approach: Using <figure>
as the Positioning Container
When using tools like rehype-pretty-code
, code blocks are often wrapped in a <figure data-rehype-pretty-code-figure>
element. Instead of inserting the copy button directly into <pre>
, we can place it inside <figure>
. This avoids potential layout issues such as scrollbars or overflow on <pre>
.
-
Target
<figure>
Instead of<pre>
Search for:const figures = rootElement.querySelectorAll( "figure[data-rehype-pretty-code-figure]" );
and append the button to the
<figure>
element rather than the<pre>
element. -
Set
<figure>
toposition: relative;
In your global CSS (e.g.,globals.css
), add:figure[data-rehype-pretty-code-figure] { position: relative; }
This ensures that the button, with absolute positioning, will be positioned relative to the
<figure>
. -
Define a
.copy-button
Class with Tailwind's@apply
To avoid issues with Tailwind Purge, define the button styles in your global CSS:.copy-button { @apply absolute top-2 right-2 bg-gray-700 text-white rounded px-2 py-1 text-sm opacity-0 group-hover:opacity-100 transition; }
Then, in your JavaScript/TypeScript code, simply add the class to the button:
button.classList.add("copy-button");
Handling Style Conflicts
If other plugins or global styles interfere with the button’s positioning:
- Use browser DevTools to ensure that the
<figure>
element hasposition: relative;
and that the button receives the correct computed styles (e.g.,position: absolute; top: 0.5rem; right: 0.5rem;
). - Adjust the styles on the
<pre>
element if necessary. For example:figure[data-rehype-pretty-code-figure] pre { overflow: auto; margin: 0; padding: 1rem; }
- Verify that the Tailwind classes are not purged by using the defined
.copy-button
class with@apply
.
Conclusion
By injecting the copy button into the <figure>
element and ensuring it has a proper positioning context and dedicated styling, we can achieve a clean, one-click copy solution for MDX code blocks. This approach aligns with modern React and Next.js best practices, providing a solid foundation for future enhancements.