navigator.clipboard is undefined in JavaScript issue [Fixed]

avatar
Borislav Hadzhiev

Last updated: Mar 7, 2024
6 min

banner

# Table of Contents

  1. navigator.clipboard is undefined in JavaScript issue
  2. Enable the "Insecure origins treated as secure" setting
  3. Providing a fallback for when navigator.clipboard is undefined
  4. Enable Clipboard access for your URL in the "Privacy and security" tab
  5. Common reasons for navigator.clipboard to be undefined

# navigator.clipboard is undefined in JavaScript issue [Fixed]

The navigator.clipboard object might be undefined because the Clipboard API is only available in secure contexts (HTTPS).

You can resolve the issue in multiple ways:

  1. Set up HTTPS when developing on localhost.
  2. Enable the Insecure origins treated as secure setting in Chrome.
  3. Provide a fallback using document.execCommand if navigator.clipboard is undefined.

As shown in this section of the MDN docs, the Clipboard API is only available in secure contexts (HTTPS).

If you try to use the API using the HTTP protocol, then navigator.clipboard will be undefined and you will likely get one of the following errors:

  1. Cannot read properties of undefined (reading 'writeText')
  2. Cannot read properties of undefined (reading 'readText')

You can use the window.isSecureContext property if you need to check if your code is run in a secure context.

The property returns true if the context is secure and false otherwise.

index.js
console.log(window.isSecureContext); if (window.isSecureContext) { console.log( 'The context is secure, can use navigator.clipboard', ); } else { console.log('The context is NOT secure'); }

# Enable the "Insecure origins treated as secure" setting

One way to get around the issue is to enable the "Insecure origins treated as secure" setting:

  1. Enter chrome://flags in your browser's address bar.

  2. Search for Insecure origins treated as secure.

  3. Enable the setting by clicking on the button to the right.

  4. Type the origins that you want to enable the setting for, e.g. http://localhost:3000 or http://example.com.

enable insecure origins treated as secure

  • Multiple origins can be specified as a comma-separated list.
origins
http://localhost:3000,http://example.com
  • Origins must have their protocol specified (http:// or https://).
  1. NOTE: After you enable the setting, you have to relaunch Chrome for the changes to take effect.

relaunch chrome for changes to take effect

Suppose you have the following HTML.

index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> </head> <body> <div class="box">bobbyhadz.com</div> <div id="content">Some text to copy here</div> <button id="btn">Copy text</button> <script src="index.js"></script> </body> </html>

Here is the code for the index.js file.

index.js
const button = document.getElementById('btn'); const contentDiv = document.getElementById('content'); button.addEventListener('click', async () => { console.log(navigator.clipboard); await navigator.clipboard.writeText(contentDiv.innerText); console.log('Copied!') });

navigator clipboard is no longer undefined

The code sample works after making the change and enables me to copy the text without using HTTPS.

Note that the writeText() method returns a Promise, therefore you either have to await it or use the .then() syntax.

Here is an example that uses the then() syntax instead.

index.js
const button = document.getElementById('btn'); const contentDiv = document.getElementById('content'); button.addEventListener('click', () => { console.log(navigator.clipboard); navigator.clipboard .writeText(contentDiv.innerText) .then(() => { console.log('The text has been copied'); }); });

The callback function we passed to .then() gets called after the Promise has been resolved.

# Providing a fallback for when navigator.clipboard is undefined

An alternative approach to resolve the issue with navigator.clipboard being undefined is to provide a fallback using document.execCommand.

Here is the HTML for the example.

index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> </head> <body> <div class="box">bobbyhadz.com</div> <button id="btn">Copy text</button> <script src="index.js"></script> </body> </html>

And here is the related JavaScript code.

index.js
const button = document.getElementById('btn'); button.addEventListener('click', async () => { try { await copyToClipboard('The text you want to copy ๐Ÿ“'); console.log('Copied!'); } catch (err) { console.log(err); } }); /** * Universal copyToClipboard function */ async function copyToClipboard(textToCopy) { if (navigator.clipboard && window.isSecureContext) { await navigator.clipboard.writeText(textToCopy); } else { const textarea = document.createElement('textarea'); textarea.value = textToCopy; // Move the textarea outside the viewport to make it invisible textarea.style.position = 'absolute'; textarea.style.left = '-99999999px'; document.body.prepend(textarea); // highlight the content of the textarea element textarea.select(); try { document.execCommand('copy'); } catch (err) { console.log(err); } finally { textarea.remove(); } } }

The copyToClipboard() function takes the text you want to copy as a parameter and copies the text to the user's clipboard.

The function is async, so you have to await it.

The function checks if the context is secure using the window.isSecureContext property.

index.js
if (navigator.clipboard && window.isSecureContext) { await navigator.clipboard.writeText(textToCopy); }

This way you won't get the "Cannot read properties of undefined (reading 'writeText')" error even if the navigator.clipboard property is undefined.

If the context is secure, the window.isSecureContext property returns true and the if block runs.

Inside the if block, we use the navigator.clipboard.writeText() method to copy the specified text to the user's clipboard.

If the context is not secure, the else block runs.

index.js
else { const textarea = document.createElement('textarea'); textarea.value = textToCopy; // Move the textarea outside the viewport to make it invisible textarea.style.position = 'absolute'; textarea.style.left = '-99999999px'; document.body.prepend(textarea); // highlight the content of the textarea element textarea.select(); try { document.execCommand('copy'); } catch (err) { console.log(err); } finally { textarea.remove(); } }

Inside the else block, we create a textarea element to use an old trick.

  1. Set the value of the textarea element to the text we want to copy.
  2. Move the textarea outside the viewport to make it invisible.
  3. Use the select() method to highlight the text in the textarea element.

Once the text is highlighted, we use the document.execCommand() method to copy the selected text.

index.js
try { document.execCommand('copy'); } catch (err) { console.log(err); } finally { textarea.remove(); }

Even though the document.execCommand method is deprecated, it is still widely supported by browsers as shown in this table.

The copyToClipboard() function:

  1. Uses the navigator.clipboard API if it is available in the environment.
  2. Uses the document.execCommand method as a fallback if navigator.clipboard is not available.

This way the text is copied to the user's clipboard regardless if the context is secure (HTTPS).

To use the copyToClipboard function, pass it the text you want to copy to the user's clipboard and await the Promise.

index.js
const button = document.getElementById('btn'); button.addEventListener('click', async () => { try { await copyToClipboard('The text you want to copy ๐Ÿ“'); console.log('Copied!'); } catch (err) { console.log(err); } });

Notice that we marked the callback function we passed to addEventListener as async to be able to use the await keyword.

We await the Promise that the copyToClipboard function returns.

# Enable Clipboard access for your URL in the "Privacy and security" tab

You can also enable clipboard access for the specific URL in the "Privacy and security" tab in Chrome:

  1. Clip on the three dots icon in the top right corner in Chrome and select Settings.

click three dots settings

  1. Click on Privacy and Security in the left sidebar and select Site settings.

click privacy and security site settings

  1. Click on the specific site for which you want to enable clipboard access if you see it under Recent activity.

If you don't see the site under Recent activity, click on View permissions and data stored across sites and select the specific site.

click on specific site

  1. Scroll down to the Clipboard setting and set it to Allow from the dropdown menu.

set clipboard to allow

After you allow clipboard access to the site, you should be able to use the navigator.clipboard API.

# Common reasons for navigator.clipboard to be undefined

The navigator.clipboard property might be undefined and cause the "Cannot read properties of undefined (reading 'writeText')" error for multiple reasons:

  1. Using the navigator.clipboard API in an insecure context (HTTP, and not HTTPS).
  2. Using a very old browser in which the navigator.clipboard API is not supported.
  3. Trying to use the navigator.clipboard API when the user hasn't focused the tab in the browser. The browser tab has to have focus for you to be able to use the navigator.clipboard API.
  4. The user might not have granted permissions for websites to use the navigator.clipboard API.
  5. Forgetting to await the Promise that is returned from the navigator.clipboard.writeText() method.

# Additional Resources

You can learn more about the related topics by checking out the following tutorials:

I wrote a book in which I share everything I know about how to become a better, more efficient programmer.
book cover
You can use the search field on my Home Page to filter through all of my articles.

Copyright ยฉ 2024 Borislav Hadzhiev