How to add copy to clipboard button.

Adding copy button in mdx in next js app router

April 25, 2024 (1mo ago) | πŸ“– 3 min read


Acme Auth


This tutorial provides a straightforward method to add a copy-to-clipboard button in an MDX file within a Next.js application.


  • MDX: A powerful markup language that combines the best of Markdown and JSX, allowing you to write content with components and logic.
  • Next.js (App Router): A React framework for building server-rendered and static web applications with routing and data fetching.
  • React: A popular JavaScript library for building user interfaces.
  • Tailwind CSS: A utility-first CSS framework for rapidly building custom designs.
  • Bright: A modern React server component for syntax highlighting


Next Mdx Remote

Next Mdx Remote is a library that allows you to load MDX files remotely, making it easy to create dynamic content that can be updated without redeploying your application.

To use Next Mdx Remote, you can install it using the following command:

npm install next-mdx-remote

Once installed, you can import it into your application and use it to load MDX files.


Create a new file named mdx.tsx in the components directory.

// components/mdx.tsx import { MDXRemote } from 'next-mdx-remote/rsc'; import { MDXComponents } from 'mdx/types'; export const mdxComponents: MDXComponents = {}; export function CustomMDX({ children, components, }: { children: string; components: MDXComponents; }) { return ( <MDXRemote source={children} components={{ ...mdxComponents, ...(components || {}) }} /> ); }

Custom Components

You can add custom components to your MDX files using the mdxComponents option. This allows you to define your own components that can be used within your MDX content.

Now let's add a custom component to our MDX file.

Create a new file named Pre.tsx in the components directory.

// components/Pre.tsx 'use client'; import { useState, useRef } from 'react'; import { Copy, Check } from 'lucide-react'; const Pre = (props: any) => { const textInput = useRef<any>(null); const [copied, setCopied] = useState(false); const onCopy = () => { setCopied(true); navigator.clipboard.writeText(textInput?.current?.textContent); setTimeout(() => { setCopied(false); }, 2000); }; return ( <div className='relative right-[20px] top-[20px]'> <span ref={textInput} className='hidden'> {props.children} </span> <button aria-label='Copy code' type='button' className='h-4 w-4' onClick={onCopy} > {copied ? ( <Check className='text-[#80d1a9]' /> ) : ( <Copy className='text-white' /> )} </button> </div> ); }; export default Pre;


Add Pre component

Import Pre component in your MDX file.

// components/mdx.tsx import { Code } from 'bright'; import Pre from './Pre'; Code.theme = { dark: 'github-dark', light: 'github-light', }; export const mdxComponents: MDXComponents = { pre: (props) => ( <div className='flex flex-row gap-2 bg-[#0d1117]'> <Code className='w-full'>{props.children}</Code> <Pre {...props} /> </div> ), };

Use CustomMDX

Import CustomMDX in page.tsx

// blog/[slug]/page.tsx import { notFound } from 'next/navigation'; import getBlogs from '@/db/get-blogs'; import { CustomMDX } from '@/components/mdx'; export default function Blog({ params }: { params: any }) { const blog = getBlogs().find((post) => post.slug === params.slug); if (!blog) { return notFound(); } return ( <div className='mx-auto mt-40 w-11/12'> <h1 className='text-start text-xl font-bold sm:text-4xl'> {blog.metadata.title} </h1> <div className='mb-4 mt-2 flex items-center justify-between'> <p className='text-lg text-neutral-700 dark:text-neutral-300'> <span className='flex flex-row items-center gap-2'> {blog.metadata.description} </span> </p> </div> <article className='prose prose-zinc mx-auto my-10 max-w-none dark:prose-invert md:prose-lg lg:prose-xl'> <CustomMDX components={{}}>{blog.content}</CustomMDX> </article> </div> ); }