Tutorials
Last updated on:
Feb 12, 2025

How to add copy-to-clipboard functionality to a button in Webflow

BRIX Templates Logo
Author
BRIX Templates
How to add copy-to-clipboard functionality to a button in Webflow
Table of contents

Adding copy-to-clipboard functionality to your Webflow site can significantly enhance user experience, especially when dealing with shareable content, promotional codes, or technical snippets. This guide will walk you through implementing a robust, attribute-based solution that's both flexible and user-friendly.

Webflow copy clipboard button

Why you should add copy-to-clipboard functionality to your Webflow site

Here are some practical applications where copy-to-clipboard functionality can enhance your Webflow site:

  • Contact information: Allow visitors to easily copy phone numbers, email addresses, or physical addresses from your contact page, making it simpler for them to save or share your business information.
  • Code snippet sharing: Enable developers to quickly copy code examples, configuration snippets, or API keys from your documentation. For instance, adding a copy button next to installation commands or integration codes streamlines the implementation process.
  • Coupon copy buttons: Make promotional codes instantly copyable, reducing friction in your checkout process and improving conversion rates. Instead of users manually selecting and copying "SUMMER2025", they can click once and proceed to checkout.
  • Social media handles: Add copy buttons next to your social media handles or branded hashtags, encouraging social sharing and making it easier for users to find and mention your brand.
  • Product specifications: Enable quick copying of product SKUs, model numbers, or technical specifications, helping customers share or reference specific products accurately.

How the copy-to-clipboard attributes work in Webflow

Our solution uses a set of carefully designed attributes that provide control over copying behavior. These attributes let you add copy buttons anywhere on your Webflow site, control what text gets copied, and customize how users see that the copy worked. Everything works directly in Webflow without needing any external tools.

  1. brix-copy-clipboard="true" The primary attribute that activates copying functionality on any element
  2. brix-copy-clipboard-text="..." The secondary attribute that specifies the exact text to copy
  3. brix-copy-clipboard-successtext="Copied!" Optional attribute for customizing the success message
  4. brix-copy-clipboard-successclass="myClass" Optional attribute for adding a temporary success state class
  5. brix-copy-clipboard-revertms="2000" Optional attribute for controlling how long the success state persists

The script also includes built-in accessibility features, with ARIA live regions for screen reader announcements and keyboard accessibility support.

How to add the copy-to-clipboard script to your Webflow project

Let's implement the copy-to-clipboard functionality step by step:

  1. Navigate to your Webflow project settings
  2. Access the Custom Code section
  3. Locate the "Before </body> tag" field
  4. Insert the following code:
Webflow copy button code tutorial
<script>
/*!
 * BRIX Templates Copy to Clipboard Attribute for Webflow
 * ----------------------------------------------------------------------------
 * Automatically enables copy-to-clipboard functionality on elements marked with
 * the appropriate attributes. This script supports two main copying modes:
 * 
 * 1. Custom Text Copying:
 *    - Add [brix-copy-clipboard="true"] to enable copying
 *    - Add [brix-copy-clipboard-text="Your text"] to specify what to copy
 * 
 * 2. URL Copying:
 *    - Add [brix-copy-clipboard="true"] to enable copying
 *    - Add [brix-copy-clipboard-url="true"] to copy the current page URL
 * 
 * Optional Attributes for Both Modes:
 *    - [brix-copy-clipboard-successtext="Copied!"] 
 *      Shows a temporary success message after copying
 * 
 *    - [brix-copy-clipboard-successclass="is-copied"]
 *      Adds a temporary class for styling feedback
 * 
 *    - [brix-copy-clipboard-revertms="2000"]
 *      Controls how long success state lasts (default: 1500ms)
 * 
 * The script includes:
 *    - Automatic success feedback
 *    - Screen reader support via ARIA
 *    - Keyboard accessibility
 *    - Detailed console logging for debugging
 * 
 * Version: 1.0.1
 * Author: BRIX Templates
 * 
 */

(function() {
  'use strict';

  //--------------------------------------------------------------------------
  // 1) Configuration & Constants
  //--------------------------------------------------------------------------
  const SCRIPT_VERSION = '1.0.1';
  const DEFAULT_REVERT_MS = 1500;
  const ARIA_ANNOUNCE_DEFAULT = 'Copied to clipboard';

  let ariaLiveContainer = null;

  //--------------------------------------------------------------------------
  // 2) Initialize on DOM Ready
  //--------------------------------------------------------------------------
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', initBrixClipboard);
  } else {
    initBrixClipboard();
  }

  //--------------------------------------------------------------------------
  // 3) Main Initialization
  //--------------------------------------------------------------------------
  function initBrixClipboard() {
    // 3.1) Find all elements with brix-copy-clipboard="true"
    const triggers = document.querySelectorAll('[brix-copy-clipboard="true"]');
    const total = triggers.length;
    if (total === 0) return;

    // 3.2) Create a hidden ARIA-live region for screenreader announcements
    ariaLiveContainer = createAriaLiveRegion();

    // 3.3) Console logging: Intro summary
    console.log('------------------------');
    console.log(
      '%c[BRIX Templates Copy to Clipboard for Webflow]%c v' + SCRIPT_VERSION +
      ' Activated. Found %c' + total + '%c element' + (total !== 1 ? 's' : '') + ' to manage:',
      'font-weight:bold;',
      'font-weight:normal;',
      'font-weight:bold;',
      'font-weight:normal;'
    );
    console.log('------------------------');

    // 3.4) For each trigger, parse attributes & log
    triggers.forEach((elem, index) => {
      const info = parseAttributes(elem, index);

      // (A) Per-element line #1
      console.log(
        '- Element #%c' + (index + 1) + '%c: %c' + info.identifier,
        'font-weight:bold;',
        'font-weight:normal;',
        'font-weight:bold;'
      );

      // (B) Per-element line #2 - attributes summary
      console.log(
        '    copyingURL=%c' + info.isUrl + '%c, text="%c' + info.textAttr + '%c", successText="%c' +
        info.successText + '%c", successClass="%c' + info.successClass + '%c", revertMs=%c' + info.revertMs,
        'font-weight:bold;',
        'font-weight:normal;',
        'font-weight:bold;',
        'font-weight:normal;',
        'font-weight:bold;',
        'font-weight:normal;',
        'font-weight:bold;',
        'font-weight:normal;',
        'font-weight:bold;',
        'font-weight:normal;'
      );

      // (C) Warnings & conflicts (same indentation)
      if (info.urlAndTextConflict) {
        console.warn(
          '    "url" specified alongside "text=' + info.textAttr +
          '". "url" takes precedence, ignoring text.'
        );
      }
      if (info.invalidRevertMs) {
        console.warn(
          '    Invalid revertMs="' + info.revertMsStr + '". Using default ' + DEFAULT_REVERT_MS + '.'
        );
      }

      // (D) Attach click listener
      elem.addEventListener('click', (evt) => {
        evt.preventDefault();
        handleCopyAction(elem, index);
      });
    });

    // 3.5) Final line after listing triggers
    console.log('------------------------');
    console.log(
      '%c[BRIX Templates Copy to Clipboard for Webflow]%c All copy triggers have been initialized successfully.',
      'font-weight:bold;',
      'font-weight:normal;'
    );
    console.log('------------------------');
  }

  //--------------------------------------------------------------------------
  // 4) Parse Attributes (for logging)
  //--------------------------------------------------------------------------
  // This function only logs the data; the actual fallback or overrides
  // still happen in handleCopyAction for the real copy process.
  function parseAttributes(elem, index) {
    const identifier = getElementIdentifier(elem, index);

    // Gather
    const rawUrl = (elem.getAttribute('brix-copy-clipboard-url') || '').toLowerCase();
    const isUrl = (rawUrl === 'true');
    const textAttr = elem.getAttribute('brix-copy-clipboard-text') || '';
    const successText = elem.getAttribute('brix-copy-clipboard-successtext') || '';
    const successClass = elem.getAttribute('brix-copy-clipboard-successclass') || '';
    const revertMsStr = elem.getAttribute('brix-copy-clipboard-revertms');
    let revertMsNum = parseInt(revertMsStr, 10);
    let invalidRevertMs = false;

    if (isNaN(revertMsNum) || revertMsNum < 0) {
      invalidRevertMs = !!revertMsStr; // only warn if the user tried to set a revertMs
      revertMsNum = (invalidRevertMs) ? 0 : DEFAULT_REVERT_MS;
      // We'll do the real fallback in handleCopyAction, but log here for clarity.
    }

    // Conflicts
    const urlAndTextConflict = (isUrl && textAttr.trim().length > 0);

    return {
      identifier,
      isUrl,
      textAttr,
      successText,
      successClass,
      revertMs: revertMsNum,
      revertMsStr,
      invalidRevertMs,
      urlAndTextConflict
    };
  }

  //--------------------------------------------------------------------------
  // 5) Build a readable identifier for the element
  //--------------------------------------------------------------------------
  function getElementIdentifier(elem, index) {
    if (elem.id) {
      return 'ID: "' + elem.id + '"';
    }
    else if (elem.classList && elem.classList.length > 0) {
      return 'Classes: "' + Array.from(elem.classList).join(' ') + '"';
    }
    else {
      return 'element-' + (index + 1);
    }
  }

  //--------------------------------------------------------------------------
  // 6) Handle the actual copy action (on click)
  //--------------------------------------------------------------------------
  function handleCopyAction(element, index) {
    const originalText = element.textContent;

    // Collect attributes again for actual logic/fallback
    const isUrl = (element.getAttribute('brix-copy-clipboard-url') || '').toLowerCase() === 'true';
    const textAttr = element.getAttribute('brix-copy-clipboard-text');
    const successText = element.getAttribute('brix-copy-clipboard-successtext') || '';
    const successClass = element.getAttribute('brix-copy-clipboard-successclass') || '';
    let revertMs = parseInt(element.getAttribute('brix-copy-clipboard-revertms'), 10);

    if (isNaN(revertMs) || revertMs < 0) {
      revertMs = DEFAULT_REVERT_MS;
      // We no longer console.warn here, because we already did it in initBrixClipboard logs
    }

    // Decide what text to copy
    let textToCopy = '';
    if (isUrl) {
      textToCopy = window.location.href;
    } else if (textAttr) {
      textToCopy = textAttr;
    } else {
      textToCopy = originalText.trim();
    }

    doCopyText(textToCopy);

    // Provide success feedback
    const finalSuccessText = successText || ARIA_ANNOUNCE_DEFAULT;

    if (successText) {
      element.textContent = successText;
    }
    if (successClass) {
      element.classList.add(successClass);
    }

    announceAriaLive(finalSuccessText);

    // Revert after revertMs
    setTimeout(() => {
      element.textContent = originalText;
      if (successClass) {
        element.classList.remove(successClass);
      }
    }, revertMs);
  }

  //--------------------------------------------------------------------------
  // 7) Copy Logic (Modern + Fallback)
  //--------------------------------------------------------------------------
  function doCopyText(str) {
    // Attempt modern async clipboard API first
    if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
      navigator.clipboard.writeText(str).catch((err) => {
        // Already in fallback, no bracket prefix here
        console.warn('    Clipboard API error:', err);
        fallbackCopyText(str);
      });
    }
    // Attempt document.execCommand('copy') if available
    else if (document.queryCommandSupported && document.queryCommandSupported('copy')) {
      fallbackCopyText(str);
    }
    else {
      console.warn('    No browser copy support found. Copy may fail.');
    }
  }

  // Fallback for older browsers
  function fallbackCopyText(str) {
    const temp = document.createElement('textarea');
    temp.value = str;
    temp.setAttribute('readonly', '');
    temp.style.position = 'absolute';
    temp.style.left = '-9999px';
    document.body.appendChild(temp);

    temp.select();
    try {
      document.execCommand('copy');
    } catch (err) {
      console.warn('    execCommand("copy") failed:', err);
    }
    document.body.removeChild(temp);
  }

  //--------------------------------------------------------------------------
  // 8) Create ARIA Live Region for Screenreaders
  //--------------------------------------------------------------------------
  function createAriaLiveRegion() {
    let region = document.getElementById('brix-copy-aria-live');
    if (region) return region;

    region = document.createElement('div');
    region.id = 'brix-copy-aria-live';
    region.setAttribute('aria-live', 'polite');
    region.setAttribute('aria-atomic', 'true');
    region.style.position = 'absolute';
    region.style.left = '-9999px';
    region.style.width = '1px';
    region.style.height = '1px';
    region.style.overflow = 'hidden';
    document.body.appendChild(region);

    return region;
  }

  //--------------------------------------------------------------------------
  // 9) Announce to Screenreaders
  //--------------------------------------------------------------------------
  function announceAriaLive(message) {
    if (!ariaLiveContainer) return;
    ariaLiveContainer.textContent = '';
    setTimeout(() => {
      ariaLiveContainer.textContent = message;
    }, 50);
  }

})();
</script>

If you only need the copy-to-clipboard functionality on specific pages (like just your pricing page or documentation), you can add the script to individual page settings instead of the global project settings. Here's how:

  1. Navigate to the specific page in your Webflow Designer
  2. Click the settings gear icon (⚙️) in the top right of your page
  3. Select "Page Settings"
  4. Scroll to find the "Custom Code" section
  5. Add the script to the "Before </body> tag" field
Add copy to clipboard script Webflow

This approach can help maintain cleaner code and faster load times on pages that don't need the copy functionality.

How to configure copy-to-clipboard elements in your Webflow site

After adding the script, here's how to set up a copy button with all available options:

  1. Select your target button or link in the Webflow Designer
  2. Open the element's Settings panel (gear icon)
  3. Locate the Custom Attributes section
  4. Add the following attributes:
Copy button Webflow

Required attributes

brix-copy-clipboard

  • Value: true
  • What it does: This is the main attribute that makes your element work as a copy button. Think of it as turning on the copy functionality for this specific button.

brix-copy-clipboard-text

  • Value: SUMMER2025 (or any text you want people to copy)
  • What it does: This determines exactly what text will be copied when someone clicks your button. Could be a discount code, phone number, address, code snippet, or any text you want to make easily copyable.

Optional attributes (Add these only if you need them)

brix-copy-clipboard-successtext

  • Value: Code copied! (or your custom message)
  • What it does: Shows a temporary message after someone copies the text (i.e. Code copied!), confirming that the copy worked.

brix-copy-clipboard-successclass

  • Value: copied-state (or your custom class name)
  • What it does: Allows you to temporarily change how the button looks after someone copies the text.

brix-copy-clipboard-revertms

  • Value: 2000 (milliseconds)
  • What it does: Controls how long to show the success message and styling before returning to normal.

This configuration creates a button that copies your specified text when clicked, shows a confirmation message, and can temporarily change its appearance to provide visual feedback.

How to test your copy-to-clipboard button implementation on Webflow

Before publishing, ensure your implementation works correctly:

  1. Test on your staging environment (webflow.io domain)
  2. Verify the basic copying functionality works with your specified text
  3. If you've added optional features, test them as well:
    • Success message appears (only if configured)
    • Success class applies (only if configured)
    • Element reverts correctly after your specified time
  4. Publish to your production domain after confirming everything works as expected

Final thoughts

Implementing copy-to-clipboard functionality in Webflow using custom attributes provides a powerful, flexible solution for enhancing user interaction with your content. Whether you're sharing promotional codes, technical snippets, or contact information, this implementation offers a seamless experience while maintaining clean, professional design standards.

Need help implementing more advanced copying features or exploring other custom Webflow solutions? Our experienced Webflow agency specializes in creating tailored functionality that enhances your site's user experience while maintaining performance and reliability. Feel free to reach out to discuss how we can help optimize your Webflow project.

BRIX Templates Logo
About BRIX Templates

At BRIX Templates we craft beautiful, modern and easy to use Webflow templates & UI Kits.

Explore our Webflow templates
Join the conversation
Join our monthly Webflow email newsletter!

Receive one monthly email newsletter with the best articles, resources, tutorials, and free cloneables from BRIX Templates!

Webflow Newsletter
Thanks for joining our Webflow email newsletter
Oops! Something went wrong while submitting the form.
BRIX Templates - Email Newsletter with Webflow ResourcesBRIX Templates - Email NewsletterBRIX Templates - Webflow Email Newsletter
How to add animated number counters to your Webflow site

How to add animated number counters to your Webflow site

Learn how to add animated number counters to your Webflow site. This step-by-step guide explains how to do it in 5 minutes or less.

Feb 19, 2025
How to add URL copy button functionality in Webflow

How to add URL copy button functionality in Webflow

Learn how to add a copy URL button to your Webflow site. Step-by-step guide for implementing easy page sharing without plugins or code.

Feb 19, 2025
How to geo-block visitors on your Webflow site using Cloudflare

How to geo-block visitors on your Webflow site using Cloudflare

Complete tutorial explaining how to prevent users from specific countries from accessing your Webflow website.

Feb 12, 2025