Skip to main content

Deploying SPAs on Github Pages: Solving direct url challenges

ยท 4 min read
Pere Pages
Developer using github pages

Deploying single-page applications (SPAs) on platforms like GitHub Pages presents unique challenges, especially when it comes to handling direct URL access. In this post, we delve into the intricacies of setting up a Preact SPA on GitHub Pages, exploring solutions to common routing issues and discussing the broader implications for various SPA technologies.

The Challenge of Direct URL Accessโ€‹

404 error

When deploying SPAs on GitHub Pages, direct navigation to a URL other than the root often results in a 404 error. This is because GitHub Pages, by default, expects a static file for each URL, but SPAs typically rely on client-side routing from a single HTML file.

In SPAs, the routing is handled client-side, and there's usually only one HTML file (e.g., index.html). When a user navigates to a specific path, the SPA's router should handle that path and render the appropriate component without the need for a new page load. However, GitHub Pages doesn't know about this client-side routing and looks for a corresponding HTML file for each path, which it doesn't find.

Deep Linking vs. Direct URL Access

While both concepts allow users to navigate directly to specific content, they differ in context and implementation. Deep linking is often used in mobile apps to direct users to specific app content from external sources. In contrast, direct URL access refers to the ability to enter a URL in a web browser and reach the corresponding page in a web application, a key feature in SPAs.

Solutions for Routing in SPAsโ€‹

There are two main strategies to address this:

  • Hash-Based Routing: A simpler solution where URLs contain a #. It's easy to implement but changes the URL structure.
  • 404.html Redirect Hack: Involves creating a custom 404.html page to redirect to the index.html, preserving the path. This maintains clean URLs but is more of a workaround.

Hash-Based Routingโ€‹

developer lookiing at a hash symbol

One solution is to switch to hash-based routing. This uses URL fragments (the part of the URL following a #) for routing. Since the part of the URL after the # is not sent to the server, GitHub Pages will only see requests for the root index.html file, regardless of the hash.

For instance, With preact-router, you can use hash-based routing like this:

import { Router } from "preact-router";
import { createHashHistory } from "history";

<Router history={createHashHistory()}>{/* Your routes */}</Router>;

404.html Redirect Hackโ€‹

Developer holding redirect

Another common solution is to use a 404.html page with a script to redirect to your index.html with the correct path. Here's how you can do it.

In the context of SPAs, especially when hosted on platforms like GitHub Pages that don't natively support client-side routing, the initial URL needs to be set correctly before the SPA's router takes over. If the SPA initializes before the URL is correctly set, the router might not be able to render the appropriate component for the given path, leading to routing issues.

Therefore, whether you're working with Preact, React, Vue, or any other SPA framework, the approach of ensuring the redirection script runs before the SPA initializes is a general best practice for handling direct URL access in environments that are optimized for serving static files.

  1. Create a 404.html file with the following content:
404.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script>
sessionStorage.redirect = location.href.split("/").slice(3).join("/");
location.replace("/"); // Or the path to your index.html
</script>
</head>
<body>
Redirecting...
</body>
</html>
  1. In your index.html, add a script to handle the redirect:
index.html
<head>
<script>
if (sessionStorage.redirect) {
const redirect = sessionStorage.redirect;
delete sessionStorage.redirect;
window.history.replaceState(null, null, redirect);
}
</script>
</head>

This way, when GitHub Pages serves the 404.html file, the script redirects to the index.html file, preserving the path. Then, the script in index.html sets the correct URL, and your SPA router can take over from there.

Choosing a Solutionโ€‹

  • Hash-Based Routing: This is the simplest solution but changes your URL structure. It's a good choice if you don't mind having # in your URLs.
  • 404.html Redirect Hack: This maintains your clean URL structure but is a bit of a hack. It works well but understand that it's a workaround rather than a standard solution.

Both methods will allow your Preact SPA to work correctly on GitHub Pages. Choose the one that best fits your needs and the expectations of your users.