home

Adding Copy Buttons to Code Blocks

[ web development · javascript · astro · ux ]

A simple but essential user experience improvement: one-click copying for code snippets.

The Problem

When readers encounter code examples, they often want to copy them for testing or reference. Without a copy button, users must manually select all the text, which is a tedious process that’s especially frustrating on mobile devices or with longer code blocks.

The Solution

I implemented copy buttons that appear alongside the existing language badges, using the Clipboard API for reliable copying and providing visual feedback when the operation succeeds.

document.addEventListener("DOMContentLoaded", function () {
    const codeBlocks = document.querySelectorAll("pre[data-language]");

    codeBlocks.forEach(function (pre) {
        const copyButton = document.createElement("button");
        copyButton.className = "copy-button";
        copyButton.innerHTML = '<svg>...</svg>'; // Clipboard icon
        copyButton.setAttribute("aria-label", "Copy code to clipboard");

        copyButton.addEventListener("click", function () {
            const code = pre.querySelector("code");
            if (!code) return;
            const text = code.textContent || code.innerText;

            navigator.clipboard.writeText(text).then(function () {
                // Show checkmark feedback
                copyButton.innerHTML = '<svg>...</svg>'; // Check icon
                setTimeout(function () {
                    copyButton.innerHTML = '<svg>...</svg>'; // Back to clipboard
                }, 2000);
            });
        });

        pre.appendChild(copyButton);
    });
});

Implementation Details

The approach builds on the existing code block infrastructure:

Styling Integration

The copy button is positioned to complement the existing language badge:

/* Copy button positioned on the far right */
.copy-button {
    position: absolute;
    bottom: 8px;
    right: 8px;
    background: rgba(0, 0, 0, 0.7);
    color: #fff;
    border: none;
    padding: 3px 4px;
    border-radius: 3px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 2;
    transition: background 0.2s ease;
}

/* Language badge positioned to the left of copy button */
pre[data-language]::before {
    content: attr(data-language);
    position: absolute;
    bottom: 8px;
    right: 36px;
    background: rgba(0, 0, 0, 0.7);
    color: #fff;
    padding: 2px 6px;
    border-radius: 3px;
    font-size: 0.7rem;
    z-index: 1;
}

Key design decisions:

Why This Matters

Copy buttons remove friction from the code sharing experience. They’re especially valuable for:

This enhancement demonstrates how small UX improvements can significantly impact user experience. The implementation is lightweight, progressively enhanced, and integrates seamlessly with the existing design system.