Progressive Enhancement vs Graceful Degradation
Answer
These are two different philosophies for building web applications that work across varying browser capabilities.
Conceptual Difference
Progressive Enhancement
"Start simple, enhance progressively"
<!-- Layer 1: Semantic HTML (always works) -->
<form action="/search" method="GET">
<label for="search">Search:</label>
<input type="text" id="search" name="q" />
<button type="submit">Search</button>
</form>
/* Layer 2: CSS enhancement */
.search-form {
display: flex;
gap: 1rem;
}
@supports (backdrop-filter: blur(10px)) {
.search-form {
backdrop-filter: blur(10px);
}
}
// Layer 3: JavaScript enhancement
if ("IntersectionObserver" in window) {
// Use modern lazy loading
} else {
// Images load normally
}
// Layer 4: Cutting-edge features
if ("scheduler" in window) {
scheduler.postTask(() => analytics.track());
} else {
setTimeout(() => analytics.track(), 0);
}
Graceful Degradation
"Build modern, provide fallbacks"
<!-- Modern video with fallbacks -->
<video controls>
<source src="video.webm" type="video/webm" />
<source src="video.mp4" type="video/mp4" />
<!-- Fallback for no video support -->
<p>
Your browser doesn't support video.
<a href="video.mp4">Download the video</a>.
</p>
</video>
<!-- Picture element with fallback -->
<picture>
<source srcset="image.avif" type="image/avif" />
<source srcset="image.webp" type="image/webp" />
<img src="image.jpg" alt="Fallback JPEG" />
</picture>
// Feature detection with fallback
function modernClipboard(text) {
if (navigator.clipboard && navigator.clipboard.writeText) {
return navigator.clipboard.writeText(text);
}
// Fallback
const textarea = document.createElement("textarea");
textarea.value = text;
document.body.appendChild(textarea);
textarea.select();
document.execCommand("copy");
document.body.removeChild(textarea);
return Promise.resolve();
}
Comparison
| Aspect | Progressive Enhancement | Graceful Degradation |
|---|---|---|
| Starting point | Basic functionality | Full functionality |
| Approach | Build up | Fall back |
| Core experience | Universal | Modern browsers |
| Maintenance | Add new features | Fix broken fallbacks |
| Testing priority | Old browsers first | New browsers first |
Real-World Examples
Progressive Enhancement
<!-- Form works without JS -->
<form action="/subscribe" method="POST">
<input type="email" name="email" required />
<button type="submit">Subscribe</button>
</form>
<script>
// Enhance with AJAX submission
document.querySelector("form").addEventListener("submit", async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
try {
await fetch("/subscribe", {
method: "POST",
body: formData,
});
showToast("Subscribed!");
} catch {
// Fall back to normal form submission
e.target.submit();
}
});
</script>
Graceful Degradation
// CSS Grid with flexbox fallback
const styles = `
.grid {
display: flex;
flex-wrap: wrap;
}
@supports (display: grid) {
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
}
}
`;
Feature Detection Best Practices
// Modernizr-style detection
const supports = {
grid: CSS.supports("display", "grid"),
webp:
document
.createElement("canvas")
.toDataURL("image/webp")
.indexOf("data:image/webp") === 0,
intersectionObserver: "IntersectionObserver" in window,
serviceWorker: "serviceWorker" in navigator,
};
// Use feature detection, not browser detection
// ❌ Bad
if (navigator.userAgent.includes("Chrome")) {
}
// ✅ Good
if ("IntersectionObserver" in window) {
}
When to Use Each
Progressive Enhancement:
- Content-focused websites
- Wide audience (varying devices/connections)
- SEO-critical pages
- Government/accessibility-required sites
Graceful Degradation:
- Web applications with complex interactions
- Internal tools with known browser requirements
- When modern features are core to the experience
Key Points
- Progressive Enhancement: Core works everywhere
- Graceful Degradation: Modern first, fallbacks added
- Both require feature detection
- Neither is "better" - choose based on context
- Combine approaches as needed