Migrating from SSE to SSE2
Key changes you'll need to make to utilize SSE2's new sse-core
Overview
SSE v2.0.0 (sse-core) introduces significant improvements that make working with Webflow sites easier and more maintainable. This guide will walk you through migrating your existing SSE v1.x code to v2.0.0.
Key Changes
Package renamed:
@sygnal/sse→@sygnal/sse-coreNew base classes:
PageBaseandComponentBasewith automatic context detectionLifecycle methods:
setup()/exec()→onPrepare()/onLoad()Singleton pattern: Access page from components via
PageBase.getCurrentPage()RouteDispatcher fix: Must create once and reuse the instance
Migration Steps
Step 1: Update Package
Update your package.json:
{
"dependencies": {
"@sygnal/sse-core": "^2.0.0"
}
}Then run:
npm installStep 2: Update Import Statements
Find and replace all imports throughout your project:
Before:
import { IModule, page, component } from '@sygnal/sse';After:
import { PageBase, ComponentBase, page, component } from '@sygnal/sse-core';Step 3: Migrate Pages to PageBase
Before (v1.x):
import { IModule, page } from '@sygnal/sse';
@page('/')
export class HomePage implements IModule {
constructor() {}
setup(): void {
// Synchronous setup
console.log('Setting up home page');
}
async exec(): Promise<void> {
// Manual context detection
const pageId = document.documentElement.getAttribute('data-wf-page');
const collectionId = document.documentElement.getAttribute('data-wf-collection');
console.log('Page ID:', pageId);
console.log('Collection:', collectionId);
}
}After (v2.0):
import { PageBase, page } from '@sygnal/sse-core';
@page('/')
export class HomePage extends PageBase {
protected onPrepare(): void {
// Synchronous setup - automatic context available!
console.log('Setting up home page');
console.log('Page ID:', this.pageInfo.pageId);
}
protected async onLoad(): Promise<void> {
// Async execution - full context available
console.log('Collection:', this.pageInfo.collectionId);
console.log('Item Slug:', this.pageInfo.itemSlug);
console.log('Query Params:', this.pageInfo.queryParams);
}
}Key Changes:
implements IModule→extends PageBaseRemove constructor (not needed)
setup()→protected onPrepare()exec()→protected async onLoad()Access
this.pageInfofor all Webflow context (no manual DOM queries!)
Step 4: Migrate Components to ComponentBase
Before (v1.x):
import { IModule, component } from '@sygnal/sse';
@component('navigation')
export class NavigationComponent implements IModule {
private elem: HTMLElement;
constructor(elem: HTMLElement) {
this.elem = elem;
}
setup(): void {
console.log('Navigation setup');
}
async exec(): Promise<void> {
// Manual element access
const componentName = this.elem.getAttribute('data-component');
const customData = this.elem.getAttribute('data-nav-type');
this.elem.addEventListener('click', () => {
console.log('Navigation clicked');
});
}
}After (v2.0):
import { ComponentBase, component, PageBase } from '@sygnal/sse-core';
@component('navigation')
export class NavigationComponent extends ComponentBase {
protected onPrepare(): void {
// Automatic element and context available!
console.log('Navigation setup');
console.log('Component:', this.context.name);
}
protected async onLoad(): Promise<void> {
// Access element directly
const customData = this.element.getAttribute('data-nav-type');
// Access current page if needed
const page = PageBase.getCurrentPage();
if (page) {
console.log('Current page:', page.pageInfo.pageId);
}
this.element.addEventListener('click', () => {
console.log('Navigation clicked');
});
}
}Key Changes:
implements IModule→extends ComponentBaseRemove constructor (automatic element injection!)
setup()→protected onPrepare()exec()→protected async onLoad()this.elem→this.element(automatic)Access
this.contextfor component metadataAccess page via
PageBase.getCurrentPage()singleton
Step 5: Fix RouteDispatcher Instance (CRITICAL)
This is a critical fix that prevents data loss between preparation and execution phases.
Before (v1.x - BROKEN):
import { routeDispatcher } from './routes';
// This creates TWO different instances!
routeDispatcher().setupRoute(); // Instance A stores data
routeDispatcher().execRoute(); // Instance B has no data - LOST!After (v2.0 - CORRECT):
import { routeDispatcher } from './routes';
// Create ONCE and reuse the same instance
const dispatcher = routeDispatcher();
dispatcher.setupRoute(); // Instance stores data
dispatcher.execRoute(); // SAME instance has the data!Update your src/index.ts:
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();
});Step 6: Update Route Registration (Optional)
If using automatic route discovery with getAllPages():
import { RouteDispatcher, getAllPages } from "@sygnal/sse-core";
import { Site } from "./site";
// Import pages to trigger @page decorators
import "./pages/home";
import "./pages/blog";
import "./pages/about";
export const routeDispatcher = (): RouteDispatcher => {
const dispatcher = new RouteDispatcher(Site);
dispatcher.routes = getAllPages(); // Automatically populated!
return dispatcher;
}Step 7: Keep Site Class Using IModule
The Site class doesn't need automatic context, so it still uses IModule:
import { IModule } from "@sygnal/sse-core";
export class Site implements IModule {
constructor() {}
setup() {
// Site-wide setup
}
exec() {
// Site-wide execution
}
}Available Automatic Context
PageBase - this.pageInfo Properties
When extending PageBase, you automatically get access to:
this.pageInfo.path- Current page path (e.g., "/blog/my-post")this.pageInfo.url- Full URLthis.pageInfo.hash- URL hash fragmentthis.pageInfo.queryParams- Parsed query parameters objectthis.pageInfo.pageId- Webflow page IDthis.pageInfo.siteId- Webflow site IDthis.pageInfo.collectionId- CMS collection ID (if applicable)this.pageInfo.itemId- CMS item ID (if applicable)this.pageInfo.itemSlug- CMS item slug (if applicable)this.pageInfo.domain- Webflow domainthis.pageInfo.lang- Page language
ComponentBase - this Properties
When extending ComponentBase, you automatically get:
this.element- The HTMLElement the component is bound tothis.context.name- Component name fromdata-componentthis.context.id- Component ID fromdata-component-idthis.context.dataAttributes- All data-* attributes as key-value pairs
Benefits Summary
Context Detection
Manual DOM queries
Automatic
Page Info
Parse attributes yourself
this.pageInfo.*
Component Element
Pass in constructor
this.element
Lifecycle Naming
setup()/exec()
onPrepare()/onLoad()
Page Access from Component
Pass references manually
PageBase.getCurrentPage()
Type Safety
Manual typing
Fully typed
Boilerplate Code
Lots
Minimal
Testing Your Migration
After migrating, verify:
✅ All pages load without console errors
✅ Page-specific code executes on correct routes
✅ Components initialize and function correctly
✅
this.pageInfocontains expected values in pages✅
this.elementandthis.contextwork in components✅
PageBase.getCurrentPage()returns page instance from components✅ Data persists between
onPrepare()andonLoad()phases
Common Issues
Issue: "Property 'pageInfo' does not exist"
Cause: Still using implements IModule instead of extends PageBase
Fix: Change class declaration:
// Before
export class HomePage implements IModule {
// After
export class HomePage extends PageBase {Issue: Data lost between onPrepare() and onLoad()
Cause: Creating multiple RouteDispatcher instances
Fix: Store dispatcher in a variable and reuse:
const dispatcher = routeDispatcher();
dispatcher.setupRoute();
dispatcher.execRoute();Issue: "Cannot read property 'pageId' of undefined"
Cause: Trying to access pageInfo before context detection completes
Fix: PageInfo is available in both onPrepare() and onLoad(), but ensure you're extending PageBase correctly.
Backward Compatibility
The IModule interface is still fully supported for advanced use cases where you need manual control. You can mix and match:
Site class: Continue using
IModule(recommended)Pages: Use
PageBasefor automatic context (recommended)Components: Use
ComponentBasefor automatic context (recommended)Advanced cases: Use
IModulewhen you need full manual control
Need Help?
If you encounter issues during migration:
Check that all imports use
@sygnal/sse-coreVerify RouteDispatcher is created once and reused
Ensure pages extend
PageBaseand components extendComponentBaseCheck that lifecycle methods use new names (
onPrepare/onLoad)Review the sse-template repository for complete examples
Last updated