Copy Button
Installation
bun add @rehype-pretty/transformers
pnpm add @rehype-pretty/transformers
npm install @rehype-pretty/transformers
yarn add @rehype-pretty/transformers
npx jsr add @rehype-pretty/transformers
Usage
You can use this as a shiki
transformer in rehype-pretty-code
by passing it to the transformers
array.
Options
visibility
:'always' | 'hover'
(default:'hover'
)feedbackDuration
:number
(default:3_000
)copyIcon
:string
(default: an inline SVG of a copy icon)successIcon
:string
(default: an inline SVG of a green checkmark icon)jsx
?:boolean
(default:false
) (required for React-based usage)
Simple
with rehype-pretty-code
import { unified } from 'unified'
import remarkParse from 'remark-parse'
import remarkRehype from 'remark-rehype'
import rehypeStringify from 'rehype-stringify'
import { rehypePrettyCode } from 'rehype-pretty-code'
import { transformerCopyButton } from '@rehype-pretty/transformers'
const file = await unified()
.use(remarkParse)
.use(remarkRehype)
.use(rehypePrettyCode, {
transformers: [
transformerCopyButton({
visibility: 'always',
feedbackDuration: 3_000,
}),
],
})
.use(rehypeStringify)
.process(`\`\`\`js\nconsole.log('Hello, World!')\n\`\`\``)
console.log(String(file))
with shiki
import { codeToHtml } from 'shiki'
const code = await codeToHtml('console.log("Hello World")', {
lang: 'ts',
theme: 'vitesse-light',
transformers: [
transformerCopyButton({
visibility: 'always',
feedbackDuration: 3_000,
}),
]
})
React / Next.js
To use this with React, you need to import the registerCopyButton
function and call it in your the outermost client component.
Next.js example:
/**
* @typedef {import('next').NextConfig} NextConfig
* @typedef {Array<((config: NextConfig & any) => NextConfig)>} NextConfigPlugins
* @typedef {import('webpack').Configuration} WebpackConfiguration
*/
import nextMDX from '@next/mdx';
import rehypeSlug from 'rehype-slug';
import { rehypePrettyCode } from 'rehype-pretty-code';
import { transformerCopyButton } from '@rehype-pretty/transformers';
/** @type {NextConfigPlugins} */
const plugins = [];
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'export',
reactStrictMode: true,
pageExtensions: ['md', 'mdx', 'tsx', 'ts', 'jsx', 'js'],
};
/** @type {import('rehype-pretty-code').RehypePrettyCodeOptions} */
const options = {
keepBackground: false,
theme: 'github-dark',
transformers: [
transformerCopyButton({
jsx: true, // required for React
visibility: 'always',
feedbackDuration: 2_500,
}),
],
};
plugins.push(
nextMDX({
extension: /\.(md|mdx)$/,
options: {
remarkPlugins: [],
rehypePlugins: [[rehypePrettyCode, options], rehypeSlug],
},
}),
);
export default () => plugins.reduce((_, plugin) => plugin(_), nextConfig);
'use client';
import { registerCopyButton } from '@rehype-pretty/transformers';
export default function Home() {
React.useEffect(() => {
registerCopyButton();
}, []);
return (
<MDXProvider disableParentContext={false}>
<Index />
</MDXProvider>
);
}