diff --git a/docs_theme/css/copy-button.css b/docs_theme/css/copy-button.css
new file mode 100644
index 0000000000..cbd38bc3f9
--- /dev/null
+++ b/docs_theme/css/copy-button.css
@@ -0,0 +1,20 @@
+.copy-block-button {
+ position: absolute;
+ top: 6px;
+ right: 6px;
+ font-size: 12px;
+ padding: 2px 6px;
+ cursor: pointer;
+
+ background: #f7f7f9;
+ border: 1px solid #e1e1e8;
+ border-radius: 3px;
+
+ color: #dc322f !important;
+
+ box-shadow: 0 1px 1px rgba(0,0,0,0.04);
+}
+
+.copy-block-button span {
+ color: #dc322f !important;
+}
diff --git a/docs_theme/js/copy-button.js b/docs_theme/js/copy-button.js
new file mode 100644
index 0000000000..15fc118b2d
--- /dev/null
+++ b/docs_theme/js/copy-button.js
@@ -0,0 +1,19 @@
+document.addEventListener("DOMContentLoaded", function () {
+ document.querySelectorAll("pre > code").forEach(function (codeBlock) {
+ const button = document.createElement("button");
+ button.className = "copy-block-button";
+ button.type = "button";
+ button.textContent = "Copy";
+
+ button.addEventListener("click", function () {
+ navigator.clipboard.writeText(codeBlock.textContent).then(function () {
+ button.textContent = "Copied!";
+ setTimeout(() => button.textContent = "Copy", 1200);
+ });
+ });
+
+ const pre = codeBlock.parentNode;
+ pre.style.position = "relative";
+ pre.appendChild(button);
+ });
+});
diff --git a/docs_theme/main.html b/docs_theme/main.html
index e373095955..a584f95393 100644
--- a/docs_theme/main.html
+++ b/docs_theme/main.html
@@ -16,6 +16,9 @@
+ {% for path in config.extra_css %}
+
+ {% endfor %}