npm.io
3.14.601 • Published 2 weeks ago

@symbo.ls/router

Licence
CC-BY-NC-4.0
Version
3.14.601
Deps
2
Size
38 kB
Vulns
0
Weekly
0

@domql/router

Client-side router plugin for DOMQL. Handles route matching, navigation, scroll management, and state updates within DOMQL elements.

Install

npm install @domql/router

Basic Usage

import { router } from '@domql/router'

// Navigate to a path
router('/about', element)

// With state and options
router('/dashboard', element, { userId: 1 }, { scrollToTop: true })

Define routes on your DOMQL element:

const App = {
  routes: {
    '/': HomePage,
    '/about': AboutPage,
    '/contact': ContactPage,
    '/*': NotFoundPage
  }
}

Dynamic Route Params

Match routes with :param segments. Enable with useParamsMatching: true.

const App = {
  routes: {
    '/': HomePage,
    '/:id': UserPage,
    '/:category/:slug': ArticlePage,
    '/*': NotFoundPage
  }
}

router('/users/42', element, {}, { useParamsMatching: true })
// state.params = { id: '42' }

router('/tech/my-article', element, {}, { useParamsMatching: true })
// state.params = { category: 'tech', slug: 'my-article' }

Exact segments score higher than params, so /about will match a literal /about route before /:id.

Query String Parsing

Query parameters are automatically parsed and stored in state.

router('/search?q=hello&tag=a&tag=b', element)
// state.query = { q: 'hello', tag: ['a', 'b'] }

Duplicate keys are collected into arrays.

Guards / Middleware

Run async guard functions before navigation. Return true to allow, false to block, or a string to redirect.

const authGuard = ({ element }) => {
  if (!element.state.root.isLoggedIn) return '/login'
  return true
}

const roleGuard = ({ params }) => {
  if (params.section === 'admin') return false
  return true
}

router('/dashboard', element, {}, {
  guards: [authGuard, roleGuard]
})

Guard functions receive a context object:

{
  pathname,  // full pathname
  route,     // matched route key
  params,    // dynamic route params
  query,     // parsed query string
  hash,      // URL hash
  element,   // DOMQL element
  state      // navigation state
}

404 Handling

Provide an onNotFound callback for unmatched routes:

router('/unknown', element, {}, {
  onNotFound: ({ pathname, route, element }) => {
    console.warn(`No route found for ${pathname}`)
  }
})

You can also define a /* wildcard route as a catch-all fallback.

Custom Router Element

Use customRouterElement in your config.js to route pages into a specific element within the component tree instead of the root. This is useful for persistent layouts where only the content area changes between routes.

// symbols/config.js
export default {
  router: {
    customRouterElement: 'Folder.Content'
  }
}

The value is a dot-separated path resolved from the root element. For example, 'Folder.Content' means the router will find root.Folder.Content and render page content inside it.

This allows you to define a layout once in the / (main) page and have sub-pages render inside a specific container:

// pages/main.js — defines the persistent layout
export const main = {
  extends: 'Layout',
  Folder: {
    Content: {
      // default content for '/' route
    }
  }
}

// pages/team.js — only defines content (rendered inside Folder.Content)
export const team = {
  state: 'team',
  extends: 'Grid',
  childExtends: 'TeamItem',
  childrenAs: 'state',
  children: (el, s) => s.data,
}

Options

Option Type Default Description
level number 0 Route nesting level (which path segment to match)
pushState boolean true Push to browser history
initialRender boolean false Whether this is the initial page render
scrollToTop boolean true Scroll to top after navigation
scrollToNode boolean false Scroll within the element node
scrollNode Element document.documentElement Node to scroll
scrollToOffset number 0 Offset when scrolling to hash anchors
scrollToOptions object { behavior: 'smooth' } Options passed to scrollTo()
useFragment boolean false Use fragment tag for content
updateState boolean true Update element state on navigation
contentElementKey string 'content' Key for the content element slot
removeOldElement boolean false Remove old content element before setting new
useParamsMatching boolean false Enable dynamic :param route matching
guards function[] undefined Array of guard/middleware functions
onNotFound function undefined Callback when no route matches

Exported Utilities

getActiveRoute(level, route)

Returns the active route segment at the given nesting level.

import { getActiveRoute } from '@domql/router'

getActiveRoute(0, '/users/42')  // '/users'
getActiveRoute(1, '/users/42')  // '/42'
parseQuery(search)

Parses a query string into an object.

import { parseQuery } from '@domql/router'

parseQuery('?page=1&sort=name')  // { page: '1', sort: 'name' }
matchRoute(pathname, routes, level)

Matches a pathname against a routes object. Returns { key, content, params }.

import { matchRoute } from '@domql/router'

const routes = { '/': Home, '/:id': Detail, '/*': NotFound }
const result = matchRoute('/42', routes)
// { key: '/:id', content: Detail, params: { id: '42' } }
parseRoutePattern(pattern)

Parses a route pattern string into segments, param definitions, and wildcard flag. Results are cached.

runGuards(guards, context)

Runs an array of async guard functions sequentially. Returns true, false, or a redirect path string.

Events

The router triggers an onRouteChanged event on the element after navigation completes. Listen for it in your element definition:

const App = {
  routes: { ... },
  onRouteChanged: (element, options) => {
    console.log('Route changed:', element.state.route)
  }
}

License

MIT