DEV Community

Cover image for Building Dynamic Routing for My JavaScript Framework (Levelo JS 2.2.0) β€” One Last Test Before Bed Changed Everything πŸš€
Saiful Siam
Saiful Siam

Posted on

Building Dynamic Routing for My JavaScript Framework (Levelo JS 2.2.0) β€” One Last Test Before Bed Changed Everything πŸš€

Building Dynamic Routing for My JavaScript Framework (Levelo JS 2.2.0) β€” One Last Test Before Bed Changed Everything πŸš€

I'm a student who enjoys building things from scratch.

For the past few months, I've been researching and building my own JavaScript framework called Levelo JS. Every feature I add teaches me something new about how modern frameworks work internally.

Today I'm releasing Levelo JS v2.2.0, a feature-focused update that introduces dynamic routing, a new params API, ref support, and several improvements to the routing system.


It Worked... Until I Tested It πŸ˜…

After hours of work, I finally finished implementing dynamic routing.

I added route pattern parsing.

I added parameter extraction.

I connected everything together.

Everything looked correct.

It was already late at night, and before going to sleep I thought,

"Let me test it one last time."

That one decision completely changed my evening.

The router simply refused to work.

No dynamic routes.

No page switching.

Nothing.


The Bug I Couldn't Stop Thinking About

Since Levelo JS is a Single Page Application framework, page navigation happens without reloading the browser.

When I tested the new router, something felt completely wrong.

At first, I thought the bug was somewhere inside my route matching logic.

So I checked the regular expressions.

I checked parameter parsing.

I checked the navigation flow.

Everything looked fine.

But the router still wasn't working.

It was already late, so I decided to stop for the night and get some sleep.

Even then, the bug stayed in the back of my mind.


The Next Morning

The next morning, I opened my editor almost immediately.

Instead of writing new code, I started reading my existing implementation.

Then I suddenly remembered something.

I had actually faced a very similar problem before...

...and somehow completely forgot about it. πŸ˜‚

Sometimes your own old code becomes the best documentation.


The Real Problem

The router wasn't the problem.

The route matching wasn't the problem.

The History API wasn't the problem.

The real issue was how I was updating the DOM.

Instead of trying to reuse the existing rendered page, I discovered that removing the old page node completely and creating a fresh one worked much better for Levelo JS.

The change itself was surprisingly small:

container.removeChild(currentRenderedNode);
Enter fullscreen mode Exit fullscreen mode

After removing the previous page, the framework creates a new component instance and mounts it again.

That simple architectural change fixed the routing issue.

Looking back, solving it the next morning taught me an important lesson about how my framework should manage DOM updates.


Dynamic Routing is Finally Here πŸŽ‰

With that solved, Levelo JS now supports routes like this:

<Page path="/user/:id" component={UserPage} />
Enter fullscreen mode Exit fullscreen mode

You can access route parameters directly:

import { params } from "levelojs";

console.log(params.id);
Enter fullscreen mode Exit fullscreen mode

Internally, Levelo JS now:

  • Parses route patterns into optimized regular expressions.
  • Extracts dynamic parameters automatically.
  • Updates pages without refreshing the browser.
  • Integrates with the History API for SPA navigation.

A Cleaner Router Architecture

While implementing dynamic routing, I also decided to reorganize the routing system.

Previously, most of the routing logic lived inside a single runtime file.

Now it's separated into dedicated modules:

src/router/
β”œβ”€β”€ Page.ts
β”œβ”€β”€ Pages.ts
└── params.ts
Enter fullscreen mode Exit fullscreen mode

This structure is much easier to maintain and gives me a better foundation for future routing features.


A New params API

Since dynamic routes are now supported, I wanted accessing route parameters to feel simple.

That's why Levelo JS now officially exports a params API, making it easy to read URL parameters anywhere they're needed.


ref Support

After finishing the routing system, I thought,

"Why not add ref support as well?"

Fortunately, this feature was much smoother to implement.

Levelo JS now supports:

  • Object refs
  • Callback refs

along with proper TypeScript typings for native DOM elements.


Better SPA Title Handling

This update also fixes a small but important SPA issue.

Previously, if a page didn't define its own head() configuration, the previous page title could remain visible.

Now Levelo JS automatically restores the default document title whenever necessary, making navigation behave much more naturally.


What I Learned

Building a framework isn't only about adding features.

It's also about questioning your own assumptions.

This time, I was convinced the router itself was broken.

In reality, the routing algorithm was working.

The regular expressions were working.

The parameter extraction was working.

The actual problem was the DOM update strategy.

Sometimes the hardest bugs aren't caused by complicated codeβ€”they're caused by a small architectural decision hiding in plain sight.


Still Learning

One thing I'd like to mention is that I'm still a student.

Levelo JS isn't a commercial frameworkβ€”it's a research project that I build to understand how modern JavaScript frameworks work internally.

Every new feature, every bug, and every refactor teaches me something new.

Version 2.2.0 is another step in that learning journey, and I'm excited to keep improving it.

If you're interested in framework development or enjoy seeing how things work under the hood, I'd love to hear your feedback.

Thanks for reading! ❀️

Top comments (0)