Home
Web
How to add a code copy button to your Hugo template
How to add a code copy button to your Hugo template
On this page
Due to the way code snippets are rendered differently when
they contain line numbers, implementing a code copy button
will depend on whether or not you have configured your snippets to
contain line numbers.
See the comments in the javascript code below to determine
which version you’ll need to use for your site.
In your theme’s assets/js
folder, create code-copy-button.js
:
function createCopyButton ( highlightDiv ) {
if ( highlightDiv . classList . contains ( "nocopybutton" )) {
return
}
const wrapper = document . createElement ( "div" );
wrapper . className = "highlight-wrapper" ;
highlightDiv . parentNode . insertBefore ( wrapper , highlightDiv );
const button = document . createElement ( "button" );
button . className = "copy-code-button" ;
button . type = "button" ;
button . innerText = "Copy" ;
button . addEventListener ( "click" , () => copyCodeToClipboard ( button , highlightDiv ));
wrapper . appendChild ( button );
wrapper . appendChild ( highlightDiv );
}
document . querySelectorAll ( ".highlight" ). forEach (( highlightDiv ) => createCopyButton ( highlightDiv ));
async function copyCodeToClipboard ( button , highlightDiv ) {
// With line numbers:
const codeToCopy = highlightDiv . querySelector ( "div > table > tbody > tr > td:nth-child(2) > pre > code" ). innerText . replaceAll ( '\n\n' , '\n' );
// Without line numbers:
//
try {
var result = await navigator . permissions . query ({ name : "clipboard-write" });
if ( result . state == "granted" || result . state == "prompt" ) {
await navigator . clipboard . writeText ( codeToCopy );
} else {
copyCodeBlockExecCommand ( codeToCopy , highlightDiv );
}
} catch ( _ ) {
copyCodeBlockExecCommand ( codeToCopy , highlightDiv );
} finally {
button . blur ();
button . innerText = "✔️" ;
setTimeout ( function () {
button . innerText = "Copy" ;
}, 2000 ); }
}
function copyCodeBlockExecCommand ( codeToCopy , highlightDiv ) {
const textArea = document . createElement ( "textArea" );
textArea . contentEditable = "true" ;
textArea . readOnly = "false" ;
textArea . className = "copyable-text-area" ;
textArea . value = codeToCopy ;
highlightDiv . insertBefore ( textArea , highlightDiv . firstChild );
const range = document . createRange ();
range . selectNodeContents ( textArea );
const sel = window . getSelection ();
sel . removeAllRanges ();
sel . addRange ( range );
textArea . setSelectionRange ( 0 , 999999 );
document . execCommand ( "copy" );
highlightDiv . removeChild ( textArea );
}
In your theme’s footer, often found in themes/<your_theme>/layouts/partials/
somewhere, add the following near the </body>
tag:
{{ if ( findRE "<pre" .Content 1 ) }}
{{ $jsCopy := resources .Get "js/code-copy-button.js" | minify }}
< script src = " {{ $jsCopy .RelPermalink }} " ></ script >
{{ end }}
Here’s what I’m using:
. highlight-wrapper {
display : block ;
/* This is important! Without this, your code copy
button will scroll along the x-axis in cases where code
lines cause overflow. This keeps the code copy button
stuck to the top right of the snippet: */
position : relative ;
background-color : #fafafa ;
margin-bottom : 1.2 rem ;
}
. highlight-wrapper . lntd pre {
padding : 0 ;
}
. chroma . lntd pre {
border : none ;
}
. chroma . lntd : first-child {
padding : 7 px 7 px 7 px 10 px ;
margin : 0 ;
}
. chroma . lntd : last-child {
padding : 7 px 10 px 7 px 7 px ;
margin : 0 ;
}
. highlight {
position : relative ;
z-index : 0 ;
padding : 0 ;
border-radius : 4 px ;
overflow-x : auto ;
}
. highlight > . chroma {
position : static ;
z-index : 1 ;
border-radius : 0 0 4 px 4 px ;
padding : 10 px ;
}
. copy-code-button {
position : absolute ;
z-index : 2 ;
right : 0 ;
top : 0 px ;
font-size : 0.75 rem ;
font-weight : 700 ;
line-height : 14 px ;
letter-spacing : 0.5 px ;
width : 55 px ;
color : #232326 ;
background-color : #fafafa ;
border : 1.25 px solid #232326 ;
border-bottom-left-radius : 4 px ;
white-space : nowrap ;
padding : 6 px ;
margin : 0 0 0 1 px ;
cursor : pointer ;
opacity : 0.45 ;
}
. copy-code-button : hover ,
. copy-code-button : focus ,
. copy-code-button : active ,
. copy-code-button : active : hover {
color : #222225 ;
background-color : #b3b3b3 ;
opacity : 1 ;
}
. copyable-text-area {
position : absolute ;
height : 0 ;
z-index : -1 ;
opacity : 0.01 ;
}
The demo is the “Copy” button you see in the code snippets
on my site. Copy this or modify it to your liking.
Hugo configuration#
Here’s my Hugo configuration settings pertaining to code
snippets:
[ markup . highlight ]
codeFences = true
guessSyntax = false
hl_Lines = ""
lineNoStart = 1
lineNos = true
lineNumbersInTable = true
tabWidth = 4
noClasses = false
noHl = true
style = 'monokailight'
[ markup ]
[ markup . goldmark ]
[ markup . goldmark . renderer ]
unsafe = true