DEV Community

Max Core
Max Core

Posted on

SvelteKit 2: How to make code-based router instead of file-based router

I've wrote lots about how to make a patch on that, and now, still not an npm module, but at least we have a repo:
https://github.com/webentlib/router
The idea is simple — just create universal [...path] folder and proxy everything through it via some urls.ts.

So, this allows to define routes like that:

urls.ts:

import type { Pattern, Layout, Error } from '/router/types';  // or from '/' in case you use root index.ts

export const error: Error = () => import('/src/error.svelte');
const layout: Layout = { page: () => import('/src/base.svelte'), error };
const layouts: Layout[] = [layout];  // for nested layouts — [layout, sublayout, ...]

export const patterns: Pattern[] = [
    {re: '',                     page: () => import('/src/home.svelte'),              layouts},
    {re: 'articles',             page: () => import('/src/articles/articles.svelte'), layouts},
    {re: 'article/(<id>[0-9]+)', page: () => import('/src/articles/article.svelte'),  layouts},
]
Enter fullscreen mode Exit fullscreen mode

Sure, now all pages/components could be stored in any folder and have any name you want and need.

More of it, one can describe load inside <script module> right in page component like in good old Sapper:

article.svelte:

<script module>
export async function load({ fetch, slugs }) {
    const response = await fetch(`/api/articles/${slugs.id}/`);
    const article = await response.json();
    return { article }
}
</script>
Enter fullscreen mode Exit fullscreen mode

Sure, load in separate .js/ts file also available by specifying js and a side params to execute on:

urls.ts:

import { Sides } from '/router/';  // SERVER | CLIENT | UNIVERSAL
Enter fullscreen mode Exit fullscreen mode
{
    re: '',
    page: () => import('/src/home.svelte'),
    js: () => import('/src/home.js'),
    side: Sides.SERVER,
    layouts,
}
Enter fullscreen mode Exit fullscreen mode

More of it, now, one can define page specific vars just in routes, like:

urls.ts:

{
    re: '',
    page: () => import('/src/home.svelte'),
    js: () => import('/src/home.js'),
    side: Sides.SERVER,
    layouts,
    layout: Layouts.DEFAULT,         // E.g: 'CUSTOM', 'CUSTOM'
    wrapper: Wrappers.NARROW,        // Can be used for css class for <main>
    title: 'Welcome to My project!', // Can be used for <title>
    h1: 'Welcome!',                  // Can be used for <h1>
    name: 'HOME',                    // 'id' of a route
    extras: [EXTRAS.GO_TOP],         // Any additions to use in layout,
}
Enter fullscreen mode Exit fullscreen mode

So it can be used in base layout and page like that:

urls.ts:

export enum Layouts {
    DEFAULT = 'DEFAULT',
    CUSTOM = 'CUSTOM',
    BLANK = 'BLANK',
}

export enum Wrappers {
    WIDE = 'WIDE',
    DEFAULT = 'DEFAULT',
    NARROW = 'NARROW',
}

export enum Extras {
    GO_TOP = 'GO_TOP',
}
Enter fullscreen mode Exit fullscreen mode

base.svelte:

<script>
    // ROUTER
    import { routeStore, titleStore, h1Store, Layouts, Wrappers, Extras, beforeNavigate } from '/';

    let title   = $derived($titleStore || $routeStore.title);
    let h1      = $derived($h1Store || $routeStore.h1);
    let path    = $derived($routeStore.url.pathname + $routeStore.url.search);
    let layout  = $derived($routeStore.layout);
    let wrapper = $derived($routeStore.wrapper);
    let extras  = $derived($routeStore.extras);
    beforeNavigate(() => {
        $titleStore = null;
        $h1Store = null;
    });

    // CUSTOM
    import { GoTop } from '/';
    import Header from '/src/Header.svelte';

    let { children } = $props();
</script>

<svelte:head>
    <title>{title ? title + ' | My Project' : 'My Project'}</title>
</svelte:head>

{#if layout !== Layouts.BLANK}
    <Header/>
{/if}

{#key path}
    {#if layout === Layouts.DEFAULT}
        <main
            class='Wrapper'
            class:_Wide={wrapper === Wrappers.WIDE}
            class:_Default={wrapper === Wrappers.DEFAULT}
            class:_Narrow={wrapper === Wrappers.NARROW}
        >
            {#if h1}
                <div class="Heading">
                    <h1 class="Title">{h1}</h1>
                </div>
            {/if}
            {@render children?.()}
        </main>
    {:else}
        {@render children?.()}
    {/if}
{/key}

{#if extras?.length || extras.includes(Extras.GO_TOP)}
    <GoTop/>
{/if}
Enter fullscreen mode Exit fullscreen mode

article.svelte:

$effect(() => {
    $titleStore = article.name    
})
Enter fullscreen mode Exit fullscreen mode

For installation, usage, etc — please follow:
https://github.com/webentlib/router


Thx for you attention.
If you like the idea, please tell — I'll think about packing that as an npm module.

Top comments (0)