Say Hello to Kitbag Router: A New Era of Vue.js Routing

30 Jun 2024

A brief history of my journey with vue-router

Virtually every developer using Vue.js is currently using or has only ever used Vue-router. Many of those developers, myself included, are mostly happy with it. The biggest complaint I hear is the lack of strongly typed routes, which is usually solved with a bit of Typescript wrapping your routes.

// router/routes.ts
import { RouteLocationRaw, RouteRecordRaw } from 'vue-router'

type RouteFunction = (...args: any[]) => RouteLocationRaw

export const routes = {
  home: () => ({ name: 'home' }) as const,
  club: (clubId: string) => ({ name: 'clubs.view', params: { clubId } }) as const,
  profile: (userId?: string) => ({ name: 'profile.view', query: { userId } }) as const,
} satisfies Record<string, RouteFunction>

Then routes becomes your source of truth, so navigation should always use a route from routes to send proper names to vue-router.

<router-link :to="'club-123')">go to club</router-link>

The syntax of defining routes is a bit cumbersome but honestly works fairly well. However, because I’m a serial starter-of-new-projects and love a challenge, I teamed up with Pleek91 to see if we could build our own.

Cover image for Introducing a new router for Vue

Introducing Kitbag Router

We made the decision to start the project from scratch; solving problems from first principles rather than taking the path of so many others and trying to build on top of the relatively old vue-router project. This means Kitbag Router is NOT a drop-in replacement for projects using vue-router. Therefore, this freedom also means we can have a relentless pursuit of the best possible developer experience, not weighed down by the need to have feature parity with Vue-router.

Let’s take a look at some of Kitbag’s features

First and foremost, it’s type safe!

Kitbag Router uses a createRoutes utility that returns routes with the name, path, and query, all preserved in the Route type. This means the routes you provide to createRouter will maintain type safety when accessing the current route or performing navigation.

example of type safety when using router.push

Type safety in routing means auto-complete when assigning to on <router-link>, the easy discovery of available routes when defining a beforeRouteEnter hook, and ensuring that Typescript will error if the expected params for a route change without updating the router.push() call.

Params are way more powerful

Not only does Kitbag Router know what params are expected for a given route, it can also support param types beyond String.

Kitbag Router ships with built-in support for params of typeString, Number, Boolean, Date, RegExp, JSON, as well as any custom param type you define.

example of how changing param types in routes definition enforces type safety elsewhere

Not only is this type useful when navigating, but it also means a route that expects a number for an “id” param won’t be considered a match if the value provided in the URL doesn’t satisfy the number constraint. Furthermore, your Vue component that gets rendered for the route doesn’t have to worry about whether the param will be present or not or having to convert it from string to the type we know it’s supposed to be.

Support for the query

Params aren’t relegated to only the path. Kitbag Router allows you to configure your expected params in the query the same way you’re used to defining params in other parts of the URL.

Handling rejections

Inevitably when defining your routes, you’ll want to add a “catch-all” or “not found” route. While Kitbag Router fully supports defining plain ol’ Regex in your path/query, it also ships with even better support for handling NotFound. We call this a “rejection”, which is customizable and can be extended to any custom rejection you need, like perhaps NotAuthorized. When a rejection is triggered, usually from within one of your hooks, the Kitbag Router will automatically render the rejection component you assigned for the given rejection type.

And more…

These are the key features we think really make Kitbag Router compelling but there is so much more. Kitbag router is built for Vue3 and supports async components, route hooks, editable params from useRoute, components for RouterLink and RouterView, and more.

Is it ready?

Kitbag Router is still super green and currently maintained by a team of 2. It has not yet had a stable release (on 0.2.0 as of the time I’m writing this), so things are still likely to change. That being said, I hope you’ll give it a try, maybe give it a star. We’re excited to hear from the community on what features we should focus on next.

Happy engineering!