Add a Code Copy Button to a Gatsby MDX Site
August 02, 2023 - 5 min read (962 words)
Many documentation sites and technical blogs feature a copy to clipboard button attached to their syntax highlighted code snippets and commands. After a great deal of searching, I was unable to find a Gatsby plugin to create one that was compatible with both modern Gatsby versions and the use of MDX. As a result, I built one.
This post covers the gatsby-remark-copy-button plugin implementation and its use in a Gatsby site.
The plugin package may be used with both of the following packages. However, it was tested primarily with the MDX plugin.
The GitHub repository storing the plugin discussed in this post can be found here. Its corresponding NPM package can be found here.
The evolving GitHub repository storing this blog and its implementation can be found here.
Table of Contents
The Plugin Implementation
The plugin was implemented following the guidelines in the Gatsby Remark Plugin Tutorial.
Stored in the /src/
folder of the plugin repository, the plugin implementation
logic is composed of four main files:
gatsby-browser.mjs
: Gatsby browser file with hooks for adding JS functions within the browserindex.mjs
: Primary plugin logic moduleparse-options.mjs
: Internal utilities for parsing option stingsstyles.scss
: A SASS file storing default component styles
The JavaScript files are parsed and transformed during the build using Babel. The SCSS file is converted to CSS using node-sass.
Dependencies
The only direct runtime dependency for the plugin is on unist-util-visit which allows the plugin to walk and modify the Markdown AST.
The Remark Plugin Entry Method
A Gatsby Remark plugin module is expected to export a default function
with the following signature and return value. An abstract syntax tree
representing the markdown file is passed with a plugin options object.
The body of the function applies logic to modify the AST and then returns
the modified tree. The file storing the module is identified in the
package.json
file using the main
property.
export default ({ markdownAST }, pluginOptions) => {
// plugin logic
// return modified markdown abstract syntax tree
return markdownAST;
};
Following the handling of plugin options, the entry method uses the
visit
function provided by the unist-util-visit
package to
inject a block of HTML
including a copy button into a node above
each code
node that calls for it using a {clipboardButton: true}
declaration.
Browser JavaScript
Using the Gatsby Browser API
onClientEntry
hook, the plugin attaches a function to the window
object to
implement copying text to the user’s clipboard using the browser
Clipboard API.
Additionally, to prevent “rage clicking”, a data attribute is added to the button
while the operation is running to introduce a lock around the clipboard and animation
function.
The default CSS styling is also imported into the browser in the gatsby-browser.js
file.
Using the Plugin Package
Install the Plugin
yarn add @jpfulton/gatsby-remark-copy-button
Configure Gatsby
Add the configuration entry to your gatsby-config.js
file. This plugin
must be added before other plugins that operate on code
nodes and
markdown code snippets to operate correctly.
The following listing assumes you are using the gatsby-plugin-mdx
plugin.
However, this plugin may also be used with the gatsby-transformer-remark
plugin.
plugins: [
{
resolve: `gatsby-plugin-mdx`,
options: {
extensions: [`.mdx`, `.md`],
gatsbyRemarkPlugins: [
{
resolve: `@jpfulton/gatsby-remark-copy-button`,
},
{
resolve: `gatsby-remark-code-titles`,
},
{
resolve: `gatsby-remark-prismjs`,
},
],
},
},
],
Configure Plugin Options
All plugin options are optional. However, it is strongly suggested that you customize them to override styling to fit your site’s look, feel and layout.
{
resolve: `@jpfulton/gatsby-remark-copy-button`,
options: {
// Provide a text label for the copy button.
// Default: null
buttonText: null,
// Provide a complete SVG tag string to replace the default
// copy icon. Be sure to include a class of "copy-icon" on your custom
// SVG tag when using this option.
copySvg: null,
// Provide a complete SVG tag string to replace the default
// success icon. Be sure to include a class of "success-icon" on your custom
// SVG tag when using this option.
successSvg: null,
// Provide a custom container class for the <div> tag that contains
// the copy button to apply custom styling.
// Default: "gatsby-remark-copy-button-container"
customButtonContainerClass: null,
// Provide a custom button class for the copy button to apply
// custom styling.
// Default: "gatsby-remark-copy-button"
customButtonClass: null,
},
},
Custom Styling
Custom styling may be applied to the default classes or using the options above custom classes may be applied to the injected markup.
.gatsby-remark-copy-button-container {
}
.gatsby-remark-copy-button {
}
Apply custom styles by adding a style sheet to your gatsby-browser.js
file.
// gatsby-browser.js
import "./src/styles/copy-button.scss";
Structure of the Injected Markup
When enabled on code snippet, the following HTML
will be injected into
the output of the page after parsing the Markdown AST using the default
plugin options. It will be injected above the code snippet in the
generated HTML
.
<div class="gatsby-remark-copy-button-container">
<button
class="gatsby-remark-copy-button"
onclick="copyToClipboard(`CLEANED CODE CONTENT TO COPY HERE`, this)"
>
<svg
class="copy-icon"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
...
</svg>
<svg
class="success-icon"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
...
</svg>
</button>
</div>
Usage in Markdown and MDX Files
Once installed, the copy button may optionally be enabled by adding
to the code snippet declaration within markdown files. When this plugin
is used in conjunction with the gatsby-remark-prismjs
plugin, the
{clipboardButton: true}
option may be provided in any order with other
prismjs options.
```js {clipboardButton: true}
const example = "This content will end up on the user's clipboard";
```
Project Contributions
Contributions to this package are welcome. To submit a feature request or report a bug, please visit the repository contributor guide.
Written by J. Patrick Fulton.