Building Client Side Routing without using React.Js

Building Client Side Routing without using React.Js


Rahul Kumar (@rahul)

Discover the inner workings of Single Page Application (SPA) navigation without React, dynamic content rendering without full reloading of page, browser history api uses and more...


Single Page Application(SPA) uses client side routing to control the navigation. It allows them to render only few part of the page instead of doing full page reload. Mostly people have seen this feature in SPA frameworks, like React, Angular, etc. In this blog, we'll explore how they does SPA navigation. 

Initial Setup

Our initial setup of this tutorial has two files, one is index.html and another is index.js


      <html lang="en">
    <title>@dsabyte - CSR</title>
    <script src="index.js"></script>
    <h1>@dsabyte - CSR</h1>
      <li><a href="/">Home</a></li>
      <li><a href="/about">About</a></li>
      <li><a href="/user/rahul">Rahul Kumar</a></li>
      <li><a href="/user/shani">Shani Kumar</a></li>

    <div id="container">
      <p>Welcome to the @dsabyte CSR demo...</p>



      window.addEventListener("DOMContentLoaded", () => {
  console.log("CSR Page Loaded!");


There are a few links on the HTML page. You will be redirected to a 404 page If you click on any one of the links, 

404 redirection will not work on codepen. You should try this on your local server. You can try this live server extension  to run on local host. 

Problem Statement

We want to render different pages/content every time any link is clicked. But the twist is, that we can not request the server to send a new page/content every time a link is clicked. 

Let's start by creating a basic router


      const router = {
  init() {},
  go(route) {},


We have created a router template that has two methods, init and go. init method will initialise the client-side routing, Let's write init method. 

writing init()

      const router = {
  init() {
    const links = document.querySelectorAll("a");
    Array.from(links).forEach((link) => {
      link.addEventListener("click", (e) => {
        const route = link.getAttribute("href");
        console.log("Route clicked: ", route);
  go(route) {},

window.addEventListener("DOMContentLoaded", () => {


init() method will get all links available on the page. It will change the default behaviour by calling the preventDefault() method. This is required otherwise the browser will redirect the user to a new page by default. 

Now, we can intercept clicks to the links and we have the URL that has been clicked. It's time to render different content based on the clicked URL. 

Rendering new content on click

Whenever the user clicks on a new URL, we'll get the clicked URL. Now we have to change the content of the page according to the new URL. Let's modify the go() method to change page content based on URL. 


      go(route) {
    console.log("go", route);
    const container = document.getElementById("container");

    switch (route) {
      case "/":
        container.innerHTML = "<h1>Home Page</h1>";
      case "/about":
        container.innerHTML = "<h1>About Page</h1>";
      case "/user/rahul":
        container.innerHTML = "<h1>Hello, i am Rahul</h1>";
      case "/user/shani":
        container.innerHTML = "<h1>Hello, i am Shani</h1>";
        console.log("404 Page");

The path in the browser address bar does not change even if we click on any URL. It causes several problems for example, no feedback is given to visitors, the URL can not be shared, the URL can not be bookmarked etc.

Let's create a function that can change the path in the address bar. 


      function pushStateToNavigationHistory(route) {
  history.pushState({ route }, "", route);

You can learn more about pushState here. pushState method will add a new entry into the browser session history stack. 

Change the go method to call pushStateToNavigationHistory upon any url clicked. 

        go(route) {
    console.log("go", route);
    // ...... other things....

Now, you can see that the path in the browser address bar is being changed. But, now we have another problem to solve. 

Suppose initially we were at http://localhost:3000 and we clicked on a link which redirected us to http://localhost:3000/user/rahul. So far everything is fine. But as soon as you refresh the page, you'll get 404 indicating there is no such page. 

The initial request is always made to the server. Once the page is loaded we can apply client-side routing.  Currently, our server has only one resource http://localhost:3000. We need to modify our server to send the same HTML page on any request. Here is a simple node server that serve the same page on any request. 


      const express = require("express");
const path = require("path");

const app = express();
const port = 3000;

// Serve static files from the 'public' directory

// Route for serving the HTML file
app.get("*", (req, res, next) => {
  // Check if requested URL does not end with .js extension
  // /user/rahul
  if (!req.url.endsWith(".js")) {
    return res.sendFile(path.join(__dirname, "index.html"));
  // If the requested URL ends with .js extension, pass the request to the next middleware

// Start the server
app.listen(port, () => {
  console.log(`Server is running on http://localhost:${port}`);


Instructions to execute server code will be shared at the end. 

You need nodejs to be installed on your system. If you don’t have nodejs then use the following references to install it

Load http://localhost:3000 after starting the server. Now, you can navigate to any route and even you can refresh any route. 

But we have another problem...

When you refresh the other than home route, you'll get the see the same content as of home route. We need to render content dynamically based on the page path after loading the page. 

Modify the DOMContentLoaded listener as follows to render content based on the current path. router.go(window.location.pathname) will call router.go method with the current path. router.go method will check the path and render the content based on that path. 

DOMContentLoaded event

      window.addEventListener("DOMContentLoaded", () => {

You'll be able to load the right content for current URL after making this change. But still there is an issue. You can only navigate forward, there is no way to render the previous page content by using the browser back arrow button. 

popstate is an event that gets triggered when we click on the back button in the browser. It also gives the state which was used while pushing into history. We can use that state to go back to the previous URL as follows. 

Going Back

      window.addEventListener("popstate", function (event) {
  const state = event.state;


Now you can easily navigate back and forth. You can read more about the popstate event here


Clone repository 

      git clone

Install NodeJs

Install Packages

      cd client-side-routing
npm i

Starting Server

      node server.js

Once the server is started you can visit http://localhost:3000 and happy navigation. 

Related Blogs

If you liked this blog, then please star this repository

Add a thoughtful comment...

✨ Explore more tech insights and coding wonders with @dsabyte! Your journey in innovation has just begun. Keep learning, keep sharing, and let's continue to code a brighter future together. Happy exploring! 🚀❤️

  • #javascript
  • #reactjs
  • #spa
  • #client-side
  • #routing

Subscribe to our newsletter.

Join the "News Later" community by entering your email. It's quick, it's easy, and it's your key to unlocking future tech revelations.

Weekly Updates

Every week, we curate and deliver a collection of articles, blogs and chapters directly to your inbox. Stay informed, stay inspired, and stay ahead in the fast-paced world of technology.

No spam

Rest assured, we won't clutter your inbox with unnecessary emails. No spam, only meaningful insights, and valuable content designed to elevate your tech experience.

© 2024 @dsabyte. All rights reserved