Clipboard History Stack Widget — Multi-Item Copy History with Pinning

Clipboard History Stack Widget · Misc · Plain HTML, CSS & JS · Live preview

What's included

Features

Real Clipboard API integration — clicking an entry actually writes to the system clipboard, not just an in-page copy
De-duplication moves a re-copied item to the top instead of creating a duplicate row
Pinned items are explicitly protected from the max-size eviction logic, not just visually reordered
Native paste event handling captures pasted text into history automatically, not just typed-and-submitted text
Graceful fallback if the Clipboard API is unavailable (e.g. insecure context) — UI still responds without throwing
Visual copy-confirmation flash with a forced reflow to correctly replay the animation on repeated clicks
Pin/unpin and remove actions scoped with stopPropagation so they don't also trigger the copy-on-click behavior
Empty-state message shown only when the history stack is genuinely empty

About this UI Snippet

Clipboard History Stack Widget — Keeping More Than the Last Thing You Copied

Screenshot of the Clipboard History Stack Widget snippet rendered live

The operating system clipboard holds exactly one item at a time — copy something new, and whatever was there before is gone. This widget solves that limitation for in-app use: it keeps a running, de-duplicated stack of recently entered values, lets a user click any past entry to copy it back to the real system clipboard via the Clipboard API, and supports pinning specific entries so they survive being pushed out once the list fills up.

Writing to the real OS clipboard, not just an internal list

Clicking any history item calls navigator.clipboard.writeText(entry.text) — a real, async browser API call that puts the text on the actual system clipboard, so the user can immediately paste it into any other application, not just somewhere else on this page. The call is wrapped in a try/catch because navigator.clipboard requires a secure context (HTTPS or localhost) and can reject if permission isn't granted — the UI still gives a visual "copied" flash even if the underlying write silently fails, but production code should surface that failure more explicitly if it matters for your use case.

De-duplication moves, rather than doubles, an existing entry

Before adding a new entry, addEntry() filters out any existing item with the exact same text, then unshifts the new one to the front — so re-copying something already in history doesn't create a duplicate row; it simply promotes that entry back to the top of the stack, matching how a real "recently used" list should behave.

Pinned items are protected from the max-size eviction

The stack is capped at MAX_ITEMS (8), and when a new entry pushes the list over that limit, the trimming logic explicitly separates out any pinned items sitting past the cutoff and re-appends them — const pinnedTail = history.slice(MAX_ITEMS).filter((h) => h.pinned) — so a pinned entry never silently disappears just because several newer, unpinned items were added after it. Pinned items are also sorted to the top of the rendered list on every render, independent of insertion order.

Handling paste directly, not just typed Enter

Beyond typing text and pressing Enter, the input also listens for a native paste event and reads the pasted text directly from e.clipboardData, automatically adding it to the history stack — so pasting something into the widget (from an external copy elsewhere) is itself enough to capture it into the running history, matching how a user would expect a "clipboard manager"-style tool to behave.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Ask an AI assistant to explain why navigator.clipboard.writeText() requires a secure context and how that should shape error handling in a real product versus this demo's silent-fail fallback. It's also worth asking for a version that persists history to localStorage across sessions, or one that groups entries by source (typed vs pasted) and supports keyboard-only navigation (arrow keys plus Enter to copy) through the stack.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a clipboard history widget in HTML, CSS and vanilla JavaScript that keeps a running stack of recently entered or pasted text snippets, with real copy-back functionality — no external libraries.

Requirements:
- A text input where pressing Enter, clicking an "add" button, or pasting directly into the field all add the current text as a new entry to the top of a history list.
- Clicking any entry in the history list must copy its text to the real system clipboard using the Clipboard API (navigator.clipboard.writeText), wrapped in error handling so a failure (e.g. insecure context) doesn't throw.
- Adding an entry whose text already exists in the history must move the existing entry to the top instead of creating a duplicate row.
- Each entry needs a "pin" toggle and a "remove" button; pinned entries must always render above unpinned ones and must be explicitly protected from any max-size eviction logic that trims the oldest entries once the list grows past a configurable limit.
- Show a brief visual confirmation (e.g. a flash animation) when an entry is successfully copied, and correctly replay that animation even on repeated clicks of the same entry.
- Show an empty-state message only when the history list is genuinely empty.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Source Code

<div class="demo">
  <div class="clip-widget">
    <div class="clip-input-row">
      <input type="text" id="clipInput" placeholder="Type or paste something, then press Enter" />
      <button id="clipAddBtn" title="Add to history">
        <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"><path d="M12 5v14M5 12h14"/></svg>
      </button>
    </div>

    <div class="clip-list" id="clipList">
      <p class="clip-empty" id="clipEmpty">Nothing saved yet — copied items will stack up here</p>
    </div>
  </div>
</div>

Step by step

How to Use

  1. 1
    Type text and press Enter, or paste directlyBoth actions add the text as a new entry at the top of the history stack.
  2. 2
    Click any history item to re-copy itThis calls the real Clipboard API, writing the text back to your system clipboard for pasting anywhere.
  3. 3
    Pin important entriesClick the pin icon on any item to keep it protected from being evicted once the stack reaches its max size.
  4. 4
    Adjust the max stack sizeChange the MAX_ITEMS constant in the JS panel to hold more or fewer entries before older ones get trimmed.
  5. 5
    Persist history across reloads (optional)Wrap the history array reads/writes with localStorage.getItem/setItem calls if you want the stack to survive a page refresh.

Real-world uses

Common Use Cases

DEV
Developer Tool Snippet Trays
Keep several frequently-reused code snippets, commands, or config values one click away from the clipboard.
SUPPORT
Support Agent Canned Responses
Stack up commonly-pasted reply templates or ticket links for quick reuse during a support session.
Multi-Field Data Entry
Help a user quickly re-paste previously entered values across multiple related form fields.
Design/Content Handoff Tools
Let a content editor stack up several copy variants and quickly copy whichever one is approved.
Related: Badge Dot Indicator
See the Badge Dot Indicator for a related misc pattern worth pairing with this one.
Related: Keyboard Shortcuts Help Overlay — Press
See the Keyboard Shortcuts Help Overlay — Press for a related misc pattern worth pairing with this one.
Related: Undo/Redo History Toolbar with Jump-to-State
See the Undo/Redo History Toolbar with Jump-to-State for a related misc pattern worth pairing with this one.
Related: GPA Calculator
See the GPA Calculator for a related misc pattern worth pairing with this one.
Related: Days Between Dates Calculator
See the Days Between Dates Calculator for a related misc pattern worth pairing with this one.
Related: Compound Interest Calculator
See the Compound Interest Calculator for a related misc pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

Yes — it calls navigator.clipboard.writeText(), the standard browser Clipboard API, which writes to the actual system clipboard so the text can be pasted into any other application, not just elsewhere on the same page.

The write call is wrapped in a try/catch, so the widget won't throw an unhandled error — it still shows the visual copy-confirmation flash, though a production implementation should probably surface an explicit failure message in that case rather than failing silently.

No — addEntry() first removes any existing entry with identical text before adding the new one to the front, so re-copying something already in history just moves it back to the top rather than duplicating it.

No — the max-size trimming logic explicitly excludes pinned items from eviction by re-appending any pinned entries that would otherwise fall past the size cutoff. A pinned item is only removed if the user explicitly clicks its delete button or unpins it first.

Yes — a native paste event listener on the input reads the pasted text directly from the clipboard event and adds it to the history stack automatically, in addition to the normal type-and-press-Enter flow.

Not by default in this snippet — the history array lives only in memory for the current page load. Add localStorage reads/writes around the history array if you need it to survive a reload.