Serving SPAs from Starlette

I’m using Starlette for a pet project because it’s a modern Python framework that has GraphQL support. I had trouble integrating it with a Single Page App, but I found an easy fix.

My client-side app gets built in the build/ directory, so just adding StaticFiles gets you off the ground:

route = [
    Mount("", app=StaticFiles(directory="build", html=True)),

The History API makes it look like your URL is changing, but if you ever reload, you’ll get a 404. How can we get all URLs to route to the root index.html?

class SpaStaticFiles(StaticFiles):
    Staticfiles for single-page-apps

    Wraps the base `lookup_path` to fallback to the root `index.html` so
    the JavaScript router takes over.

    async def lookup_path(self, path):
        full_path, stat_result = await super().lookup_path(path)
        if stat_result is None:
            return await super().lookup_path("./index.html")

        return full_path, stat_result

Now you just have to change the route to use our version of StaticFiles:

route = [
    Mount("", app=SpaStaticFiles(directory="build", html=True)),


Starlette’s static files docs:

Leave a Reply

Your email address will not be published. Required fields are marked *