Enhance Markdown Code Blocks With Copy To Clipboard
Hey guys! Today, we're diving into an exciting project: adding a nifty "copy to clipboard" feature to those handy markdown syntax blocks you see all over the web. This enhancement will make it super easy for your users to grab code snippets and use them without any hassle. No more tedious manual copying – just a single click and boom, the code is ready to be pasted wherever they need it!
Why Add a Copy to Clipboard Feature?
So, why should you even bother with adding this feature? Well, think about it. As developers or content creators, we often share code examples in our blogs, documentation, and tutorials. Making it simple for readers to copy these code snippets improves their experience and encourages them to engage more with your content. Here’s a breakdown of the benefits:
- Improved User Experience: Let's face it, manually selecting and copying code can be a pain. A single-click copy button makes the process much smoother and more user-friendly.
- Increased Engagement: When it's easy to copy and paste code, users are more likely to experiment with it, leading to deeper engagement with your content.
- Professional Look: A copy to clipboard feature adds a touch of polish to your site, making it look more professional and well-maintained.
- Accessibility: It makes your content more accessible to users who might have difficulty with manual selection, especially on mobile devices.
By providing this feature, you're not just adding a button; you're enhancing the overall usability and appeal of your site. Plus, it shows that you care about your audience and are willing to go the extra mile to make their lives easier.
Leveraging Shiki for Code Highlighting and More
Before we get into the nitty-gritty, let’s talk about Shiki. Shiki is a fantastic syntax highlighter that can transform your markdown code blocks into beautifully rendered and interactive elements. It's not just about making code look pretty; Shiki also provides powerful tools for adding functionality, like our copy to clipboard feature.
Shiki achieves this through something called transformer blocks. These are essentially plugins that can modify the output of Shiki's highlighting process. In our case, we'll use a transformer block to inject a copy button into each code block. When a user clicks this button, the code inside the block will be copied to their clipboard. Cool, right?
Ebacala's blog post (https://ebacala.com/blog/create-a-code-block-with-a-copy-button-using-astro-markdown-and-shiki/) provides an excellent example of how to implement this using Astro, markdown, and Shiki. We'll be adapting some of those concepts to fit our specific needs.
Step-by-Step Implementation
Alright, let’s get our hands dirty with the implementation. Here’s a breakdown of the steps we’ll be following:
- Create a
shiki.transformer.utils.tsFile: This file will house the core logic for our transformer block. It will contain the functions needed to generate the copy button and handle the clipboard functionality. - Wire Up to
vite.config.ts: We'll need to configure Vite (or your build tool of choice) to include our transformer block in the Shiki highlighting process. This involves modifying thevite.config.tsfile to import and register our transformer. - Style Transformer Markup and Support Themes: Finally, we'll add some CSS styling to make the copy button look good and ensure it integrates seamlessly with your site's theme. This includes handling different color schemes and ensuring the button is accessible.
Let's dive into each of these steps in detail.
1. Create shiki.transformer.utils.ts
First up, we need to create a utility file that will contain the logic for our Shiki transformer. This file will be responsible for generating the HTML for the copy button and attaching the necessary event listeners to handle the copy functionality. Create a new file named shiki.transformer.utils.ts in your project, and add the following code:
// shiki.transformer.utils.ts
import { h } from 'hastscript';
import { visit } from 'unist-util-visit';
export function addCopyToClipboard() {
return (tree: any) => {
visit(tree, 'element', (node: any) => {
if (node.tagName === 'pre' && node.children && node.children[0].tagName === 'code') {
const codeNode = node.children[0];
const codeText = codeNode.children.map((child: any) => child.value).join('');
const copyButton = h('button',
{
class: 'copy-button',
'aria-label': 'Copy code to clipboard'
},
'Copy'
);
copyButton.data = {
hProperties: {
onclick: `navigator.clipboard.writeText(\'${codeText.replace(/\n/g, '\\n').replace(/'/g, '\'')}\')`,
},
};
node.children.unshift(copyButton);
}
});
};
}
Explanation:
- Imports: We import
hfromhastscriptto create HTML elements andvisitfromunist-util-visitto traverse the syntax tree. addCopyToClipboardFunction: This is our main transformer function. It takes the syntax tree as input and modifies it.visitFunction: We usevisitto find allpreelements that contain acodeelement. These are our code blocks.- Code Extraction: We extract the code text from the
codeelement by mapping over its children and joining their values. - Copy Button Creation: We create a
buttonelement with the classcopy-buttonand anaria-labelfor accessibility. The text of the button is set to "Copy". onclickHandler: This is where the magic happens. We set theonclickattribute of the button to a JavaScript function that uses thenavigator.clipboard.writeText()method to copy the code to the clipboard. We also escape special characters in the code to prevent errors.- Adding the Button: Finally, we use
unshiftto add the copy button as the first child of thepreelement, placing it above the code block.
2. Wire Up to vite.config.ts
Next, we need to tell Vite (or your bundler) to use our transformer when highlighting code with Shiki. Open your vite.config.ts file and modify it as follows:
// vite.config.ts
import { defineConfig } from 'vite'
import { sveltekit } from '@sveltejs/kit/vite';
import shiki from 'shiki';
import { addCopyToClipboard } from './src/lib/shiki.transformer.utils';
export default defineConfig({
plugins: [
sveltekit(),
{
name: 'shiki-transformer',
async transform(code, id) {
if (id.endsWith('.svelte.md') || id.endsWith('.md')) {
const highlighter = await shiki.getHighlighter({
theme: 'dracula',
});
highlighter.codeToHtml = (code, lang) => {
const originalCodeToHtml = highlighter.codeToHtml.bind(highlighter);
return originalCodeToHtml(code, lang, { transformers: [addCopyToClipboard()] });
};
return code;
}
},
},
],
});
Explanation:
- Imports: We import
shikiand ouraddCopyToClipboardfunction. - Vite Plugin: We create a Vite plugin named
shiki-transformer. transformHook: This hook is called whenever Vite processes a file. We check if the file is a markdown file (either.svelte.mdor.md).- Shiki Highlighter: We get a Shiki highlighter instance with the
draculatheme (you can choose any theme you like). - Overriding
codeToHtml: This is the crucial part. We override thecodeToHtmlmethod of the highlighter to include our transformer. We usebindto preserve the context of the originalcodeToHtmlmethod and then call it with our transformer in thetransformersarray.
3. Style Transformer Markup and Support Themes
Finally, we need to add some CSS to style our copy button. Create a new CSS file (e.g., src/lib/copy-button.css) and add the following styles:
/* src/lib/copy-button.css */
.copy-button {
position: absolute;
top: 0.5rem;
right: 0.5rem;
padding: 0.25rem 0.5rem;
background-color: #444;
color: #fff;
border: none;
border-radius: 0.25rem;
cursor: pointer;
font-size: 0.75rem;
opacity: 0.7;
transition: opacity 0.2s ease-in-out;
}
.copy-button:hover {
opacity: 1;
}
Explanation:
- Positioning: We use
position: absoluteto position the button in the top-right corner of the code block. - Styling: We add some basic styling to make the button look visually appealing.
- Opacity: We use
opacityto make the button slightly transparent by default and fully opaque on hover. - Transition: We add a
transitionto create a smooth hover effect.
Import this CSS file in your main layout or app component to apply the styles globally.
Wrapping Up
And there you have it! You’ve successfully added a copy to clipboard feature to your markdown code blocks using Shiki. Your users can now easily copy code snippets with a single click, making their experience much smoother and more enjoyable. This small enhancement can significantly improve the usability and appeal of your site. Keep experimenting with Shiki and transformer blocks to add even more cool features to your code blocks!