import { useRef, useState, useEffect } from 'react'; function App() { const canvasRef = useRef(null); const [text, setText] = useState('YOUR AWESOME TITLE'); const [subtext, setSubtext] = useState('Click to watch now!'); const [fontSize, setFontSize] = useState(80); const [subFontSize, setSubFontSize] = useState(40); const [textColor, setTextColor] = useState('#FFFFFF'); const [strokeColor, setStrokeColor] = useState('#000000'); const [backgroundColor, setBackgroundColor] = useState('#FF0000'); const [backgroundImage, setBackgroundImage] = useState(null); const [fontFamily, setFontFamily] = useState('Arial Black'); const [textY, setTextY] = useState(300); const [subTextY, setSubTextY] = useState(450); const [strokeWidth, setStrokeWidth] = useState(8); const CANVAS_WIDTH = 1280; const CANVAS_HEIGHT = 720; useEffect(() => { drawThumbnail(); }, [text, subtext, fontSize, subFontSize, textColor, strokeColor, backgroundColor, backgroundImage, fontFamily, textY, subTextY, strokeWidth]); const drawThumbnail = () => { const canvas = canvasRef.current; if (!canvas) return; const ctx = canvas.getContext('2d'); if (!ctx) return; // Clear canvas ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT); // Draw background if (backgroundImage) { const img = new Image(); img.onload = () => { ctx.drawImage(img, 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT); drawText(ctx); }; img.src = backgroundImage; } else { ctx.fillStyle = backgroundColor; ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT); drawText(ctx); } }; const drawText = (ctx: CanvasRenderingContext2D) => { // Draw main text ctx.font = `bold ${fontSize}px ${fontFamily}`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; // Stroke (outline) ctx.strokeStyle = strokeColor; ctx.lineWidth = strokeWidth; ctx.strokeText(text, CANVAS_WIDTH / 2, textY); // Fill ctx.fillStyle = textColor; ctx.fillText(text, CANVAS_WIDTH / 2, textY); // Draw subtext if (subtext) { ctx.font = `bold ${subFontSize}px ${fontFamily}`; ctx.strokeStyle = strokeColor; ctx.lineWidth = strokeWidth / 2; ctx.strokeText(subtext, CANVAS_WIDTH / 2, subTextY); ctx.fillStyle = textColor; ctx.fillText(subtext, CANVAS_WIDTH / 2, subTextY); } }; const handleImageUpload = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (file) { const reader = new FileReader(); reader.onload = (event) => { setBackgroundImage(event.target?.result as string); }; reader.readAsDataURL(file); } }; const downloadThumbnail = () => { const canvas = canvasRef.current; if (!canvas) return; const link = document.createElement('a'); link.download = 'youtube-thumbnail.png'; link.href = canvas.toDataURL('image/png'); link.click(); }; const clearBackground = () => { setBackgroundImage(null); }; return (

YouTube Thumbnail Generator

Create stunning thumbnails in seconds • 1280x720px

{/* Canvas Preview */}

Preview

{/* Controls */}
{/* Text Controls */}

✏️ Text

setText(e.target.value)} className="w-full px-4 py-2 bg-gray-700 border border-gray-600 rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent" placeholder="Enter your title" />
setSubtext(e.target.value)} className="w-full px-4 py-2 bg-gray-700 border border-gray-600 rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent" placeholder="Enter subtitle (optional)" />
{/* Font Controls */}

🎨 Styling

setFontSize(Number(e.target.value))} className="w-full" />
setSubFontSize(Number(e.target.value))} className="w-full" />
setTextY(Number(e.target.value))} className="w-full" />
setSubTextY(Number(e.target.value))} className="w-full" />
setTextColor(e.target.value)} className="w-full h-12 rounded-lg cursor-pointer" />
setStrokeColor(e.target.value)} className="w-full h-12 rounded-lg cursor-pointer" />
setStrokeWidth(Number(e.target.value))} className="w-full mt-3" />
{/* Background Controls */}

🖼️ Background

setBackgroundColor(e.target.value)} className="w-full h-12 rounded-lg cursor-pointer" disabled={!!backgroundImage} />
{backgroundImage && ( )}
{/* Quick Presets */}

⚡ Quick Presets

); } export default App;