Building Resilient Frontends
A resilient frontend is one that degrades gracefully. It works without JavaScript. It loads fast on slow connections. It's usable on any screen size. It serves the content first and enhances the experience second.
The web platform gives us resilience for free — if we work with it rather than against it. But somewhere along the way, we started shipping megabytes of JavaScript for what should have been a document. We forgot that HTML is already responsive, already accessible, and already fast.
This post is about finding our way back.
The Layers of Resilience
Think of a resilient frontend as layers that build on each other. Each layer works independently. If a layer fails, the layers beneath it still function. This is progressive enhancement — start with a solid foundation and add capability without breaking what's already there.
The key insight: each layer is optional. If JavaScript never loads (slow network, blocked, error), the user still gets styled content. If CSS fails too, they still get semantic HTML. Nothing breaks; it just becomes simpler.
Fragile vs. Resilient in Practice
Here's the same feature implemented two ways. On the left, a typical single-page app approach. On the right, progressive enhancement.
The resilient approach doesn't mean less capable — it means differently capable. The form submits via standard HTTP POST by default. JavaScript enhances it with client-side validation, optimistic UI updates, and fetch-based submission. When JS runs, the experience is richer. When it doesn't, the experience still works.
The Pattern: Enhance on Top of HTML
Here's the core pattern. Notice that the HTML is a fully functional starting point — the JavaScript only adds behavior on top:
<form action="/subscribe" method="POST" id="subscribe-form">
<input type="email" name="email" required>
<button type="submit">Subscribe</button>
</form>
// JS enhances, doesn't replace
const form = document.getElementById('subscribe-form');
form.addEventListener('submit', async (e) => {
e.preventDefault();
const data = new FormData(e.target);
await fetch('/subscribe', { method: 'POST', body: data });
form.insertAdjacentHTML('afterend', '<p>Thanks!</p>');
});
If JavaScript fails to load — broken CDN, ad blocker interference, flaky connection — the form submits normally and the server returns a new page. The user might not even notice something went wrong.
This isn't about avoiding frameworks. Frameworks are great for complex interactivity. It's about knowing when to use them. A blog post, a form, a product page — these don't need a framework. A spreadsheet, a drawing tool, a code editor — these probably do.
Practical Steps
If you're sold on the idea, here's where to start:
- Audit your site with JavaScript disabled. Open DevTools, disable JS, and navigate around. What breaks? What disappears entirely? Fix those things first.
- Use semantic HTML elements.
<button>not<div onclick>.<a href>not<span onclick="navigate()">.<form>not<div>with fetch. The platform gives you accessible, keyboard-navigable elements for free. - Server-render the critical path. The initial page load should be complete HTML. Don't make the browser fetch JSON, parse it, and build the DOM. Send the DOM directly.
- Keep your CSS under 50KB. If your stylesheet is larger than your content, something's wrong. CSS is cumulative — every rule adds to what the browser must compute on every frame.
- Measure everything. Lighthouse, WebPageTest, and Chrome DevTools performance tab. If you don't measure, you're guessing.
The Web Is Resilient by Default
The web was designed to be resilient. It works across devices, operating systems, screen sizes, and network conditions that didn't exist when the spec was written. Every layer of the platform was built with fallbacks in mind.
When we build with the grain of the platform — HTML first, CSS second, JS third — we inherit all of that resilience for free. When we build against it, we have to recreate it ourselves, and we usually do a worse job.
Start with the document. Enhance from there. The web will thank you.
Footnotes
If you want to go deeper, read Resilient Web Design by Jeremy Keith — it's free and it's the best book on this topic. ↩