Getting Started
You want to first install Lit into your current Astro project. You can do this with
npm i litCreating JS files
In your components folder, create a new folder named copy-code (or whatever name you prefer.) Create these files in the folder you just created and paste the following code:
import { LitElement, html, css } from "lit";
export const copyIcon = html`<svg xmlns="http://www.w3.org/2000/svg" height="18px" viewBox="0 0 24 24" width="18px" fill="currentColor"> <path d="M0 0h24v24H0z" fill="none" /> <path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z" /></svg>`;
export const checkIcon = html`<svg xmlns="http://www.w3.org/2000/svg" height="18px" viewBox="0 0 24 24" width="18px" fill="#00FF00"> <path d="M0 0h24v24H0z" fill="none" /> <path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z" /></svg>`;
export class CopyCodeButton extends LitElement { static styles = [ css` :host { display: inline-flex; position: absolute; top: 0.5rem; right: 0.5rem; } button { display: inline-flex; align-items: center; justify-content: center; width: fit-content; margin: 0; padding: 0.5rem; background: #e2e8f022; border: none; border-radius: 0.25rem; color: #fff; cursor: pointer; font-weight: 600; }
@keyframes animateScale { 0% { transform: scale(1); } 50% { transform: scale(1.2); } 100% { transform: scale(1); } }
button.clicked svg { animation: animateScale 0.5s ease; } `, ];
constructor() { super(); this._isCopied = false; }
static get properties() { return { _isCopied: { type: Boolean }, }; }
copyCode() { this._isCopied = true; const pre = this.parentElement; let code = pre.querySelector("code"); const range = document.createRange(); range.selectNode(code); window.getSelection().removeAllRanges(); window.getSelection().addRange(range);
// check if the browser supports clipboard API if (!navigator.clipboard) { // if not use the old commandExec() way document.execCommand("copy"); } else { try { navigator.clipboard.writeText(range.toString()); } catch (error) { console.error(error); } } window.getSelection().removeAllRanges(); setTimeout(() => { this._isCopied = false; }, 1000);
const button = this.shadowRoot.querySelector("button"); button.classList.add("clicked"); setTimeout(() => { this._isCopied = false; button.classList.remove("clicked"); }, 1000); } render() { return html` <button @click=${this.copyCode}> ${this._isCopied ? checkIcon : copyIcon} </button> `; }}customElements.define("copy-code-button", CopyCodeButton);let blocks = document.querySelectorAll("pre");
blocks.forEach((block) => { let codeElement = block.querySelector("code");
let containerDiv = document.createElement("div"); containerDiv.classList.add("code-container");
containerDiv.appendChild(codeElement);
block.appendChild(containerDiv);
let button = document.createElement("copy-code-button"); containerDiv.appendChild(button); // Append button to the containerDiv, not block});pre[class*="language-"] { overflow: auto;}
pre { position: relative;}
.code-container { position: relative; overflow-x: auto; max-width: 100%;}
pre code { max-height: 300px; overflow-y: auto; display: block;}
copy-code-button { position: absolute; top: 5px; right: 10px;}
pre code::-webkit-scrollbar-corner { background-color: transparent;}
pre code::-webkit-scrollbar { width: 2px; height: 2px; background-color: transparent;}
pre code::-webkit-scrollbar-track { background-color: transparent;}
pre code.dark::-webkit-scrollbar-thumb { border-radius: 8px; background-color: #363636;}
pre code.dark::-webkit-scrollbar-thumb:hover { background-color: #555;}
pre code::-webkit-scrollbar-thumb { border-radius: 8px; background-color: #c1c1c1;}
pre code::-webkit-scrollbar-thumb:hover { background-color: #a0a0a0;}
pre code { scrollbar-width: thin; scrollbar-color: #363636 transparent;}
pre code.dark::-moz-scrollbar-thumb { border-radius: 8px; background-color: #363636;}
pre code.dark::-moz-scrollbar-thumb:hover { background-color: #555;}
pre code::-moz-scrollbar { width: 5px; background-color: transparent;}
pre code::-moz-scrollbar-track { background-color: transparent;}
pre code::-moz-scrollbar-thumb { border-radius: 8px; background-color: #c1c1c1;}
pre code::-moz-scrollbar-thumb:hover { background-color: #a0a0a0;}
pre code { scrollbar-width: 0; -ms-overflow-style: none; overflow: -moz-scrollbars-none;}
pre code.dark { scrollbar-width: thin; -ms-overflow-style: auto; overflow: overlay;}In your layouts folder, go to the main layout and add these lines of code:
---import "../styles/globals.scss";---
<head> <script> import "@/components/copy-code/copy-code-button.js"; import "@/components/copy-code/create-copy-buttons.js"; </script></head>And you’re basically all set! Just refresh the page and a copy button should appear on all your code blocks!
Acknowledgements:
This guide was inspired by Jaydan Urwin. However, his code had a couple of bugs with how the copy button was positioned (during overflow). This code fixes that as well as provide scrollbars when an overflow occurs.