Route Dispatcher
The Route Dispatcher is the core routing system in SSE that maps URL paths to Page classes and manages the execution lifecycle.
Overview
The RouteDispatcher handles:
Matching URL paths to Page classes
Managing Site-level code execution
Coordinating the setup/execution lifecycle
Supporting wildcard routes for CMS collections
Basic Usage (v2.0+)
Creating the Dispatcher
In src/routes.ts, create and configure your RouteDispatcher:
import { RouteDispatcher, getAllPages } from "@sygnal/sse-core";
import { Site } from "./site";
// Import pages to trigger @page decorator registration
import "./pages/home";
import "./pages/about";
import "./pages/blog";
export const routeDispatcher = (): RouteDispatcher => {
const dispatcher = new RouteDispatcher(Site);
dispatcher.routes = getAllPages(); // Auto-populated from @page decorators
return dispatcher;
}Using the Dispatcher
In src/index.ts, create the dispatcher once and reuse it:
import { routeDispatcher } from './routes';
// Create dispatcher once
const dispatcher = routeDispatcher();
// Setup phase (runs in <head>)
dispatcher.setupRoute();
// Execution phase (runs after DOM ready)
window.Webflow ||= [];
window.Webflow.push(() => {
dispatcher.execRoute();
});Critical: The dispatcher must be created once and reused. Creating multiple instances causes data loss between setup and execution phases.
Route Mapping
Automatic Route Discovery (Recommended)
New in v2.0: Use the @page decorator for automatic route registration:
import { PageBase, page } from '@sygnal/sse-core';
@page('/')
export class HomePage extends PageBase {
protected async onLoad(): Promise<void> {
console.log('This is the homepage.');
}
}
@page('/about')
export class AboutPage extends PageBase {
protected async onLoad(): Promise<void> {
console.log('This is the about page.');
}
}
@page('/blog/*')
export class BlogPage extends PageBase {
protected async onLoad(): Promise<void> {
console.log('This is a blog post.');
console.log('Item slug:', this.pageInfo.itemSlug);
}
}Then import these pages in routes.ts to register them automatically.
Manual Route Registration (Legacy)
You can still manually define routes if needed:
import { RouteDispatcher } from "@sygnal/sse-core";
import { Site } from "./site";
import { HomePage } from "./pages/home";
import { AboutPage } from "./pages/about";
import { BlogPage } from "./pages/blog";
export const routeDispatcher = (): RouteDispatcher => {
const dispatcher = new RouteDispatcher(Site);
dispatcher.routes = {
'/': HomePage,
'/about': AboutPage,
'/blog/*': BlogPage,
};
return dispatcher;
}Wildcard Routes
Wildcard paths use a trailing /* to match dynamic segments:
@page('/blog/*')
export class BlogPage extends PageBase {
protected async onLoad(): Promise<void> {
// Matches: /blog/post-1, /blog/post-2, /blog/any-slug
console.log('Current slug:', this.pageInfo.itemSlug);
}
}
@page('/products/*')
export class ProductPage extends PageBase {
protected async onLoad(): Promise<void> {
// Matches: /products/item-a, /products/item-b
console.log('Product slug:', this.pageInfo.itemSlug);
}
}Wildcard Behavior:
/blog/*matches/blog/post-1but NOT/blogitselfTo match both, use two decorators:
@page('/blog') @page('/blog/*') export class BlogPage extends PageBase { }
Execution Lifecycle
The RouteDispatcher manages a two-phase lifecycle:
Phase 1: Setup (Synchronous)
Runs during <head> load via dispatcher.setupRoute():
Site's
setup()method executesMatched Page's
onPrepare()method executesMatched Components'
onPrepare()methods execute
// This runs in <head> before DOM is fully loaded
dispatcher.setupRoute();Phase 2: Execution (Asynchronous)
Runs after DOM ready via dispatcher.execRoute():
Site's
exec()method executesMatched Page's
onLoad()method executesMatched Components'
onLoad()methods execute
// This runs after DOM is ready
window.Webflow ||= [];
window.Webflow.push(() => {
dispatcher.execRoute();
});Instance Persistence (Critical Fix)
v2.0.0 Critical Fix: RouteDispatcher must be created once and reused to prevent data loss.
Wrong Approach (Data Loss)
// ❌ BROKEN - Creates two different instances
routeDispatcher().setupRoute(); // Instance A stores data
routeDispatcher().execRoute(); // Instance B has no data - LOST!This creates two separate RouteDispatcher instances. Any data stored during setupRoute() is lost because execRoute() runs on a different instance.
Correct Approach (Data Preserved)
// ✅ CORRECT - Single instance preserves data
const dispatcher = routeDispatcher();
dispatcher.setupRoute(); // Instance stores data
dispatcher.execRoute(); // SAME instance has the dataThis ensures the same RouteDispatcher instance is used for both phases, preserving all data.
Route Matching Logic
The dispatcher matches routes in the following order:
Exact matches first:
/aboutmatches before/about/*Wildcard matches second:
/blog/*matches/blog/post-1No match: No page code executes (Site code still runs)
Example:
// routes.ts
dispatcher.routes = {
'/': HomePage, // Exact: /
'/blog': BlogIndexPage, // Exact: /blog
'/blog/*': BlogPostPage, // Wildcard: /blog/anything
};
// URL: / → HomePage
// URL: /blog → BlogIndexPage
// URL: /blog/my-post → BlogPostPage
// URL: /about → No page code (only Site)Accessing Route Information
When using PageBase, route information is automatically available:
@page('/blog/*')
export class BlogPage extends PageBase {
protected async onLoad(): Promise<void> {
console.log('Path:', this.pageInfo.path); // /blog/my-post
console.log('Page ID:', this.pageInfo.pageId); // Webflow page ID
console.log('Collection:', this.pageInfo.collectionId); // CMS collection
console.log('Item Slug:', this.pageInfo.itemSlug); // my-post
}
}Multiple Routes Per Page
Use multiple @page decorators to handle multiple routes with one class:
@page('/about')
@page('/about-us')
@page('/team')
export class AboutPage extends PageBase {
protected async onLoad(): Promise<void> {
// Check which route was accessed
if (this.pageInfo.path === '/team') {
this.showTeamSection();
}
}
}Best Practices
Create dispatcher once - Store in a variable and reuse for both setup and exec
Use @page decorator - Simplifies route registration
Import all pages - Import page files in routes.ts to trigger decorators
Wildcard for CMS - Use
/*for collection template pagesExtend PageBase - Get automatic context detection and pageInfo access
Example: Complete Setup
Here's a complete example showing proper dispatcher usage:
src/routes.ts:
import { RouteDispatcher, getAllPages } from "@sygnal/sse-core";
import { Site } from "./site";
// Import all pages
import "./pages/home";
import "./pages/about";
import "./pages/blog";
import "./pages/products";
// Import all components
import "./components/navigation";
import "./components/footer";
export const routeDispatcher = (): RouteDispatcher => {
const dispatcher = new RouteDispatcher(Site);
dispatcher.routes = getAllPages();
return dispatcher;
}src/index.ts:
import { routeDispatcher } from './routes';
// Create once
const dispatcher = routeDispatcher();
// Setup phase
dispatcher.setupRoute();
// Execution phase
window.Webflow ||= [];
window.Webflow.push(() => {
dispatcher.execRoute();
});src/pages/blog.ts:
import { PageBase, page } from '@sygnal/sse-core';
@page('/blog/*')
export class BlogPage extends PageBase {
protected onPrepare(): void {
console.log('Blog page preparing...');
}
protected async onLoad(): Promise<void> {
console.log('Blog post loaded:', this.pageInfo.itemSlug);
// Your blog-specific code here
this.loadComments();
this.setupSocialSharing();
}
private async loadComments() {
// Load comments for this post
}
private setupSocialSharing() {
// Setup social sharing buttons
}
}Last updated