DEV Community

Kokomi
Kokomi

Posted on • Edited on

The link generator creates paths in a type-safe way

Next, SvelteKit, Astro, Nuxt, and common web frameworks utilize directory-based routing.

Unfortunately, directory-based routing becomes harder to maintain as routes increase and is not suitable for creating complex routes.

The LinkGenerator I created partially solves this issue by centrally managing the paths (or links) used in web applications.

Here, I'll briefly explain routing with SvelteKit.

First, let's take a look at a static route.

To create an about page, follow these steps:

  1. Create a directory with the route's name inside the routes directory:

    src/routes/about
    
  2. Inside the about directory, create a +page.svelte file:

    src/routes/about/+page.svelte
    
  3. Write the content in the +page.svelte file:

    <h1>About</h1>
    

This will display the About page when you access /about.

Next, let's create a dynamic page.
Here, we'll create a route like /posts/:postid.

  1. Create the route, and make sure to enclose the directory name for path parameters in [ and ]:

    src/routes/posts/[postid]
    
  2. Create the page file:

    src/routes/posts/[postid]/+page.svelte
    

This setup allows you to retrieve the value of the path parameter from the load function in the +page.ts or +page.server.ts files within the src/routes/posts/[postid] directory. Don't worry too much about the differences between these files.

// src/routes/posts/[postid]/+page.ts
export const load = (routeContext) => {
  const postid = routeContext.params.postid;
  return { postid };
};
Enter fullscreen mode Exit fullscreen mode

The value returned from the load function can be retrieved in the +page.svelte file as shown below:

// src/routes/posts/[postid]/+page.svelte
<script lang="ts">
   export let data;
</script>

<h1>Postid: {data.postid}</h1>
Enter fullscreen mode Exit fullscreen mode

As a result, when you access src/routes/posts/1, it will display Postid: 1.

Directory-based routing may seem simple, but we need to create many directories and files to achieve straightforward tasks.

As the number of routes increases, the directory structure becomes more complex, and it gradually becomes difficult to grasp the entire routing structure.

Every time you want to access a specific route on each page, you must check the structure of the routes directory.

How can we solve this problem elegantly?

Should we create a new router that is not directory-based? While this would be a great idea, it would take a long time to develop, and integrating the router with the framework is not easy.

Here’s the conclusion I came to:

How about creating just one function that generates links completely type-safe?

What if we represent the routes as a single object and centrally generate links based on that? While it may not guarantee that the defined object perfectly matches the directory structure, if we can centrally manage links, I no longer have to spend time opening and closing directories. Additionally, when routes are changed, TypeScript types can check the impact across the entire project.

Let me introduce how to use the LinkGenerator I created.

This package is published on JSR, a JavaScript package registry operated by Deno. It can be easily installed using Deno, NPM, PNPM, Yarn, or Bun.

Here, we'll use NPM. Open your project directory and run the following command:

npx jsr add @kokomi/link-generator
Enter fullscreen mode Exit fullscreen mode

Usage is very simple. Refer to the following code:

// 1. import modules
import { type RouteConfig, flatten_route_config, create_link_generator } from "@kokomi/link-generator";

// 2. define route config
const routeConfig = {
  posts: {
    path: "/posts",
    children: {
      post: {
        path: "/:postid"
      }
    }
  }
} as const satisfies RouteConfig;

// 3. flatten route config
const flat_route_config = flatten_route_config(routeConfig);

// 4. create link function
export const link = create_link_generator(flat_route_config);

link("posts/post", { postid: "1" }); // => /posts/1
Enter fullscreen mode Exit fullscreen mode

An example of usage in Svelte would look like this:

<script>
  import link from "./path_to_link_function";
</script>

<a href={link('posts/post', { postid: '1' })}>Goto post1</a>
Enter fullscreen mode Exit fullscreen mode

It also strictly types the parameters and supports query parameters.

For more details, check out the link-generator repository on GitHub.

Thank you for reading.

Top comments (0)