Custom 404 Error Page in React App with HashRouting and Spring Boot
Are you challenged with showing a custom 404 error page built in React when your app routing is handled using HashRouter instead of BrowserRouter and you are using spring boot to deploy your app as static SPA?
Then, you are on the right blog looking for the right solution.
First Thought: Why not just use BrowserRouter then?
Not always the available options right? Especially when we prefer:
- Backward compatibility (supporting older browsers)
- Separate server-side & client-side routing
First Instinct: Shouldn’t it be simple? Both React & Spring has routing options?
Let’s understand more in detail the problem statement with some code:
- Simple React setup for 404:
VOILA! For local npm start, this is how the paths will resolve:
http://localhost:3000/# >>> Home Page
http://localhost:3000/#home >>> Home Page
http://localhost:3000/#404 >>> NotFound Page
http://localhost:3000/#xyz >>> NotFound Page
- Spring boot server-side setup:
When we deploy this with spring boot as static content, by copying the build scrips in src/main/resources/public folder and run the bootRun. All the root requests to the server will serve index.html and that in turn serves our react app.
http://localhost:8080 >>> Index.html >>> React Home Page (And our url path will look like this http://localhost:8080/#/)
- Spring boot Whitelabel Error Page:
And any unknown paths will land to spring boot ugly Whitelabel error page.
- Spring boot Error 404 Handling:
Out of multiple options available with spring boot docs, let's try the two most common ones:
- Forward to react 404 page path using ErrorController:
And when we do this, tomcat or any web server doesn’t like handling client-side URL fragments (anything after #) in the RequestDispatcher and it will be discarded.
2020–04–11 12:32:39 [http-nio-3001-exec-6] WARN o.a.c.c.C.[Tomcat].[localhost].[/] — The fragment in dispatch path [/#404] has been removed
So the user will always end up on the root home page for all broken paths.
BAD USER EXPERIENCE, we just swallowed the 404 error. User blinks and all of a sudden they are somewhere they weren’t expecting to be.
2. Create 404.html at Spring expected path src/main/resources/public/error:
For brevity, I kept it a simple page, once can use any view templates. This will take care of showing the custom Not Found error page. But…
BAD TECH STACK, BAD DEVELOPER EXPERIENCE, we just introduced two UI stacks in our code base. Shouldn’t all UI be handled with react-app?
To achieve the right solution, let's talk about improvising the above options:
- Modify WebServer to not ignore client-side URL fragments.❌
This will require somehow overriding server dispatcher behavior and allow client URL fragments to be passed back to the client. Is it safe & possible? What if the client browser discards it then in server response? I don’t know. Sounds a lot of complexity. Let’s rule this out.👎
- Modify 404.html to point to react-app instead somehow.✅
This sounds doable with client redirect, so we will modify our 404.html this way.👍
<meta http-equiv=”refresh” content=”0; url=/#404" />
And VOILA! Mission Accomplished 🍾, now users will see react 404 page for all broken paths. Internally, this is how the paths will be resolved:
http://localhost:8080/badpath >>> 404.html >>> ClientRedirect >>> http://localhost:8080/#404 >>> NotFound Page
I hope you liked ❤️ this article, stay tuned for more posts. All feedback, comments & questions are welcomed. 🏳️🌈
If this helped you reduce time to develop, you can buy me a cup of coffee ☕