Warning signs of front-end complexity
These technologies give us the possibility to improve the user experience and developer experience. However, they are often misused or misunderstood. Instead of making something more usable or more accessible, they often result in the opposite.
This article outlines three warning signs to look out for, their effect on the user or developer experience, and alternative techniques that should be considered.
- Warning: Paths after the hash (#) in the URL
- Warning: Loading indicators on page load
- Warning: Page does not reload or redirect when submitting a form
Warning: Paths after the hash (#) in the URL
When using a link, verify the following:
- Refreshing or reloading works as expected.
- Using the browser back and forward buttons works as expected.
- Pasting the URL into another browser, window, or tab works as expected.
- Using in-page links (such as screen-reader skip links and “On this page” navigation) works as expected.
- The page title updates to be similar or identical to the primary heading.
- If using client-side rendering: Focus moves to the primary heading and a screen reader announces the heading.
Look for URLs ending with a hash symbol and a path, such as
#/path/to/page. A full URL could look like
example.com/sub-page/?foo=bar#/path/to/page. This “hash routing” technique creates complications by:
- Placing the path before query parameters, causing a usability issue.
This hash routing technique is a primary clue that client-side routing is implemented, but more sophisticated implementations may not have this clue.
Client-side routing typically adds even more technical complications. The implementation may:
- Use third-party dependencies, which increases the code sent to users.
- Force the code for the entire app to be downloaded up front, instead of the code needed for each page on demand.
- Share cached data among page transitions and various UI components. This typically leads to additional tooling and code to manage state and refresh stale data.
Overcoming all these issues is rarely worth the effort. If the technical goal is to have a single static launch page and the client manage routing, a better approach is to make the path a URL parameter (
?page=path/to/page). This technique can:
- Refresh the page.
- Simplify routing.
- Reduce the need for state management.
- Avoid interfering with in-page navigation.
- Place the path before secondary query parameters (such as
The following is a simple implementation of this style of routing. Code and data for rendering pages are dynamically loaded.
Warning: Loading indicators on page load
In more detail:
- The browser loads a blank or empty page.
- Server responds with data or an error.
- The browser loads a blank or empty page.
By having the server provide the initial data on load, it renders faster, removes the need for a loading indicator, and shifts the burden of handling network errors from the client to the server.
To embed data, the server will have to respond with some dynamic HTML. If that’s happening anyway, then it is best for the server to also manage the page title (as it could be dynamic as well) and identify the code needed to supplement the page. If the server is already doing this work, then it is likely little more work to switch from using the query-parameter-style links from the previous Warning section (
?page=path/to/page) to just standard links (
The following is an updated version of the sample code from the previous Warning section.
Furthermore, the server rather than the browser could render the page contents, especially if there is little to no interactivity beyond links and form submissions. With Node, some of the same tools used in the browser can be used on the server. That means for example, a page that was rendered by a browser with React could instead be rendered by the server with React.
In the meantime, consider what on the page is truly interactive or needs dynamic data.
- Try using custom web components to provide interactivity.
- Try rendering substantial sections of static content with the server.
Warning: Page does not reload or redirect when submitting a form
The default behavior of a form submission results in a call to the server. The server then responds with redirecting or reloading the page. Creating a new document may open that document. Purchasing a product may redirect the user to a confirmation page. Changing filters on a search page may reload the current page with the new applied values.
If the form would trigger a call to the server anyway, it may be better to allow the default submit behavior to continue on its own if it passes client-side validation. In this case, the additional loading state may not be needed, and the server could include any success or error information in the pages to which it redirects or reloads.
When the form is submitted with this GET method:
- The form data is automatically serialized into a query string (
- Without an explicit “action” attribute on the form, the query string is applied to the current page, causing a refresh.
- The server renders the page with the requested data. That data could be embedded in the HTML (see the previous Warning section).
- Incur unnecessary technical and design debt
- Increase the possibility of bugs
- Slow down development time
- Make the product less usable and less accessible
- Obstruct the user and developer experience
- Let’s make accessibility boring again
- Interview with Adrian Roselli about his “under-engineered” series of articles. The latest article is Under-Engineered Multi-Selects.
- Rendering on the Web
- Rendering patterns
- The balance has shifted away from SPAs