Programming & Development / April 6, 2025

How to Capture Screenshots of Individual List Items in React and Copy to Clipboard

React html2canvas screenshot clipboard API copy image capture DOM list item screenshot React tutorial JavaScript screenshot UI tools

Description:

In modern web applications, providing users with tools to interact visually with content—such as capturing screenshots of specific components—can greatly enhance user experience. A practical use case is enabling users to capture screenshots of individual <li> (list item) elements directly from a React component and automatically copy them to the clipboard.

This tutorial walks through a simple and efficient implementation using React, html2canvas, and the Clipboard API.

Why You Might Need This

Imagine a scenario where your app displays a list of notes, tasks, or posts. Allowing users to quickly take a visual snapshot of any specific item—without capturing the entire screen—can streamline workflows, aid in sharing, and enhance visual feedback.

Step-by-Step Implementation

1. Set Up Your Project

Make sure you're working in a React environment. First, install html2canvas:

bash

npm install html2canvas

2. Create a Reusable Component

You’ll use useRef to store references to each <li> element and a button in each list item to trigger the screenshot.

Here’s a full example:

jsx

import React, { useRef } from 'react';
import html2canvas from 'html2canvas';

const items = [
  'First item',
  'Second item',
  'Third item',
  'Fourth item'
];

const ScreenshotList = () => {
  const itemRefs = useRef([]);

  const handleScreenshot = async (index) => {
    const target = itemRefs.current[index];
    if (!target) return;

    try {
      const canvas = await html2canvas(target);
      canvas.toBlob(async (blob) => {
        if (blob) {
          try {
            await navigator.clipboard.write([
              new ClipboardItem({ 'image/png': blob })
            ]);
            alert('Screenshot copied to clipboard!');
          } catch (err) {
            console.error('Clipboard write failed:', err);
          }
        }
      });
    } catch (err) {
      console.error('Screenshot capture failed:', err);
    }
  };

  return (
    <ul>
      {items.map((text, index) => (
        <li
          key={index}
          ref={(el) => (itemRefs.current[index] = el)}
          style={{
            marginBottom: '10px',
            padding: '10px',
            border: '1px solid #ccc',
            position: 'relative'
          }}
        >
          {text}
          <button
            onClick={() => handleScreenshot(index)}
            style={{ marginLeft: '10px' }}
          >
            Copy Screenshot
          </button>
        </li>
      ))}
    </ul>
  );
};

export default ScreenshotList;

How It Works

  • html2canvas takes the <li> DOM node and renders it to a canvas.
  • The canvas is converted to a Blob (PNG image).
  • The Clipboard API (navigator.clipboard.write) copies the image to the clipboard.
  • Users can paste it directly into an email, document, or chat.

Browser Support

  • Clipboard API (image support): Mostly supported in modern Chromium-based browsers like Chrome and Edge.
  • Security: This feature only works over https:// or on localhost in development.

Use Cases

  • Task management apps (copy individual tasks)
  • Notes apps
  • Bug reporting tools
  • Social sharing of individual list items



Nice! Let’s enhance the component with:

  1. Modern button styles (with CSS-in-JS styling or classes).
  2. Two additional features for each <li>:
  • Copy screenshot to clipboard (image).
  • Download content of <li> as .txt file.

Updated React Component:

jsx

import React, { useRef } from 'react';
import html2canvas from 'html2canvas';

const items = [
  'First item',
  'Second item',
  'Third item',
  'Fourth item'
];

const ScreenshotList = () => {
  const itemRefs = useRef([]);

  const handleScreenshot = async (index) => {
    const target = itemRefs.current[index];
    if (!target) return;

    try {
      const canvas = await html2canvas(target);
      canvas.toBlob(async (blob) => {
        if (blob) {
          try {
            await navigator.clipboard.write([
              new ClipboardItem({ 'image/png': blob })
            ]);
            alert('Screenshot copied to clipboard!');
          } catch (err) {
            console.error('Clipboard write failed:', err);
          }
        }
      });
    } catch (err) {
      console.error('Screenshot capture failed:', err);
    }
  };

  const handleDownloadText = (index) => {
    const li = itemRefs.current[index];
    if (!li) return;

    const text = li.innerText.replace(/Copy Screenshot|Download Text/g, '').trim();
    const blob = new Blob([text], { type: 'text/plain' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');

    a.href = url;
    a.download = `item-${index + 1}.txt`;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
  };

  const buttonStyle = {
    padding: '6px 10px',
    marginLeft: '8px',
    border: 'none',
    borderRadius: '4px',
    backgroundColor: '#007bff',
    color: '#fff',
    cursor: 'pointer',
    fontSize: '0.9rem'
  };

  const secondaryButtonStyle = {
    ...buttonStyle,
    backgroundColor: '#28a745'
  };

  return (
    <ul style={{ listStyleType: 'none', padding: 0 }}>
      {items.map((text, index) => (
        <li
          key={index}
          ref={(el) => (itemRefs.current[index] = el)}
          style={{
            marginBottom: '12px',
            padding: '12px',
            border: '1px solid #ccc',
            borderRadius: '6px',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'space-between'
          }}
        >
          <span>{text}</span>
          <div>
            <button
              onClick={() => handleScreenshot(index)}
              style={buttonStyle}
            >
              Copy Screenshot
            </button>
            <button
              onClick={() => handleDownloadText(index)}
              style={secondaryButtonStyle}
            >
              Download Text
            </button>
          </div>
        </li>
      ))}
    </ul>
  );
};

export default ScreenshotList;

What’s New:

  • Buttons styled to look modern (bootstrap-inspired look).
  • Download Text button creates and downloads a .txt file with the content of the <li>.
  • Clean layout using Flexbox.

Here's the complete, self-contained React component with:

  • Font Awesome icons
  • Modern styled buttons
  • Screenshot to clipboard
  • Download .txt feature
  • Clean layout

Final Code:

jsx

// ScreenshotList.js
import React, { useRef } from 'react';
import html2canvas from 'html2canvas';

const items = [
  'First item',
  'Second item',
  'Third item',
  'Fourth item'
];

const ScreenshotList = () => {
  const itemRefs = useRef([]);

  const handleScreenshot = async (index) => {
    const target = itemRefs.current[index];
    if (!target) return;

    try {
      const canvas = await html2canvas(target);
      canvas.toBlob(async (blob) => {
        if (blob) {
          try {
            await navigator.clipboard.write([
              new ClipboardItem({ 'image/png': blob })
            ]);
            alert('Screenshot copied to clipboard!');
          } catch (err) {
            console.error('Clipboard write failed:', err);
          }
        }
      });
    } catch (err) {
      console.error('Screenshot capture failed:', err);
    }
  };

  const handleDownloadText = (index) => {
    const li = itemRefs.current[index];
    if (!li) return;

    const text = li.innerText.replace(/Copy Screenshot|Download Text/g, '').trim();
    const blob = new Blob([text], { type: 'text/plain' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');

    a.href = url;
    a.download = `item-${index + 1}.txt`;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
  };

  const buttonStyle = {
    padding: '6px 10px',
    marginLeft: '8px',
    border: 'none',
    borderRadius: '4px',
    backgroundColor: '#007bff',
    color: '#fff',
    cursor: 'pointer',
    fontSize: '0.9rem',
    transition: 'background-color 0.2s'
  };

  const secondaryButtonStyle = {
    ...buttonStyle,
    backgroundColor: '#28a745'
  };

  return (
    <ul style={{ listStyleType: 'none', padding: 0 }}>
      {items.map((text, index) => (
        <li
          key={index}
          ref={(el) => (itemRefs.current[index] = el)}
          style={{
            marginBottom: '12px',
            padding: '12px',
            border: '1px solid #ccc',
            borderRadius: '6px',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'space-between'
          }}
        >
          <span>{text}</span>
          <div>
            <button
              onClick={() => handleScreenshot(index)}
              style={buttonStyle}
            >
              <i className="fas fa-copy" style={{ marginRight: '6px' }}></i>
              Copy Screenshot
            </button>
            <button
              onClick={() => handleDownloadText(index)}
              style={secondaryButtonStyle}
            >
              <i className="fas fa-download" style={{ marginRight: '6px' }}></i>
              Download Text
            </button>
          </div>
        </li>
      ))}
    </ul>
  );
};

export default ScreenshotList;

Add Font Awesome to public/index.html:

html

<!-- public/index.html -->
<head>
  <link
    rel="stylesheet"
    href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css"
    integrity="sha512-Fo3rlrZj/k7ujTTXRN+2H5QZ1oR3N6FhN0zJdLR0mVnxCyaDfCCQd7K8XTCaULjZ7R8uF8nE5Y0knc9VZfU2NA=="
    crossorigin="anonymous"
    referrerpolicy="no-referrer"
  />
</head>

✅ What You Get:

  • Responsive layout
  • Screenshot-to-clipboard per <li>
  • Download button to save .txt version
  • Font Awesome icons (copy, download)
  • Clear, modern UI



Comments

No comments yet

Add a new Comment

NUHMAN.COM

Information Technology website for Programming & Development, Web Design & UX/UI, Startups & Innovation, Gadgets & Consumer Tech, Cloud Computing & Enterprise Tech, Cybersecurity, Artificial Intelligence (AI) & Machine Learning (ML), Gaming Technology, Mobile Development, Tech News & Trends, Open Source & Linux, Data Science & Analytics

Categories

Tags

©{" "} Nuhmans.com . All Rights Reserved. Designed by{" "} HTML Codex