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

  1. Package renamed: @sygnal/sse@sygnal/sse-core

  2. New base classes: PageBase and ComponentBase with automatic context detection

  3. Lifecycle methods: setup()/exec()onPrepare()/onLoad()

  4. Singleton pattern: Access page from components via PageBase.getCurrentPage()

  5. 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 install

Step 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 IModuleextends PageBase

  • Remove constructor (not needed)

  • setup()protected onPrepare()

  • exec()protected async onLoad()

  • Access this.pageInfo for 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 IModuleextends ComponentBase

  • Remove constructor (automatic element injection!)

  • setup()protected onPrepare()

  • exec()protected async onLoad()

  • this.elemthis.element (automatic)

  • Access this.context for component metadata

  • Access 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 URL

  • this.pageInfo.hash - URL hash fragment

  • this.pageInfo.queryParams - Parsed query parameters object

  • this.pageInfo.pageId - Webflow page ID

  • this.pageInfo.siteId - Webflow site ID

  • this.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 domain

  • this.pageInfo.lang - Page language

ComponentBase - this Properties

When extending ComponentBase, you automatically get:

  • this.element - The HTMLElement the component is bound to

  • this.context.name - Component name from data-component

  • this.context.id - Component ID from data-component-id

  • this.context.dataAttributes - All data-* attributes as key-value pairs

Benefits Summary

Feature
v1.x (IModule)
v2.0 (Base Classes)

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:

  1. ✅ All pages load without console errors

  2. ✅ Page-specific code executes on correct routes

  3. ✅ Components initialize and function correctly

  4. this.pageInfo contains expected values in pages

  5. this.element and this.context work in components

  6. PageBase.getCurrentPage() returns page instance from components

  7. ✅ Data persists between onPrepare() and onLoad() 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 PageBase for automatic context (recommended)

  • Components: Use ComponentBase for automatic context (recommended)

  • Advanced cases: Use IModule when you need full manual control

Need Help?

If you encounter issues during migration:

  1. Check that all imports use @sygnal/sse-core

  2. Verify RouteDispatcher is created once and reused

  3. Ensure pages extend PageBase and components extend ComponentBase

  4. Check that lifecycle methods use new names (onPrepare/onLoad)

  5. Review the sse-template repository for complete examples

Last updated