Enhance the code block in Gutenberg with a code snippet

Introduction

Boost your Gutenberg code blocks with just a simple snippet!
If you often share code on your WordPress site, adding line numbers and a copy button can make your content much easier to read and interact with. In this tutorial, I’ll show you how to enhance the default Gutenberg code block using a lightweight, easy-to-add PHP snippet without needing any plugins.

By default, the Gutenberg Code block in WordPress is very basic — no line numbers and no easy way for visitors to copy the code.
In this tutorial. We will create a single code snippet that:

  • Adds line numbers automatically to each code block.
  • Inserts a sticky copy button styled like ChatGPT’s.
  • Works in normal, wide and full-width alignments.
  • Is responsive and uses clean and minimal CSS.

No extra plugins are required other than a code snippet plugin or a way to add code to your theme.

Step 1 – The Functional Snippet

This is the complete working snippet you paste into a Code Snippet plugin or your theme’s functions.php.
It includes all the CSS and JavaScript inline. There is no need for separate files.

add_action('wp_enqueue_scripts', function () {
    /* CSS: Line numbers, copy button, alignment fix */
    $css = <<<CSS

/* Wrapper around each code block */
.code-copy-wrapper { 
  position: relative; 
  margin-bottom: 1em; 
}

/* Alignwide setting */
.code-copy-wrapper.alignwide { 
  max-width: var(--wp--style--global--content-size); 
  margin-left: auto; 
  margin-right: auto; 
}

/* Alignfull setting */
.code-copy-wrapper.alignfull { 
  max-width: none; 
  width: 100%; 
  margin-left: 0; 
  margin-right: 0; 
}

/* Main code block styling */
.code-copy-wrapper pre.wp-block-code {
    position: relative;
    background: #f5f5f5;
    border: 1px solid #ddd;
    border-radius: 5px;
    padding: 2.7em 3.5em 1em 1em; /* right space for button, left for numbers */
    white-space: pre-wrap;
    word-wrap: break-word;
    font-family: monospace;
    counter-reset: linenumber;
    margin: 0;
    box-sizing: border-box;
}

/* Code inside block */
.code-copy-wrapper pre.wp-block-code code {
    display: block;
    white-space: pre-wrap;
    word-wrap: break-word;
    user-select: text;
}

/* Each line inside code */
.code-copy-wrapper pre.wp-block-code code > span {
    display: block;
    counter-increment: linenumber;
    padding-left: 3.5em; /* space before code text */
    position: relative;
}

/* Line number style */
.code-copy-wrapper pre.wp-block-code code > span::before {
    content: counter(linenumber);
    position: absolute;
    left: 0;
    width: 2.2em;
    text-align: right;
    padding-right: 0.5em;
    color: #999;
    user-select: none;
}

/* Copy button styling */
.code-copy-btn {
    position: absolute;
    top: 8px;
    right: 8px;
    background: transparent;
    border: none;
    cursor: pointer;
    opacity: 0.7;
    transition: opacity 0.3s ease;
    z-index: 10;
    padding: 0;
    font-size: 14px;
    color: #333;
    display: flex;
    align-items: center;
    gap: 0.25em;
}
.code-copy-btn:hover,
.code-copy-btn:focus { opacity: 1; outline: none; }
CSS;

    wp_register_script('code-enhancer', '', [], null, true);
    wp_enqueue_script('code-enhancer');
    wp_add_inline_style('wp-block-library', $css);

    /* JS: Add copy button + wrap lines */
    $js = <<<JS
document.addEventListener('DOMContentLoaded', function () {
    const clipboardSVG = '<svg aria-hidden="true" focusable="false" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>';
    const checkmarkSVG = '<svg aria-hidden="true" focusable="false" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>';

    document.querySelectorAll('pre.wp-block-code').forEach(pre => {
        if (pre.querySelector('.code-copy-btn')) return;

        // Wrap with alignment support
        if (!pre.parentNode.classList.contains('code-copy-wrapper')) {
            const wrapper = document.createElement('div');
            wrapper.className = 'code-copy-wrapper';
            if (pre.classList.contains('alignwide')) wrapper.classList.add('alignwide');
            if (pre.classList.contains('alignfull')) wrapper.classList.add('alignfull');
            pre.parentNode.insertBefore(wrapper, pre);
            wrapper.appendChild(pre);
        }

        // Add copy button
        const button = document.createElement('button');
        button.className = 'code-copy-btn';
        button.type = 'button';
        button.setAttribute('aria-label', 'Copy code to clipboard');
        button.innerHTML = clipboardSVG + ' Copy';
        pre.appendChild(button);

        // Wrap each line in a span for numbering
        const code = pre.querySelector('code');
        if (code && !code.dataset.linesWrapped) {
            const lines = code.innerText.split('\\n');
            const escapeHtml = text => text.replace(/[&<>"']/g, m => ({
                '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;'
            }[m] || m));
            code.innerHTML = lines.map(line =>
                '<span>' + (line === '' ? '&#8203;' : escapeHtml(line)) + '</span>'
            ).join('');
            code.dataset.linesWrapped = 'true';
        }

        // Copy to clipboard
        button.addEventListener('click', () => {
            if (!code) return;
            navigator.clipboard.writeText(code.innerText).then(() => {
                button.innerHTML = checkmarkSVG + ' Copied';
                setTimeout(() => button.innerHTML = clipboardSVG + ' Copy', 2000);
            });
        });
    });
});
JS;

    wp_add_inline_script('code-enhancer', $js);
});

Step 2 – How it works

  1. CSS styles the wrapper, line numbers, and copy button.
  2. JavaScript:
    • Wraps each <pre> in a container for alignment.
    • Adds a “Copy” button in the top right corner.
    • Wraps each line in <span> tags so we can style line numbers.
    • Handles copying text to the clipboard and showing a “Copied” confirmation.

Step 3 — Testing

  • Add a Gutenberg Code block with some multi-line code.
  • Try normal width, wide width, and full width.
  • Verify that line numbers align neatly and the button works on mobile and desktop.

Screenshots

Before

Default Gutenberg Code block

After

Enhanced Gutenberg Code block with line numnbers and copy button

Conclusion

With this snippet, your Gutenberg code blocks are now much more developer-friendly and user-friendly.
It’s lightweight, doesn’t require extra CSS files, and works in all alignment modes.

Resources used.

ChatGPT.com for producing the code which took a few hours to get it the way I wanted and for structuring and writing most of this tutorial.
Now where is my own creative writing in this process. Well I am exploring…I do not have the same ownership of my tutorials any longer and I am not sure how I feel about that….I go through and decide what to use or not.

Share the article:

Leave a Reply

Your email address will not be published. Required fields are marked *