Skip to content

AegisJSProject/atlas

@aegisjsproject/atlas

A client-side router library using Navigation & URLPattern

CodeQL Node CI Lint Code Base

GitHub license GitHub last commit GitHub release GitHub Sponsors

npm node-current npm bundle size gzipped npm

GitHub followers GitHub forks GitHub stars Twitter Follow

Donate using Liberapay


Overview

This router intercepts same-origin navigations and resolves them to registered route modules. Each module can return content in multiple native formats (e.g. Response, Document, Element), allowing flexibility without imposing rendering constraints.

Key characteristics:

  • Native Navigation API (navigation)
  • Route-to-module mapping via dynamic import()
  • Direct DOM updates (no diffing layer)
  • Supports HTML streaming via Response
  • Built-in metadata handling (title, description, styles)
  • Optional preload observation
  • Abort-safe lifecycle with AbortController and DisposableStack

Important

This requires the Navigation API, which is Baseline 2026. It also creates a Trusted Types Policy, where supported, labeled "aegis-atlas#html" for handling HTML responses without sanitizer restrictions.

Tip

Route module specifiers can use bare specifiers like @acme/blog. These can be resolved via an import map, for example:

<script type="importmap">
{
  "imports": {
    "@acme/blog": "https://cdn.example.com/acme-blog/index.js"
  }
}
</script>

This allows modules to be loaded from a CDN without changing route definitions.


Installation

This module is intended to be used directly in modern browser environments.

import { init } from '@aegisjsproject/atlas';

No dependencies required.


Core Concepts

Route Modules

Each route resolves to a module with the following shape:

export default async function handler(request, context) {
	return new Response('<h1>Hello</h1>', {
		headers: { 'Content-Type': 'text/html' }
	});
}

export const title = 'Page Title';

export const description = 'Page description';

export const styles = new CSSStyleSheet();

Supported Exports

  • default (required)
    • Function: (Request, RouteContextObject) => HandlerResult
    • Or static value: HandlerResult
  • title (optional)
  • description (optional)
  • styles (optional: CSSStyleSheet or array)

Handler Return Types

Handlers may return:

  • Response (must be text/html)
  • HTMLDocument
  • Element
  • DocumentFragment
  • URL (triggers navigation)

Route Context

Each handler receives a context object:

{

	result,        // URLPatternResult
	params,        // extracted route params
	stack,         // DisposableStack
	controller,    // AbortController
	signal,        // AbortSignal
	type,          // navigation type
	url,           // URL instance
	state,         // navigation state
	info,          // navigation info
	timestamp      // performance timestamp
}

Usage

Initialize Router

init({
	'/': '/routes/home.js',
	'/users/:id': '/routes/user.js'
	'/posts/:year(\\d{4})/:month(\\d{2})/:day(\\d{2})/:slug': '@acme/blog',
	'/product/:sku': '@acme/store/product',
}, {
	root: 'app',
	preload: true
});

Options

  • root: Element or element ID where content is rendered
  • preload: Enable preload observation
  • signal: Optional AbortSignal for teardown

Navigation Helpers

import { navigate, back, forward, reload } from './router.js';

navigate('/about');
back();
forward();
reload();

Navigation Lifecycle

Wait for navigation completion:

import { whenLoaded } from './router.js';

await whenLoaded();

Behavior Details

Interception Rules

Navigation is intercepted only if:

  • event.canIntercept is true
  • URL is same-origin
  • Triggering element does not have .no-router

Content Handling

Response

  • Must be text/html
  • Parsed via Document.parseHTMLUnsafe
  • Re-processed as HTMLDocument

HTMLDocument

  • Updates:
    • document.title
    • meta description
    • root content

Element / DocumentFragment

  • Directly replaces root children

URL

  • Triggers navigation

Root Management

setRoot('app');

// or

setRoot(document.getElementById('app'));

If root is <body>, full body is replaced.

If root is an element with id, only matching subtree is replaced.


Metadata Updates

Route modules can define:

  • title → updates document.title
  • description → updates all matching meta tags:
    • name="description"
    • og:description
    • twitter:description
  • styles → appended to document.adoptedStyleSheets

Form Handling

  • Automatically determines method (GET or POST)
  • Submits FormData when applicable
  • Uses Request API for consistency

Abort + Cleanup

Each navigation:

  • Uses AbortController
  • Combines signals via AbortSignal.any
  • Cleans up via DisposableStack

Handlers should respect context.signal where applicable.


Trusted Types

If supported, a Trusted Types policy is used to safely pass HTML into:

Document.parseHTMLUnsafe(...)

This ensures CSP compatibility without stripping critical markup like:

  • <iframe>
  • inline event handlers
  • form attributes

Example Route

export default async function(request, { params }) {
	return new Response(`
		<h1>User ${params.id}</h1>
	`, {
		headers: { 'Content-Type': 'text/html' }
	});
}

export const title = 'User Profile';
export const description = 'User details page';

Notes

  • Only text/html responses are supported for Response
  • Non-matching routes fall back to fetch()
  • Errors during routing are surfaced via reportError
  • Designed for modern browsers with Navigation API support

Summary

This router provides a low-level, high-control alternative to framework routers by:

  • Eliminating abstraction layers
  • Leveraging native platform APIs
  • Supporting flexible content types
  • Maintaining strict control over navigation lifecycle

Intended for environments where performance, control, and minimal overhead are priorities.