JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 19
  • Score
    100M100P100Q54355F
  • License MIT

Framework-agnostic, lightweight router.

Package Exports

  • rutter

Readme

About

Rutter is a framework-agnostic, lightweight router. Built with URLPattern & History API. Internal reactivity is powered by Signal.

This library doesn't ship polyfill for URLPattern. You may consider installing urlpattern-polyfill.

Usage

VanillaJS

import { CreateHistory } from 'rutter'

const router = new CreateHistory({
  routes: {
    index: {
      pathname: '/'
    },
    about: {
      pathname: '/about'
    },
    blog: {
      pathname: '/blog'
    },
    blogDetail: {
      pathname: '/blog/:id'
    }
  }
})

router.on('index') // boolean
router.onOneOf(['index', 'about']) // boolean

React bindings: via useState

// router.(ts|js)

import { CreateHistory } from 'rutter'
import { useEffect, useState } from 'react'

export const {
  redirect,
  on,
  summaryState,
  routeState,
  watchSummaryState,
  watchRouteState
} = new CreateHistory({
  routes: {
    index: {
      pathname: '/'
    },
    about: {
      pathname: '/about'
    },
    blog: {
      pathname: '/blog'
    },
    blogDetail: {
      pathname: '/blog/:id'
    }
  }
})

export const useRouterState = () => {
  const [state, setState] = useState(summaryState)

  useEffect(() => watchSummaryState(setState), [])

  return state
}

export const useRoute = () => {
  const [state, setState] = useState(routeState)

  useEffect(() => watchRouteState(setState), [])

  return state
}
// app.(tsx|jsx)

import { FC } from 'react'

import { on, redirect, useRoute } from './router'

const App: FC = () => {
  const { is404, ...restStates } = useRoute()

  return (
    <>
      <nav>
        <button onClick={() => redirect('index')}>Index</button>

        <button onClick={() => redirect('blog')}>Blog</button>

        <a href="/invalid-url">
          <button>404</button>
        </a>
      </nav>

      <fieldset>
        <legend>Body:</legend>

        <div>
          {is404 ? (
            <h1>404 Page</h1>
          ) : (
            <>
              {on('index') && <h1>Index Page</h1>}

              {on('about') && <h1>About Page</h1>}

              {on('blog') && (
                <>
                  <h1>Blog Page</h1>

                  <button
                    onClick={() =>
                      redirect('blogDetail', {
                        params: {
                          id: 123
                        }
                      })
                    }
                  >
                    Blog Detail
                  </button>
                </>
              )}

              {on('blogDetail') && <h1>Blog Detail Page</h1>}
            </>
          )}
        </div>
      </fieldset>

      <fieldset>
        <legend>Current route detail:</legend>

        <code>
          <pre>{JSON.stringify(restStates, null, 2)}</pre>
        </code>
      </fieldset>
    </>
  )
}

Svelte bindings: via readable/derived

// router.(ts|js)

import { readable, derived } from 'svelte/store'
import { CreateHistory } from 'rutter'
import { mapValues } from 'lodash-es'

const router = new CreateHistory({
  routes: {
    index: {
      pathname: '/'
    },
    about: {
      pathname: '/about'
    },
    blog: {
      pathname: '/blog'
    },
    blogDetail: {
      pathname: '/blog/:id'
    }
  }
})

const { summaryState, routeState, watchSummaryState, watchRouteState } = router

export const { redirect, on, onOneOf } = router

export const route = readable(routeState, watchRouteState)
export const routerState = readable(summaryState, watchSummaryState)

export const matches = derived(routerState, ({ details }) =>
  mapValues(details, (_, name) => on(name as keyof typeof details))
)
<script lang="ts">
  // app.svelte

  import { redirect, route, matches } from './router'

  $: ({ is404, ...restState } = $route)
  $: data = JSON.stringify(restState, null, 2)
</script>

<nav>
  <button on:click={() => redirect('index')}>Index</button>

  <button on:click={() => redirect('blog')}>Blog</button>

  <a href="/invalid-url">
    <button>404</button>
  </a>
</nav>

<fieldset>
  <legend>Body:</legend>

  <div>
    {#if is404}
      <h1>404 Page</h1>
    {:else}
      {#if $matches.index}
        <h1>Index Page</h1>
      {/if}

      {#if $matches.about}
        <h1>About Page</h1>
      {/if}

      {#if $matches.blog}
        <h1>Blog Page</h1>

        <button
          on:click={() => redirect('blogDetail', { params: { id: 123 } })}
        >
          Blog Detail
        </button>
      {/if}

      {#if $matches.blogDetail}
        <h1>Blog Detail Page</h1>
      {/if}
    {/if}
  </div>
</fieldset>

<fieldset>
  <legend>Current route detail:</legend>

  <code>
    <pre>{data}</pre>
  </code>
</fieldset>

Documentation

Type API: https://paka.dev/npm/rutter/api

Development

pnpm i
pnpm dev