webdevpuneet.com

API Key Manager — Free HTML CSS JS Snippet

API Key Manager · Dashboards · Plain HTML, CSS & JS · Live preview

CategoryDashboards

Copy & paste

Source Code

<div class="wrap">
  <div class="header">
    <div>
      <h1 class="title">API Keys</h1>
      <p class="subtitle">Manage your API access tokens. Keep them secret — never share in public repos.</p>
    </div>
    <button class="create-btn" onclick="createKey()">
      <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
      New Key
    </button>
  </div>
  <div class="keys-list" id="keysList">
    <div class="key-row" data-key="sk_live_A4f9Kd2mNp8rTx1vYw3zQb5e">
      <div class="key-info">
        <div class="key-name">Production Key</div>
        <div class="key-meta"><span class="env-chip live">Live</span><span class="key-date">Created Jan 12, 2024</span><span class="key-last">Last used 2h ago</span></div>
      </div>
      <div class="key-val">
        <code class="key-code" data-shown="false">sk_live_••••••••••••••••••••••Qb5e</code>
        <button class="icon-btn" onclick="toggleKey(this)" title="Show key">
          <svg class="eye-off" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94"/><path d="M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19"/><line x1="1" y1="1" x2="23" y2="23"/></svg>
          <svg class="eye-on" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="display:none"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
        </button>
        <button class="icon-btn" onclick="copyKey(this)" title="Copy">
          <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
        </button>
      </div>
      <div class="key-actions">
        <div class="perms"><span class="perm">read</span><span class="perm">write</span></div>
        <button class="revoke-btn" onclick="revokeKey(this)">Revoke</button>
      </div>
    </div>
    <div class="key-row" data-key="sk_test_C7h1Lm4nPq6sUv9xZa2dRg8f">
      <div class="key-info">
        <div class="key-name">Test Environment</div>
        <div class="key-meta"><span class="env-chip test">Test</span><span class="key-date">Created Mar 4, 2024</span><span class="key-last">Last used 3d ago</span></div>
      </div>
      <div class="key-val">
        <code class="key-code" data-shown="false">sk_test_••••••••••••••••••••••Rg8f</code>
        <button class="icon-btn" onclick="toggleKey(this)" title="Show key">
          <svg class="eye-off" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94"/><path d="M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19"/><line x1="1" y1="1" x2="23" y2="23"/></svg>
          <svg class="eye-on" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="display:none"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
        </button>
        <button class="icon-btn" onclick="copyKey(this)" title="Copy">
          <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
        </button>
      </div>
      <div class="key-actions">
        <div class="perms"><span class="perm">read</span></div>
        <button class="revoke-btn" onclick="revokeKey(this)">Revoke</button>
      </div>
    </div>
    <div class="key-row" data-key="sk_live_Ef3jMo7qRt0wXc5yBn1pHi6k">
      <div class="key-info">
        <div class="key-name">CI/CD Pipeline</div>
        <div class="key-meta"><span class="env-chip live">Live</span><span class="key-date">Created May 1, 2024</span><span class="key-last">Last used 5m ago</span></div>
      </div>
      <div class="key-val">
        <code class="key-code" data-shown="false">sk_live_••••••••••••••••••••••Hi6k</code>
        <button class="icon-btn" onclick="toggleKey(this)" title="Show key">
          <svg class="eye-off" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94"/><path d="M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19"/><line x1="1" y1="1" x2="23" y2="23"/></svg>
          <svg class="eye-on" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="display:none"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
        </button>
        <button class="icon-btn" onclick="copyKey(this)" title="Copy">
          <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
        </button>
      </div>
      <div class="key-actions">
        <div class="perms"><span class="perm">read</span><span class="perm">write</span><span class="perm admin">admin</span></div>
        <button class="revoke-btn" onclick="revokeKey(this)">Revoke</button>
      </div>
    </div>
  </div>
  <div class="empty" id="emptyState" style="display:none">
    <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="#94a3b8" stroke-width="1.5"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
    <p>No API keys yet</p>
    <button class="create-btn" onclick="createKey()">Create your first key</button>
  </div>
  <div class="info-bar">
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
    Revoking a key cannot be undone. Any apps using a revoked key will lose access immediately.
  </div>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month, 7 days free), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Masked key display: first 8 chars + bullets + last 4 (industry-standard pattern)
data-key attribute stores the full key — never in visible DOM by default
toggleKey(): alternates eye-off/eye-on SVG and masked/full key text
navigator.clipboard.writeText() with 2s checkmark icon feedback
revokeKey(): opacity fade then DOM removal with confirm() guard
createKey(): prompt for name + crypto-random key generation
Empty state: shows when all keys are revoked
Environment chips: Live (green) and Test (amber) labels
Permission badges: read, write, admin with colour coding

About this UI Snippet

API Key Manager — Show/Hide Toggle, Copy, Revoke, and Create New Keys

Screenshot of the API Key Manager snippet rendered live

An API key manager is a standard component in any developer-facing SaaS product, authentication service, or platform dashboard. It lets users create, view, copy, and revoke API access tokens from one place. This snippet provides a complete API key manager with masked key display, show/hide toggle with eye icon, copy to clipboard with checkmark feedback, revoke with confirmation dialog, create new key prompt, environment chips (Live/Test), permission badges (read/write/admin), and an empty state when all keys are revoked.

The masked key display

Each key is stored in data-key on the .key-row — never in the visible DOM by default. The .key-code element shows a masked version: the first 8 characters + bullet characters + the last 4 characters. This is the industry-standard partial masking pattern used by Stripe, Vercel, and AWS. toggleKey() reads the full key from data-key and alternates between the masked and full string.

The show/hide toggle

toggleKey() swaps the eye-off and eye-on SVG icons by toggling their display properties. code.dataset.shown tracks the current state (false = masked). The full key is always available in the row's data-key attribute — it never leaves the DOM element, so no server round-trip is needed to reveal it. In production, you would NOT store the full key in the DOM — instead, fetch it from the API on demand.

Copy to clipboard

copyKey() uses navigator.clipboard.writeText() to copy the full key. On success, the copy icon morphs to a checkmark (by replacing the SVG innerHTML) and .copied class adds a green tint. After 2 seconds, the icon reverts. This pattern is identical to the copy button snippet but embedded in the key row.

Revoke and empty state

revokeKey() shows a confirm() dialog before removing the row. On confirm, the row fades to 0.4 opacity with pointer-events: none before DOM removal (visual confirmation the action is in progress). After removal, if no .key-row elements remain, the keys list hides and the .empty empty state appears.

Security considerations for production

The snippet stores the full key in a data-key DOM attribute for demonstration. In a real application, never embed full API keys in the HTML. The recommended pattern: on key creation, return the full key in the API POST response and display it once in a modal with a "Copy and close" button. Store only a hashed (bcrypt or SHA-256) version in the database, along with a short prefix (sk_live_Qb5e) for identification. On subsequent page loads, only fetch the masked prefix from the API — the full key is never retrievable again. This is the same pattern used by Stripe, GitHub, and Vercel.

Key expiry and rotation UI

A production API key manager should support expiry dates and key rotation. Add an "Expires" column to each key row with a date picker for optional expiry. Show a warning badge on keys expiring within 7 days. The "Rotate" action (instead of Revoke) generates a new key, briefly shows both old and new keys active simultaneously for a transition window, then automatically revokes the old key after the window. This zero-downtime rotation pattern prevents service interruption during credential updates.

Step by step

How to Use

  1. 1
    Click the eye icon to reveal a keyEach row shows a masked key (sk_live_••••...Qb5e). Click the eye button to toggle between masked and full key display. The icon switches between eye-off and eye-on variants.
  2. 2
    Copy a key to clipboardClick the copy icon next to any key. The icon turns green with a checkmark for 2 seconds to confirm. The full key is copied regardless of whether it is masked or shown.
  3. 3
    Revoke a keyClick Revoke on any key row. A confirmation dialog appears. On confirm, the row fades and is removed. If all keys are revoked, the empty state appears.
  4. 4
    Create a new keyClick the New Key button. Enter a name in the prompt. A new test key row is added at the bottom with a randomly generated key.
  5. 5
    Add permission scopesEdit the .perms div in each key row to add read/write/admin chips. Wire these to a modal where users select scopes when creating a key.
  6. 6
    Export for your frameworkClick "JSX" for a React component with key state management, masked display, and clipboard integration. Click "Vue" for a Vue 3 SFC.

Real-world uses

Common Use Cases

Developer dashboard API credentials page
The primary use case: let users generate API keys to authenticate their applications (the password generator shows the crypto-random string technique). Wire createKey() to POST /api/keys, revokeKey() to DELETE /api/keys/:id, and the copy button to the actual key value returned by the POST response (never store full keys in the frontend after initial display).
OAuth application client secrets management
Adapt for OAuth client credentials: show Client ID in plain text and Client Secret masked. The show/hide toggle lets developers reveal the secret to configure their app. Include a Rotate Secret option that generates a new secret and invalidates the old one.
Webhook endpoint secrets and signing keys
List webhook signing secrets for each registered endpoint. The masked display prevents accidental exposure in screen shares or videos. The copy button makes it easy to paste into CI/CD environment variable fields.
Integrate with a JWT or API key authentication system
On createKey(), POST to your auth service which returns the full key once. Store only a hashed version on the server. In the frontend, show the full key in a one-time modal ("Copy this key — it won't be shown again") and then only show the masked version on subsequent loads.
Study masked input display and clipboard patterns
The snippet demonstrates partial masking (first + last chars visible), a show/hide toggle like the password toggle, and the navigator.clipboard API with async/await success feedback. These patterns apply to any sensitive data display — tokens, passwords, connection strings.
Analytics and data access token management
Manage read-only API tokens for embedding charts, accessing report data, or connecting BI tools. The permission badges (read/write/admin) let users assign minimal-privilege tokens to each integration.
Related: BroadcastChannel Cross-Tab Sync
See the BroadcastChannel Cross-Tab Sync for a related dashboards pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

For a demo, yes. In production, no — never embed live API keys in the DOM. The standard pattern: on key creation, show the full key once in a modal ("Save this — it won't appear again"), then store only a hashed/masked version in your database. On subsequent page loads, only fetch the masked version. Reveal requests must go to a secure endpoint with auth.

In the browser: crypto.getRandomValues(new Uint8Array(32)) gives 32 cryptographically random bytes. Convert to hex: Array.from(bytes, b => b.toString(16).padStart(2,"0")).join(""). On the server (Node.js): crypto.randomBytes(32).toString("hex") or "base64url". Never use Math.random() for security-sensitive tokens.

Manage keys as const [keys, setKeys] = useState(initialKeys). Each key object: { id, name, masked, full, env, perms, createdAt }. Render a KeyRow component per key with show/hide state in useState(false). revokeKey filters the array: setKeys(keys.filter(k => k.id !== id)). createKey appends a new key object from an API call.

The full key lives in a data-key attribute on the .key-row, never in the visible text. The masked form is built with string slicing — the first 8 characters, a run of bullet characters, then the last 4: fullKey.slice(0, 8) + "•".repeat(n) + fullKey.slice(-4) — which preserves the recognisable sk_live_ prefix while hiding the secret middle. toggleKey() swaps the .key-code element's textContent between the masked and full value, tracks the current state in code.dataset.shown, and switches the eye / eye-off icons to match.

revokeKey() first gates on confirm("Revoke this API key? This cannot be undone."), then plays a soft exit: the row's opacity drops to 0.4 with pointer-events disabled, and after 400ms the row is removed from the DOM. If no .key-row remains, the list is hidden and the #emptyState panel is shown instead, so the UI never ends on a blank list. In production, call your DELETE /keys/:id endpoint before the animation and only remove the row on a successful response — keep the confirm step, because key revocation is irreversible.