A stylish random quote generator that fetches quotes from a free public API, displays them with a fade animation, and lets you share them on Twitter/X.
quotable.io provides a free API with 1,000+ curated quotes — no key needed.
async function getQuote() {
const res = await fetch('https://api.quotable.io/random');
const data = await res.json();
console.log(data.content); // "Life is what happens when..."
console.log(data.author); // "John Lennon"
}
getQuote();
Card layout, a quote block, author line, and two action buttons.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Random Quote Generator</title>
<style>
* { margin:0; padding:0; box-sizing:border-box; }
body { font-family:'Segoe UI',sans-serif; background:linear-gradient(135deg,#1e3a5f,#162032); min-height:100vh; display:flex; align-items:center; justify-content:center; padding:20px; }
.card { background:#fff; color:#1e293b; border-radius:20px; padding:48px 44px; max-width:580px; width:100%; box-shadow:0 25px 60px rgba(0,0,0,0.35); }
.quote-mark { font-size:5rem; line-height:1; color:#e2e8f0; font-family:Georgia,serif; margin-bottom:-20px; }
#quote { font-size:1.3rem; line-height:1.7; font-style:italic; color:#334155; margin-bottom:20px; transition:opacity 0.4s; }
#author { font-weight:700; color:#0f172a; font-size:1rem; margin-bottom:32px; }
#author::before { content:'— '; color:#94a3b8; }
.actions { display:flex; gap:12px; }
.btn-new { flex:1; padding:12px; background:#1e3a5f; color:#fff; border:none; border-radius:10px; font-size:0.95rem; font-weight:700; cursor:pointer; transition:background 0.2s; }
.btn-new:hover { background:#162032; }
.btn-tweet { padding:12px 20px; background:#1DA1F2; color:#fff; border:none; border-radius:10px; font-size:0.95rem; font-weight:700; cursor:pointer; text-decoration:none; display:flex; align-items:center; gap:6px; }
.loading { opacity:0.3; }
</style>
</head>
<body>
<?php require_once __DIR__ . '/../promo_banner.php'; ?>
<div class="card">
<div class="quote-mark">"</div>
<p id="quote">Loading...</p>
<p id="author"></p>
<div class="actions">
<button class="btn-new" onclick="loadQuote()">New Quote</button>
<a class="btn-tweet" id="tweetBtn" href="#" target="_blank">Tweet</a>
</div>
</div>
<script src="app.js"></script>
</body>
</html>
Fade the quote out, update it, then fade back in.
// app.js
const quoteEl = document.getElementById('quote');
const authorEl = document.getElementById('author');
const tweetBtn = document.getElementById('tweetBtn');
async function loadQuote() {
quoteEl.classList.add('loading');
try {
const res = await fetch('https://api.quotable.io/random');
const data = await res.json();
// Fade in new content
setTimeout(() => {
quoteEl.textContent = data.content;
authorEl.textContent = data.author;
tweetBtn.href = `https://twitter.com/intent/tweet?text=${encodeURIComponent('"' + data.content + '" — ' + data.author)}`;
quoteEl.classList.remove('loading');
}, 300);
} catch (err) {
quoteEl.textContent = 'Could not load a quote. Check your connection.';
quoteEl.classList.remove('loading');
}
}
// Load on page open
loadQuote();
Your first API-powered app. The same fetch() pattern works with any JSON API — weather, movies, jokes, exchange rates. Change the API URL and adjust the display code for any data source.
Spotted a bug, broken code, or something that doesn't look right? Tell us what's off and we'll fix it.