v 0.1.49

Client-Side Navigation

The @ecopages/browser-router package enables Single Page Application (SPA) navigation behavior in your Ecopages application. It intercepts link clicks, fetches the next page via fetch, and uses morphdom to efficiently diff and update only the parts of the DOM that changed.

This preserves element state (like audio players, web component internals, or form values) and enables native View Transitions.

Installation

bunx jsr add @ecopages/browser-router

Setup

To enable client-side routing, initialize the router in your global script (e.g., src/includes/html.script.ts).

The simplest approach is to use createRouter, which creates and starts the router in one call:

import { createRouter } from '@ecopages/browser-router/client';
 
const router = createRouter({
  viewTransitions: true,
});

For manual control over when the router starts and stops, use the EcoRouter class directly:

import { EcoRouter } from '@ecopages/browser-router/client';
 
const router = new EcoRouter({
  viewTransitions: true,
});
 
router.start();
 
// Later, if needed:
// router.stop();

Configuration

You can customize the router behavior by passing an options object:

OptionTypeDefaultDescription
linkSelectorstring'a[href]'Selector for links to intercept.
persistAttributestring'data-eco-persist'Attribute to mark elements that should persist across navigations.
reloadAttributestring'data-eco-reload'Attribute (on link or element) to force a full hard reload.
scrollBehavior'top' | 'preserve' | 'auto''top'Controls scroll behavior after navigation.
viewTransitionsbooleanfalseEnables View Transitions API support.
updateHistorybooleantrueWhether to push a new entry to the browser history for each client-side navigation.
smoothScrollbooleantrueWhether to use smooth scrolling when adjusting scroll position after navigation.

Example with Options

const router = createRouter({
  viewTransitions: true,
  scrollBehavior: 'preserve',
  linkSelector: 'a:not([data-no-route])',
});

Features

Persistence

Elements marked with data-eco-persist are never recreated during navigation. morphdom recognizes these elements by their persist ID and skips updating them entirely, preserving their internal state (event listeners, web component state, form values, audio playback, etc.).

Add the data-eco-persist attribute with a unique ID:

<audio 
  controls 
  src="/music.mp3" 
  data-eco-persist="global-player" 
/>

View Transitions

If viewTransitions: true is enabled, Ecopages will trigger a View Transition on navigation. You can customize the animation using standard CSS or the provided optional styles.

Using Included Styles

The package includes default fade and slide animations. Import them in your CSS:

@import '@ecopages/browser-router/styles.css';

Then use the data-eco-transition attribute on elements to apply specific animations:

ValueDescription
slideSlides the element horizontally during transition.
<main data-eco-transition="slide">
  <!-- Content slides in -->
</main>

Lifecycle Events

The router emits lifecycle custom events on the document object, allowing you to hook into the navigation process.

EventDetailDescription
eco:before-swap{ url, direction, newDocument, reload }Fired after fetching new content but before updating the DOM. Use newDocument to inspect upcoming content. Call reload() to force a hard reload.
eco:after-swap{ url, direction }Fired after the DOM has been updated. Useful for re-initializing scripts or analytics.
eco:page-load{ url, direction }Fired on both initial load and subsequent navigations.

Example: Re-initializing Scripts

document.addEventListener('eco:after-swap', () => {
  console.log('Page updated!');
  // Re-run any page-specific logic here
});

Example: Navigation-Aware Components

For components that need to react to URL changes (like updating active states), listen for eco:page-load:

document.addEventListener('eco:page-load', () => {
  // Update active states, re-highlight nav links, etc.
  highlightActiveLink();
});