extension-qr-code — Claude Code skill: camera QR scanner
Adds QR code scanning through the device camera to a React app. Useful when you need to read a code right in the browser.
npx skills add caffeinelabs/skills --skill extension-qr-code -a claude-codeWhat extension-qr-code does
The skill ships a ready-made React hook, useQRScanner, from the @caffeinelabs/qr-code package. It sits on top of the camera component and decodes codes with jsQR. The hook returns the list of scanned codes, camera state, and methods to start, stop and switch the camera.
The hook itself cannot be modified. Configuration is small: scanInterval sets how often scanning happens in milliseconds, maxResults caps the result history, and jsQRUrl points at where the jsQR library loads from. By default jsQR comes from the jsdelivr CDN, so the app depends on an outside network — worth checking how the scanner behaves without CDN access.
Two packages sit underneath: @caffeineai/qr-code at ~0.1.2 and @caffeineai/camera at ~0.1.1. Attach videoRef to the video element and keep a hidden canvasRef for processing. Camera errors land in the error field, and the skill expects you to surface them in the UI.
What it helps with
- scan a QR code with a phone camera in the browser
- show a timestamped history of scanned codes
- switch between front and rear cameras
- handle the case where the camera is unsupported or blocked
- clear results before starting a new scan
Example prompts for the agent
- wire up useQRScanner from @caffeineai/qr-code and build a scanner component
- add a switch camera button and show it only on mobile
- render the camera error when error is not null
How to install extension-qr-code
1Skills CLI (Vercel)
npx skills add caffeinelabs/skills --skill extension-qr-code -a claude-code2Manually: put the files in place
~/.claude/skills/extension-qr-code/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/extension-qr-code/
1Skills CLI (Vercel)
npx skills add caffeinelabs/skills --skill extension-qr-code -a codex2Manually: put the files in place
.agents/skills/extension-qr-code/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add caffeinelabs/skills --skill extension-qr-code -a cursor2Manually: put the files in place
.cursor/skills/extension-qr-code/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install caffeinelabs/skills extension-qr-code2Manually: put the files in place
.github/skills/extension-qr-code/SKILL.md1Skills CLI (Vercel)
npx skills add caffeinelabs/skills --skill extension-qr-code -a gemini-cli2Platform CLI
gemini skills install https://github.com/caffeinelabs/skills --path skills/extension-qr-codeManually: put the files in place
.devin/skills/extension-qr-code/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add caffeinelabs/skills --skill extension-qr-code -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about extension-qr-code
How do I install extension-qr-code in Claude?
Run npx skills add caffeinelabs/skills --skill extension-qr-code -a claude-code. The skill lands in ~/.claude/skills/extension-qr-code/SKILL.md.
What does extension-qr-code do?
It adds QR code scanning through the device camera, built on the useQRScanner React hook and jsQR decoding.
What packages does it need?
@caffeineai/qr-code at ~0.1.2 and @caffeineai/camera at ~0.1.1. The hook is imported from @caffeinelabs/qr-code and cannot be edited.
Source text
SKILL.md · Apache-2.0
---
name: extension-qr-code
description: QR code scanner using the camera.
version: 0.1.5
compatibility:
npm:
"@caffeineai/qr-code": "~0.1.2"
"@caffeineai/camera": "~0.1.1"
caffeineai-subscription: [none]
---
# QR Code Scanner
QR code scanner extension for [Caffeine AI](https://caffeine.ai?utm_source=caffeine-skill&utm_medium=referral).
## Overview
This skill adds QR code scanning using the device camera. Built on top of the camera component with jsQR for decoding.
# Frontend
For QR code scanner support:
There is a prefabricated React hook imported from `@caffeinelabs/qr-code` that cannot be modified.
```typescript filepath=@caffeinelabs/qr-code/src/hooks/useQRScanner.ts
import { RefObject } from 'react';
import { CameraConfig, CameraError } from '@caffeineai/camera';
export interface QRResult {
// The decoded QR code data
data: string;
// Timestamp when the QR code was scanned
timestamp: number;
}
export interface QRScannerConfig extends CameraConfig {
// How often to scan for QR codes in milliseconds (default: 100)
scanInterval?: number;
// Maximum number of results to keep in history (default: 10)
maxResults?: number;
// URL to load jsQR library from (default: jsdelivr CDN)
jsQRUrl?: string;
}
export interface UseQRScannerReturn {
// Array of scanned QR codes (newest first)
qrResults: QRResult[];
// Whether currently scanning for QR codes
isScanning: boolean;
// Whether jsQR library has been loaded
jsQRLoaded: boolean;
// Camera state (pass-through from useCamera)
isActive: boolean;
isSupported: boolean | null;
error: CameraError | null;
isLoading: boolean;
currentFacingMode: 'user' | 'environment';
// Start camera and begin scanning - returns true on success
startScanning: () => Promise<boolean>;
// Stop scanning and camera
stopScanning: () => Promise<void>;
// Switch camera facing mode - returns true on success
switchCamera: () => Promise<boolean>;
// Clear all scan results
clearResults: () => void;
// Reset scanner state (stop scanning and clear results)
reset: () => void;
// Retry camera initialization after error - returns true on success
retry: () => Promise<boolean>;
// Ref to attach to video element for camera preview
videoRef: RefObject<HTMLVideoElement>;
// Ref to attach to canvas element used for QR processing (can be hidden)
canvasRef: RefObject<HTMLCanvasElement>;
// Computed state
// Whether scanner is ready to use (jsQR loaded and camera supported)
isReady: boolean;
// Whether scanning can be started (ready + not loading)
canStartScanning: boolean;
}
export declare function useQRScanner(config?: QRScannerConfig): UseQRScannerReturn;
```
Usage example:
```typescript filepath=example.ts
import { useQRScanner } from '@caffeineai/qr-code';
function QRScannerComponent() {
const {
qrResults,
isScanning,
isActive,
isSupported,
error,
isLoading,
canStartScanning,
startScanning,
stopScanning,
switchCamera,
clearResults,
videoRef,
canvasRef
} = useQRScanner({
facingMode: 'environment',
scanInterval: 100,
maxResults: 5
});
if (isSupported === false) {
return <div>Camera not supported</div>;
}
return (
<div>
<video
ref={videoRef}
style={{ width: '100%', height: 'auto' }}
playsInline
muted
/>
<canvas ref={canvasRef} style={{ display: 'none' }} />
{error && <div>Error: {error.message}</div>}
<div>
<button onClick={startScanning} disabled={!canStartScanning}>
Start Scanning
</button>
<button onClick={stopScanning} disabled={isLoading || !isActive}>
Stop Scanning
</button>
{/* Only show switch camera on mobile */}
{/Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) && (
<button onClick={switchCamera} disabled={isLoading || !isActive}>
Switch Camera
</button>
)}
</div>
<div>
<h3>Results {qrResults.length > 0 && <button onClick={clearResults}>Clear</button>}</h3>
{qrResults.map(result => (
<div key={result.timestamp}>
<small>{new Date(result.timestamp).toLocaleTimeString()}</small>
<p>{result.data}</p>
</div>
))}
</div>
</div>
);
}
```
Properly display QR scanner error messages in the app.
© caffeinelabs · Apache-2.0 · caffeinelabs/skills