A client called in November. Their JavaScript-heavy web application was broken for an entire segment of their user base — corporate employees behind a proxy that stripped inline scripts. The application showed a blank white page. No content. No fallback. Nothing.
The fix took three weeks. It should never have been necessary.
What progressive enhancement means today
Progressive enhancement is not about supporting browsers without JavaScript. It is about building layers of experience where each layer works independently.
The base layer is HTML. It delivers content and structure. A form with proper HTML attributes submits data without JavaScript. A link navigates without a client-side router. A page with semantic markup is readable without styles.
The second layer is CSS. It adds visual design, layout, and responsive behavior. If CSS fails to load, the content is still accessible — just unstyled.
The third layer is JavaScript. It adds interactivity, dynamic behavior, and enhanced user experiences. If JavaScript fails — blocked, errored, or slow to load — the base layers still function.
This is not theoretical. JavaScript fails more often than you think.
Why JavaScript fails
Network issues. Mobile users on unreliable connections may receive HTML before JavaScript times out. A content delivery network misconfiguration can serve HTML from cache while JavaScript returns a 503.
Corporate proxies and firewalls. Enterprise networks routinely block, modify, or delay scripts. Content security policies can prevent inline scripts from executing.
Browser extensions. Ad blockers, privacy extensions, and accessibility tools can interfere with scripts. Roughly thirty percent of users have at least one extension that modifies page behavior.
Errors in your code. An uncaught exception in one module can prevent the entire bundle from executing. A single TypeError in initialization can blank the screen.
Progressive enhancement does not prevent these failures. It ensures they are not catastrophic.
Practical patterns
Forms that work without JavaScript. Use standard HTML form elements with action and method attributes. The form submits to the server, the server processes it, the server returns a response. Then enhance with JavaScript — inline validation, optimistic updates, submission without page reload. If the enhancement fails, the base form still works.
Navigation that works without a client-side router. Use <a> tags with real href attributes. The browser handles navigation natively. Then enhance with a client-side router for faster transitions and preserved state. If the router fails, links still work.
Content that renders on the server. Server-side rendering or static generation ensures content is in the HTML before JavaScript executes. Client-side hydration adds interactivity. If hydration fails, the content is still readable.
Interactive components with fallbacks. A sortable data table can start as a static HTML table. JavaScript adds sorting controls. An image carousel can start as a simple list of images. JavaScript adds the carousel behavior.
Astro gets this right
Our choice of Astro for many projects is directly informed by progressive enhancement. Astro renders everything to HTML by default. JavaScript is opt-in, per-component, through directives like client:load and client:visible.
A page built with Astro works without JavaScript because it is HTML first. Interactive islands hydrate independently — if one fails, the others continue to work. This is progressive enhancement as an architectural default, not an afterthought.
The cost is lower than you think
The common objection is that progressive enhancement doubles the work — you build the base version and then the enhanced version. In practice, the base version is often trivial. HTML forms, semantic markup, and server rendering are less work than client-side state management and custom form handling.
The enhanced layer adds convenience and polish, not core functionality. This is actually easier to build because you are adding to a working foundation instead of building everything from scratch in JavaScript.
Testing progressive enhancement
Disable JavaScript in your browser and use the application. Can you read content? Can you navigate? Can you submit forms?
If the answer is “nothing works,” you have a JavaScript application, not a web application. If the core workflows function — even if they are less polished — you have a progressively enhanced experience that will survive the real-world conditions your users face.
The web was designed to be resilient. Progressive enhancement is how you honor that design.