Skip to main content

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

AspectProgressive EnhancementGraceful Degradation
Starting pointBasic functionalityFull functionality
ApproachBuild upFall back
Core experienceUniversalModern browsers
MaintenanceAdd new featuresFix broken fallbacks
Testing priorityOld browsers firstNew 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