Building a resilient frontend using progressive enhancement

Progressive enhancement is a way of building websites and applications. It’s based on the idea that you should start by making your page work with just HTML, before adding anything else like Cascading Style Sheets (CSS) and JavaScript.

This is because HTML is the most resilient layer. If the HTML fails there’s no web page. Should the CSS or JavaScript fail, the HTML will still render correctly.

Using progressive enhancement means your users will be able to do what they need to do if any part of the stack fails. Building your service using progressive enhancement will:

  • make your service more resilient
  • mean your service’s most basic functionality will work and meet the core needs of the user
  • improve accessibility by encouraging best practices like writing semantic markup
  • help users with device or connectivity limitations to use your service

Start with HTML

Most government services should be functional using only HTML. This includes services such as:

Using interactive elements

You can use interactive elements, but there must be a fallback that allows for the same core functionality. For example, a dynamic autocomplete element could fall back to a <select> element, or something similar. This still lets the user do what they need to do, even if the interactive element fails.

You must also structure your source order and document outline logically.

This approach will give your service a strong foundation and means your site will work with most devices and browsers, including older ones. This approach also helps make sure your site is accessible, as the user will be able to access everything they need via HTML.

Adding the extras

Once you’ve built a foundation of HTML for your service, you can add things like:

  • images
  • styling
  • video and audio
  • scripted behaviour, for example JavaScript
  • smoother and faster interactions that do not require the user to refresh the entire page
  • ways to validate data that users submit before that data hits the network
  • interactive charts

Building more complex services

Building services using progressive enhancement gives you a resilient base from which to build more complex services.

In cases such as this, it’s not necessarily helpful to think about the service as a whole. You should break the service down into different interactions and decide if JavaScript offers the best user experience.

Some government services may need functionality that you cannot implement using HTML alone. For example, the Environment Agency’s flood mapping service, which shows areas that are at risk of flooding, needs JavaScript to work.

Doing this could cause problems. For example:

  • parts of your service will not be accessible to disabled users
  • your service may be less reliable

To avoid these drawbacks, you should first try to make the functionality work without JavaScript. If that’s not possible, your JavaScript implementation should have a fallback digital interaction. That might not be as good as the JavaScript-enabled interaction, but it will mean users can still do what they need to with your service. For example, this could be data shown in an accessible table.

If neither of those are possible you’ll need to show you’ve considered how users of assistive technology can use your service. This may mean non-digital channels, such as telephone calls or in-person visits to offices.

Using JavaScript frameworks

Some developers find JavaScript frameworks a convenient way of using JavaScript on their service.

Some JavaScript frameworks require the user to download large amounts of data. This can cause performance issues, particularly if the user has a slow internet connection or an older, slower device. You should consider whether the benefits of using a JavaScript framework outweigh these potential performance problems.

Using JavaScript frameworks can cause other issues, such as:

  • reliance on third-party code that your developers do not have control over
  • difficulties in finding the skills required to maintain the framework, if its popularity drops

You’ll need to make sure any JavaScript framework loads the elements as isolated components. If the JavaScript fails to load, it will only be that single component that fails. The rest of the page will load as normal. If your service renders the whole page using a JavaScript framework, which then fails to load, your service will break.

These components should have fallbacks in place, so users will still be able to use the service if the JavaScript fails.

The server should render the HTML fallback and deliver that code to the browser as normal. You can then use JavaScript to enhance the component where necessary.

Using JavaScript server-side rendering (SSR)

You can use server-side rendering, also known as isomorphic JavaScript or Universal JavaScript. However you’ll need to test the service with client-side JavaScript disabled. The rendered HTML should still work as a functional application if the client-side JavaScript fails.

Testing your service

You’ll need to test the components of your service that heavily rely on JavaScript or JavaScript frameworks for accessibility. Ideally you should test these components with real users.

You’ll also need to test the performance of your frontend.

Do not assume users turn off CSS or JavaScript

You should not assume the reason for designing a service that works without CSS or JavaScript is because a user chooses to switch these off.

There are many situations when extra layers can fail to load or are filtered, for example:

  • temporary network errors
  • third-party browser extensions like ad blockers
  • third-party supplier downtime, such as when using a content delivery network
  • DNS lookup failures
  • bugs introduced by browser updates
  • corporate firewalls blocking, removing or changing content (large institutions like banks or government departments may use these)
  • mobile network providers changing content to speed up load times and reduce bandwidth usage
  • personal firewalls or antivirus software changing or blocking content
  • the use of unsecure connections, where internet providers insert their own code into the page that accidentally conflicts with your own

Some users turn off features in their browsers deliberately. You should respect their decision and make sure those users can still use your service.

Last update:

Added browser update bugs to the list of reasons why JavaScript or CSS might fail to load

  1. Updated to reflect progressive enhancement's effect on a service's resilience, plus clarified guidance on building more complex services that use JavaScript

  2. Guidance first published