Repository: swup/swup
Pull request #706 (2023-07-05 16:37:15)

Namespaced hooks
**Description**

- Group and namespace hooks for better sorting and easier parsing
- Follow logic of DOM events and use present tense as much as possible
- Simplify tooling in libs like Alpine that expect lowercase events
- Rename `transition` to `visit` to avoid confusion between animations and transitions here
- `replaceContent` → `content:replace`
- `animationInStart` → `animation:in:start`

**Hooks**

| Now | Before |
| ------- | ------- |
| `animation:out:start` | `animationOutStart` |
| `animation:out:end` | `animationOutDone` |
| `animation:in:start` | `animationInStart` |
| `animation:in:end` | `animationInDone` |
| `animation:skip` | `animationSkipped` |
| `animation:await` | `awaitAnimation` |
| `cache:clear` | `cacheCleared` |
| `cache:set` | `pageCached` |
| `content:replace` | `replaceContent` |
| `content:scroll` | `scrollToContent` |
| `fetch:request` | `fetchPage` |
| `fetch:error` | `serverError` |
| `history:popstate` | `popState` |
| `link:click` | `clickLink` |
| `link:self` | `samePage` |
| `link:anchor` | `samePageWithHash` |
| `link:newtab` | `openPageInNewTab` |
| `page:request` | `loadPage` |
| `page:load` | `pageLoaded` |
| `page:view` | `pageView` |
| `enable` | `enabled` |
| `disable` | `disabled` |
| `visit:start` | `transitionStart` |
| `visit:end` | `transitionEnd` |


**Checks**

- [x] The PR is submitted to the `next` branch
- [x] The code was linted before pushing (`npm run lint`)
- [x] All tests are passing (`npm run test`)
- [x] The documentation [was updated as required](https://github.com/swup/docs/pull/127)


Patch:
diff --git a/cypress/support/commands.js b/cypress/support/commands.js
index 6c96c81..33d4102 100644
--- a/cypress/support/commands.js
+++ b/cypress/support/commands.js
@@ -112,12 +112,12 @@ Cypress.Commands.add('shouldAnimateWithDuration', function (durationInMs, url =
 	let durationIn = null;
 
 	cy.window().then((window) => {
-		this.swup.hooks.on('animationOutStart', () => (startOut = performance.now()));
-		this.swup.hooks.on('animationOutDone', () => (durationOut = performance.now() - startOut));
-		this.swup.hooks.on('animationInStart', () => (startIn = performance.now()));
-		this.swup.hooks.on('animationInDone', () => (durationIn = performance.now() - startIn));
-		this.swup.hooks.on('animationSkipped', () => (durationIn = 0));
-		this.swup.hooks.on('animationSkipped', () => (durationOut = 0));
+		this.swup.hooks.on('animation:out:start', () => (startOut = performance.now()));
+		this.swup.hooks.on('animation:out:end', () => (durationOut = performance.now() - startOut));
+		this.swup.hooks.on('animation:in:start', () => (startIn = performance.now()));
+		this.swup.hooks.on('animation:in:end', () => (durationIn = performance.now() - startIn));
+		this.swup.hooks.on('animation:skip', () => (durationIn = 0));
+		this.swup.hooks.on('animation:skip', () => (durationOut = 0));
 		url = url || window.location.href;
 		this.swup.visit(url);
 	});
diff --git a/package-lock.json b/package-lock.json
index e449ff7..4eb4287 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2778,9 +2778,9 @@
       "dev": true
     },
     "node_modules/@swup/prettier-config": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/@swup/prettier-config/-/prettier-config-1.0.0.tgz",
-      "integrity": "sha512-NbbuG2zT+bQkPcUGyeLNFbiwdunc58e3A/GZpPpUDrXA4ZKN7/rNh97sfG6z5+0OfxrNmDE3N7t+60p5HKhc9w==",
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@swup/prettier-config/-/prettier-config-1.1.0.tgz",
+      "integrity": "sha512-EF4DMdIGieEsuY2XK0PuLf7Uw7yUQOMbA6IdCMvvRvKXj03WLLpnNIFfFp+6hmMtXRSUE88VBpRyp6Giiu1Pbg==",
       "dev": true
     },
     "node_modules/@tootallnate/once": {
diff --git a/src/Swup.ts b/src/Swup.ts
index f99dcb0..eb7c2e4 100644
--- a/src/Swup.ts
+++ b/src/Swup.ts
@@ -83,7 +83,7 @@ export default class Swup {
 		resolveUrl: (url) => url,
 		requestHeaders: {
 			'X-Requested-With': 'swup',
-			Accept: 'text/html, application/xhtml+xml'
+			'Accept': 'text/html, application/xhtml+xml'
 		},
 		skipPopStateHandling: (event) => event.state?.source !== 'swup'
 	};
@@ -134,16 +134,16 @@ export default class Swup {
 		// Modify initial history record
 		updateHistoryRecord();
 
-		// Trigger enabled event
-		await this.hooks.trigger('enabled', undefined, () => {
+		// Trigger enable hook
+		await this.hooks.trigger('enable', undefined, () => {
 			// Add swup-enabled class to html tag
 			document.documentElement.classList.add('swup-enabled');
 		});
 
 		await nextTick();
 
-		// Trigger page view event
-		await this.hooks.trigger('pageView', { url: this.currentPageUrl, title: document.title });
+		// Trigger page view hook
+		await this.hooks.trigger('page:view', { url: this.currentPageUrl, title: document.title });
 	}
 
 	async destroy() {
@@ -159,8 +159,8 @@ export default class Swup {
 		// unmount plugins
 		this.options.plugins.forEach((plugin) => this.unuse(plugin));
 
-		// trigger disable event
-		await this.hooks.trigger('disabled', undefined, () => {
+		// trigger disable hook
+		await this.hooks.trigger('disable', undefined, () => {
 			// remove swup-enabled class from html tag
 			document.documentElement.classList.remove('swup-enabled');
 		});
@@ -221,7 +221,7 @@ export default class Swup {
 
 		// Exit early if control key pressed
 		if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
-			this.hooks.trigger('openPageInNewTab', { href });
+			this.hooks.trigger('link:newtab', { href });
 			return;
 		}
 
@@ -230,7 +230,7 @@ export default class Swup {
 			return;
 		}
 
-		this.hooks.triggerSync('clickLink', { el, event }, () => {
+		this.hooks.triggerSync('link:click', { el, event }, () => {
 			const from = this.context.from.url ?? '';
 
 			event.preventDefault();
@@ -240,7 +240,7 @@ export default class Swup {
 				if (hash) {
 					updateHistoryRecord(url + hash);
 					this.hooks.triggerSync(
-						'samePageWithHash',
+						'link:anchor',
 						{ hash, options: { behavior: 'auto' } },
 						(context, { hash, options }) => {
 							const target = this.getAnchorElement(hash);
@@ -250,7 +250,7 @@ export default class Swup {
 						}
 					);
 				} else {
-					this.hooks.triggerSync('samePage', undefined, (context) => {
+					this.hooks.triggerSync('link:self', undefined, (context) => {
 						if (!context.scroll.reset) return;
 						window.scroll({ top: 0, left: 0, behavior: 'auto' });
 					});
@@ -310,7 +310,7 @@ export default class Swup {
 		// 	event.preventDefault();
 		// }
 
-		this.hooks.triggerSync('popState', { event }, () => {
+		this.hooks.triggerSync('history:popstate', { event }, () => {
 			this.performVisit(url);
 		});
 	}
diff --git a/src/modules/Cache.ts b/src/modules/Cache.ts
index 6a97770..2fb4405 100755
--- a/src/modules/Cache.ts
+++ b/src/modules/Cache.ts
@@ -32,7 +32,7 @@ export class Cache {
 		url = this.resolve(url);
 		page = { ...page, url };
 		this.pages.set(url, page);
-		this.swup.hooks.triggerSync('pageCached', { page });
+		this.swup.hooks.triggerSync('cache:set', { page });
 	}
 
 	public update(url: string, page: CacheData) {
@@ -47,7 +47,7 @@ export class Cache {
 
 	public clear(): void {
 		this.pages.clear();
-		this.swup.hooks.triggerSync('cacheCleared');
+		this.swup.hooks.triggerSync('cache:clear');
 	}
 
 	public prune(predicate: (url: string, page: CacheData) => boolean): void {
diff --git a/src/modules/Hooks.ts b/src/modules/Hooks.ts
index 6dfc36c..4fa582d 100644
--- a/src/modules/Hooks.ts
+++ b/src/modules/Hooks.ts
@@ -7,30 +7,30 @@ import { FetchOptions, PageData } from './fetchPage.js';
 import { AnimationDirection } from './awaitAnimations.js';
 
 export interface HookDefinitions {
-	animationInDone: undefined;
-	animationInStart: undefined;
-	animationOutDone: undefined;
-	animationOutStart: undefined;
-	animationSkipped: undefined;
-	awaitAnimation: { direction: AnimationDirection };
-	cacheCleared: undefined;
-	clickLink: { el: HTMLAnchorElement; event: DelegateEvent<MouseEvent> };
-	disabled: undefined;
-	enabled: undefined;
-	fetchPage: { url: string; options: FetchOptions };
-	loadPage: { url: string; options: FetchOptions };
-	openPageInNewTab: { href: string };
-	pageCached: { page: PageData };
-	pageLoaded: { page: PageData; cache?: boolean };
-	pageView: { url: string; title: string };
-	popState: { event: PopStateEvent };
-	replaceContent: { page: PageData };
-	samePage: undefined;
-	samePageWithHash: { hash: string; options: ScrollIntoViewOptions };
-	scrollToContent: { options: ScrollIntoViewOptions };
-	serverError: { url: string; status: number; response: Response };
-	transitionStart: undefined;
-	transitionEnd: undefined;
+	'animation:out:start': undefined;
+	'animation:out:end': undefined;
+	'animation:in:start': undefined;
+	'animation:in:end': undefined;
+	'animation:skip': undefined;
+	'animation:await': { direction: AnimationDirection };
+	'cache:clear': undefined;
+	'cache:set': { page: PageData };
+	'content:replace': { page: PageData };
+	'content:scroll': { options: ScrollIntoViewOptions };
+	'enable': undefined;
+	'disable': undefined;
+	'fetch:request': { url: string; options: FetchOptions };
+	'fetch:error': { url: string; status: number; response: Response };
+	'history:popstate': { event: PopStateEvent };
+	'link:click': { el: HTMLAnchorElement; event: DelegateEvent<MouseEvent> };
+	'link:self': undefined;
+	'link:anchor': { hash: string; options: ScrollIntoViewOptions };
+	'link:newtab': { href: string };
+	'page:request': { url: string; options: FetchOptions };
+	'page:load': { page: PageData; cache?: boolean };
+	'page:view': { url: string; title: string };
+	'visit:start': undefined;
+	'visit:end': undefined;
 }
 
 export type HookArguments<T extends HookName> = HookDefinitions[T];
@@ -80,30 +80,30 @@ export class Hooks {
 	// Can we deduplicate this somehow? Or make it error when not in sync with HookDefinitions?
 	// https://stackoverflow.com/questions/53387838/how-to-ensure-an-arrays-values-the-keys-of-a-typescript-interface/53395649
 	readonly hooks: HookName[] = [
-		'animationInDone',
-		'animationInStart',
-		'animationOutDone',
-		'animationOutStart',
-		'animationSkipped',
-		'awaitAnimation',
-		'cacheCleared',
-		'clickLink',
-		'disabled',
-		'enabled',
-		'fetchPage',
-		'loadPage',
-		'openPageInNewTab',
-		'pageCached',
-		'pageLoaded',
-		'pageView',
-		'popState',
-		'replaceContent',
-		'samePage',
-		'samePageWithHash',
-		'scrollToContent',
-		'serverError',
-		'transitionStart',
-		'transitionEnd'
+		'animation:out:start',
+		'animation:out:end',
+		'animation:in:start',
+		'animation:in:end',
+		'animation:skip',
+		'animation:await',
+		'cache:clear',
+		'cache:set',
+		'content:replace',
+		'content:scroll',
+		'enable',
+		'disable',
+		'fetch:request',
+		'fetch:error',
+		'history:popstate',
+		'link:click',
+		'link:self',
+		'link:anchor',
+		'link:newtab',
+		'page:request',
+		'page:load',
+		'page:view',
+		'visit:start',
+		'visit:end'
 	];
 
 	constructor(swup: Swup) {
diff --git a/src/modules/enterPage.ts b/src/modules/enterPage.ts
index e64ce5e..b1eabb6 100755
--- a/src/modules/enterPage.ts
+++ b/src/modules/enterPage.ts
@@ -4,21 +4,21 @@ import { nextTick } from '../utils.js';
 export const enterPage = async function (this: Swup) {
 	if (this.context.animation.animate) {
 		const animation = this.hooks.trigger(
-			'awaitAnimation',
+			'animation:await',
 			{ direction: 'in' },
 			async (context, { direction }) => {
 				await this.awaitAnimations({ selector: context.animation.selector, direction });
 			}
 		);
 		await nextTick();
-		await this.hooks.trigger('animationInStart', undefined, () => {
+		await this.hooks.trigger('animation:in:start', undefined, () => {
 			this.classes.remove('is-animating');
 		});
 		await animation;
-		await this.hooks.trigger('animationInDone');
+		await this.hooks.trigger('animation:in:end');
 	}
 
-	await this.hooks.trigger('transitionEnd', undefined, () => {
+	await this.hooks.trigger('visit:end', undefined, () => {
 		this.classes.clear();
 	});
 };
diff --git a/src/modules/fetchPage.ts b/src/modules/fetchPage.ts
index 02824f6..acf5162 100644
--- a/src/modules/fetchPage.ts
+++ b/src/modules/fetchPage.ts
@@ -36,7 +36,7 @@ export async function fetchPage(
 	if (this.cache.has(url)) {
 		const page = this.cache.get(url) as PageData;
 		if (options.triggerHooks !== false) {
-			await this.hooks.trigger('pageLoaded', { page, cache: true });
+			await this.hooks.trigger('page:load', { page, cache: true });
 		}
 		return page;
 	}
@@ -46,7 +46,7 @@ export async function fetchPage(
 
 	// Allow hooking before this and returning a custom response-like object (e.g. custom fetch implementation)
 	const response: Response = await this.hooks.trigger(
-		'fetchPage',
+		'fetch:request',
 		{ url, options },
 		(context, { url, options }) => fetch(url, options)
 	);
@@ -55,7 +55,7 @@ export async function fetchPage(
 	const html = await response.text();
 
 	if (status === 500) {
-		this.hooks.trigger('serverError', { status, response, url: responseUrl });
+		this.hooks.trigger('fetch:error', { status, response, url: responseUrl });
 		throw new FetchError(`Server error: ${responseUrl}`, { status, url: responseUrl });
 	}
 
@@ -73,7 +73,7 @@ export async function fetchPage(
 	}
 
 	if (options.triggerHooks !== false) {
-		await this.hooks.trigger('pageLoaded', { page, cache: false });
+		await this.hooks.trigger('page:load', { page, cache: false });
 	}
 
 	return page;
diff --git a/src/modules/leavePage.ts b/src/modules/leavePage.ts
index b6a7782..405deca 100755
--- a/src/modules/leavePage.ts
+++ b/src/modules/leavePage.ts
@@ -3,11 +3,11 @@ import { classify } from '../helpers.js';
 
 export const leavePage = async function (this: Swup) {
 	if (!this.context.animation.animate) {
-		await this.hooks.trigger('animationSkipped');
+		await this.hooks.trigger('animation:skip');
 		return;
 	}
 
-	await this.hooks.trigger('animationOutStart', undefined, () => {
+	await this.hooks.trigger('animation:out:start', undefined, () => {
 		this.classes.add('is-changing', 'is-leaving', 'is-animating');
 		if (this.context.history.popstate) {
 			this.classes.add('is-popstate');
@@ -18,12 +18,12 @@ export const leavePage = async function (this: Swup) {
 	});
 
 	await this.hooks.trigger(
-		'awaitAnimation',
+		'animation:await',
 		{ direction: 'out' },
 		async (context, { direction }) => {
 			await this.awaitAnimations({ selector: context.animation.selector, direction });
 		}
 	);
 
-	await this.hooks.trigger('animationOutDone');
+	await this.hooks.trigger('animation:out:end');
 };
diff --git a/src/modules/renderPage.ts b/src/modules/renderPage.ts
index 400150f..b1cc45e 100755
--- a/src/modules/renderPage.ts
+++ b/src/modules/renderPage.ts
@@ -28,7 +28,7 @@ export const renderPage = async function (this: Swup, requestedUrl: string, page
 	this.context.to.html = html;
 
 	// replace content: allow handlers and plugins to overwrite paga data and containers
-	await this.hooks.trigger('replaceContent', { page }, (context, { page }) => {
+	await this.hooks.trigger('content:replace', { page }, (context, { page }) => {
 		const success = this.replaceContent(page, { containers: context.containers });
 		if (!success) {
 			throw new Error('[swup] Container mismatch, aborting');
@@ -43,7 +43,7 @@ export const renderPage = async function (this: Swup, requestedUrl: string, page
 	});
 
 	await this.hooks.trigger(
-		'scrollToContent',
+		'content:scroll',
 		{ options: { behavior: 'auto' } },
 		(context, { options }) => {
 			if (this.context.scroll.target) {
@@ -59,7 +59,7 @@ export const renderPage = async function (this: Swup, requestedUrl: string, page
 		}
 	);
 
-	await this.hooks.trigger('pageView', { url: this.currentPageUrl, title: document.title });
+	await this.hooks.trigger('page:view', { url: this.currentPageUrl, title: document.title });
 
 	// empty cache if it's disabled (in case preload plugin filled it)
 	if (!this.options.cache) {
diff --git a/src/modules/visit.ts b/src/modules/visit.ts
index 87e7086..f1091d4 100755
--- a/src/modules/visit.ts
+++ b/src/modules/visit.ts
@@ -56,11 +56,11 @@ export async function performVisit(
 	}
 
 	try {
-		await this.hooks.trigger('transitionStart');
+		await this.hooks.trigger('visit:start');
 
 		// Begin fetching page
 		const pagePromise = this.hooks.trigger(
-			'loadPage',
+			'page:request',
 			{ url: this.context.to.url, options },
 			async (context, { options }) => await this.fetchPage(context.to.url as string, options)
 		);


Test patch:
diff --git a/cypress/e2e/main.spec.js b/cypress/e2e/main.spec.js
index 4088950..fda080e 100644
--- a/cypress/e2e/main.spec.js
+++ b/cypress/e2e/main.spec.js
@@ -53,8 +53,8 @@ describe('Fetch', function () {
 		cy.wrapSwupInstance();
 	});
 
-	it('should allow calling original loadPage handler', function () {
-		this.swup.hooks.replace('loadPage', (context, args, defaultHandler) => {
+	it('should allow calling original page:request handler', function () {
+		this.swup.hooks.replace('page:request', (context, args, defaultHandler) => {
 			return defaultHandler(context, args);
 		});
 		this.swup.visit('/page-2.html');
@@ -63,13 +63,13 @@ describe('Fetch', function () {
 		cy.shouldHaveH1('Page 2');
 	});
 
-	it('should allow returning a page object to loadPage', function () {
+	it('should allow returning a page object to page:request', function () {
 		let requested = false;
 		cy.intercept('/page-2.html', (req) => {
 			requested = true;
 		});
 
-		this.swup.hooks.replace('loadPage', () => {
+		this.swup.hooks.replace('page:request', () => {
 			return {
 				url: '/page-3.html',
 				html: '<html><body><div id="swup"><h1>Page 3</h1></div></body></html>'
@@ -84,8 +84,8 @@ describe('Fetch', function () {
 		});
 	});
 
-	it('should allow returning a fetch Promise to loadPage', function () {
-		this.swup.hooks.replace('loadPage', () => {
+	it('should allow returning a fetch Promise to page:request', function () {
+		this.swup.hooks.replace('page:request', () => {
 			return this.swup.fetchPage('page-3.html');
 		});
 		this.swup.visit('/page-2.html');
@@ -158,7 +158,7 @@ describe('Events', function () {
 		let triggered = false;
 		let data = [];
 		cy.document().then((document) => {
-			document.addEventListener('swup:clickLink', (event) => {
+			document.addEventListener('swup:link:click', (event) => {
 				triggered = true;
 				data = event.detail;
 			});
@@ -166,7 +166,7 @@ describe('Events', function () {
 		cy.triggerClickOnLink('/page-2.html');
 		cy.window().should(() => {
 			expect(triggered, 'event was not triggered').to.be.true;
-			expect(data).to.have.property('hook', 'clickLink');
+			expect(data).to.have.property('hook', 'link:click');
 		});
 	});
 
@@ -190,7 +190,7 @@ describe('Events', function () {
 		const handlers = { click() {} };
 		cy.spy(handlers, 'click');
 
-		this.swup.hooks.on('clickLink', handlers.click);
+		this.swup.hooks.on('link:click', handlers.click);
 		cy.triggerClickOnLink('/page-2.html');
 		cy.window().should(() => {
 			expect(handlers.click).to.be.called;
@@ -202,8 +202,8 @@ describe('Events', function () {
 		cy.spy(handlers, 'transition');
 		cy.spy(handlers, 'content');
 
-		this.swup.hooks.on('transitionStart', handlers.transition);
-		this.swup.hooks.on('replaceContent', handlers.content);
+		this.swup.hooks.on('visit:start', handlers.transition);
+		this.swup.hooks.on('content:replace', handlers.content);
 
 		cy.triggerClickOnLink('/page-2.html');
 		cy.window().should(() => {
@@ -212,7 +212,7 @@ describe('Events', function () {
 		});
 
 		cy.window().then(() => {
-			this.swup.hooks.off('transitionStart', handlers.transition);
+			this.swup.hooks.off('visit:start', handlers.transition);
 		});
 		cy.triggerClickOnLink('/page-3.html');
 		cy.window().should(() => {
@@ -541,11 +541,11 @@ describe('History', function () {
 		});
 	});
 
-	it('should trigger a custom popState event', function () {
+	it('should trigger a custom popstate event', function () {
 		const handlers = { popstate() {} };
 		cy.spy(handlers, 'popstate');
 
-		this.swup.hooks.on('popState', handlers.popstate);
+		this.swup.hooks.on('history:popstate', handlers.popstate);
 
 		cy.triggerClickOnLink('/page-2.html');
 		cy.shouldBeAtPage('/page-2.html');
@@ -591,7 +591,7 @@ describe('Context', function () {
 	it('has the current and next url', function () {
 		let from = '';
 		let to = '';
-		this.swup.hooks.before('transitionStart', (ctx) => {
+		this.swup.hooks.before('visit:start', (ctx) => {
 			from = ctx.from.url;
 			to = ctx.to.url;
 		});
@@ -605,7 +605,7 @@ describe('Context', function () {
 	it('has the correct current url on history visits', function () {
 		let from = '';
 		let to = '';
-		this.swup.hooks.before('transitionStart', (ctx) => {
+		this.swup.hooks.before('visit:start', (ctx) => {
 			from = ctx.from.url;
 			to = ctx.to.url;
 		});
@@ -626,7 +626,7 @@ describe('Context', function () {
 	it('passes along the click trigger and event', function () {
 		let el = null;
 		let event = null;
-		this.swup.hooks.before('transitionStart', (context) => {
+		this.swup.hooks.before('visit:start', (context) => {
 			el = context.trigger.el;
 			event = context.trigger.event;
 		});
@@ -640,7 +640,7 @@ describe('Context', function () {
 	it('passes along the popstate status and event', function () {
 		let event = null;
 		let historyVisit = null;
-		this.swup.hooks.before('transitionStart', (context) => {
+		this.swup.hooks.before('visit:start', (context) => {
 			event = context.trigger.event;
 			historyVisit = context.history.popstate;
 		});
@@ -661,7 +661,7 @@ describe('Context', function () {
 	it('passes along the custom animation', function () {
 		let name = null;
 		let expectedName = null;
-		this.swup.hooks.before('transitionStart', (context) => {
+		this.swup.hooks.before('visit:start', (context) => {
 			name = context.animation.name;
 		});
 		cy.get('a[data-swup-animation]').should(($el) => {
@@ -676,7 +676,7 @@ describe('Context', function () {
 	});
 
 	it('should allow disabling animations', function () {
-		this.swup.hooks.before('transitionStart', (context) => {
+		this.swup.hooks.before('visit:start', (context) => {
 			context.animation.animate = false;
 		});
 		cy.shouldAnimateWithDuration(0, '/page-2.html');
@@ -690,7 +690,7 @@ describe('Containers', function () {
 	});
 
 	it('should be customizable from context', function () {
-		this.swup.hooks.before('transitionStart', (context) => {
+		this.swup.hooks.before('visit:start', (context) => {
 			context.containers = ['#aside'];
 		});
 		this.swup.visit('/containers-2.html', { animate: false });
@@ -699,7 +699,7 @@ describe('Containers', function () {
 	});
 
 	it('should be customizable from hook context', function () {
-		this.swup.hooks.before('replaceContent', (context) => {
+		this.swup.hooks.before('content:replace', (context) => {
 			context.containers = ['#main'];
 		});
 		this.swup.visit('/containers-2.html', { animate: false });
diff --git a/src/__test__/index.test.ts b/src/__test__/index.test.ts
index 3ce715c..4104b2e 100644
--- a/src/__test__/index.test.ts
+++ b/src/__test__/index.test.ts
@@ -38,7 +38,7 @@ describe('Exports', () => {
 			resolveUrl: (url) => url,
 			requestHeaders: {
 				'X-Requested-With': 'swup',
-				Accept: 'text/html, application/xhtml+xml'
+				'Accept': 'text/html, application/xhtml+xml'
 			},
 			skipPopStateHandling: (event) => event.state?.source !== 'swup'
 		};
diff --git a/src/modules/__test__/cache.test.ts b/src/modules/__test__/cache.test.ts
index ef6e372..5161155 100644
--- a/src/modules/__test__/cache.test.ts
+++ b/src/modules/__test__/cache.test.ts
@@ -77,7 +77,7 @@ describe('Cache', () => {
 	it('should trigger a hook on set', () => {
 		const handler = vi.fn();
 
-		swup.hooks.on('pageCached', handler);
+		swup.hooks.on('cache:set', handler);
 
 		cache.set(page1.url, page1);
 
@@ -88,7 +88,7 @@ describe('Cache', () => {
 	it('should allow augmenting cache entries on save', () => {
 		const now = Date.now();
 
-		swup.hooks.on('pageCached', (_, { page }) => {
+		swup.hooks.on('cache:set', (_, { page }) => {
 			const ttl: CacheTtlData = { ttl: 1000, created: now };
 			cache.update(page.url, ttl as AugmentedCacheData);
 		});
@@ -101,7 +101,7 @@ describe('Cache', () => {
 	});
 
 	it('should allow manual pruning', () => {
-		swup.hooks.on('pageCached', (_, { page }) => {
+		swup.hooks.on('cache:set', (_, { page }) => {
 			cache.update(page.url, { index: cache.size } as AugmentedCacheData);
 		});
 
@@ -124,9 +124,9 @@ describe('Types', () => {
 		const cache = new Cache(swup);
 
 		// @ts-expect-no-error
-		swup.hooks.on('popState', (ctx: Context, { event: PopStateEvent }) => {});
+		swup.hooks.on('history:popstate', (ctx: Context, { event: PopStateEvent }) => {});
 		// @ts-expect-no-error
-		await swup.hooks.trigger('popState', { event: new PopStateEvent('') });
+		await swup.hooks.trigger('history:popstate', { event: new PopStateEvent('') });
 
 		try {
 			// @ts-expect-error
diff --git a/src/modules/__test__/hooks.test.ts b/src/modules/__test__/hooks.test.ts
index c8c7ebb..b2dcbc3 100644
--- a/src/modules/__test__/hooks.test.ts
+++ b/src/modules/__test__/hooks.test.ts
@@ -16,8 +16,8 @@ describe('Hook registry', () => {
 		};
 		const hooks = new HooksWithAccess(swup);
 
-		hooks.on('enabled', handler);
-		const ledger = hooks.getRegistry().get('enabled');
+		hooks.on('enable', handler);
+		const ledger = hooks.getRegistry().get('enable');
 
 		expect(ledger).toBeDefined();
 		expect(ledger).toBeInstanceOf(Map);
@@ -33,7 +33,7 @@ describe('Hook registry', () => {
 		const swup = new Swup();
 		const handler = vi.fn();
 
-		const handlerReturned = swup.hooks.on('enabled', handler);
+		const handlerReturned = swup.hooks.on('enable', handler);
 
 		expect(handlerReturned).toEqual(handler);
 	});
@@ -42,9 +42,9 @@ describe('Hook registry', () => {
 		const swup = new Swup();
 		const handler = vi.fn();
 
-		swup.hooks.on('enabled', handler);
+		swup.hooks.on('enable', handler);
 
-		await swup.hooks.trigger('enabled');
+		await swup.hooks.trigger('enable');
 
 		expect(handler).toBeCalledTimes(1);
 	});
@@ -53,10 +53,10 @@ describe('Hook registry', () => {
 		const swup = new Swup();
 		const handler = vi.fn();
 
-		swup.hooks.on('enabled', handler, { once: true });
+		swup.hooks.on('enable', handler, { once: true });
 
-		await swup.hooks.trigger('enabled', undefined, () => {});
-		await swup.hooks.trigger('enabled', undefined, () => {});
+		await swup.hooks.trigger('enable', undefined, () => {});
+		await swup.hooks.trigger('enable', undefined, () => {});
 
 		expect(handler).toBeCalledTimes(1);
 	});
@@ -65,10 +65,10 @@ describe('Hook registry', () => {
 		const swup = new Swup();
 		const handler = vi.fn();
 
-		swup.hooks.once('enabled', handler);
+		swup.hooks.once('enable', handler);
 
-		await swup.hooks.trigger('enabled', undefined, () => {});
-		await swup.hooks.trigger('enabled', undefined, () => {});
+		await swup.hooks.trigger('enable', undefined, () => {});
+		await swup.hooks.trigger('enable', undefined, () => {});
 
 		expect(handler).toBeCalledTimes(1);
 	});
@@ -77,7 +77,7 @@ describe('Hook registry', () => {
 		const swup = new Swup();
 		const handler = vi.fn();
 
-		await swup.hooks.trigger('enabled', undefined, handler);
+		await swup.hooks.trigger('enable', undefined, handler);
 
 		expect(handler).toBeCalledTimes(1);
 	});
@@ -101,11 +101,11 @@ describe('Hook registry', () => {
 			}
 		};
 
-		swup.hooks.on('disabled', handlers.before, { before: true });
-		swup.hooks.on('disabled', handlers.normal, {});
-		swup.hooks.on('disabled', handlers.after, {});
+		swup.hooks.on('disable', handlers.before, { before: true });
+		swup.hooks.on('disable', handlers.normal, {});
+		swup.hooks.on('disable', handlers.after, {});
 
-		await swup.hooks.trigger('disabled', undefined, handlers.original);
+		await swup.hooks.trigger('disable', undefined, handlers.original);
 
 		expect(called).toEqual(['before', 'original', 'normal', 'after']);
 	});
@@ -135,13 +135,13 @@ describe('Hook registry', () => {
 			}
 		};
 
-		swup.hooks.on('disabled', handlers['1'], { priority: 1, before: true });
-		swup.hooks.on('disabled', handlers['2'], { priority: 2, before: true });
-		swup.hooks.on('disabled', handlers['4'], { priority: 5 });
-		swup.hooks.on('disabled', handlers['6'], { priority: 4 });
-		swup.hooks.on('disabled', handlers['5'], { priority: 4 });
+		swup.hooks.on('disable', handlers['1'], { priority: 1, before: true });
+		swup.hooks.on('disable', handlers['2'], { priority: 2, before: true });
+		swup.hooks.on('disable', handlers['4'], { priority: 5 });
+		swup.hooks.on('disable', handlers['6'], { priority: 4 });
+		swup.hooks.on('disable', handlers['5'], { priority: 4 });
 
-		await swup.hooks.trigger('disabled', undefined, handlers['3']);
+		await swup.hooks.trigger('disable', undefined, handlers['3']);
 
 		expect(called).toEqual([2, 1, 3, 4, 6, 5]);
 	});
@@ -151,9 +151,9 @@ describe('Hook registry', () => {
 		const customHandler = vi.fn();
 		const defaultHandler = vi.fn();
 
-		swup.hooks.on('enabled', customHandler, { replace: true });
+		swup.hooks.on('enable', customHandler, { replace: true });
 
-		await swup.hooks.trigger('enabled', undefined, defaultHandler);
+		await swup.hooks.trigger('enable', undefined, defaultHandler);
 
 		expect(customHandler).toBeCalledTimes(1);
 		expect(defaultHandler).toBeCalledTimes(0);
@@ -165,9 +165,9 @@ describe('Hook registry', () => {
 		const defaultHandler = vi.fn();
 		const ctx = swup.context;
 
-		swup.hooks.on('enabled', customHandler, { replace: true });
+		swup.hooks.on('enable', customHandler, { replace: true });
 
-		await swup.hooks.trigger('enabled', undefined, defaultHandler);
+		await swup.hooks.trigger('enable', undefined, defaultHandler);
 
 		expect(customHandler).toBeCalledWith(ctx, undefined, defaultHandler);
 	});
@@ -178,21 +178,21 @@ describe('Hook registry', () => {
 		const handler = vi.fn();
 		const ctx = swup.context;
 
-		swup.hooks.on('enabled', listener);
+		swup.hooks.on('enable', listener);
 
-		await swup.hooks.trigger('enabled', undefined, handler);
+		await swup.hooks.trigger('enable', undefined, handler);
 
 		expect(listener).toBeCalledWith(ctx, undefined, undefined);
 	});
 
 	it('should trigger event handler with context and args', async () => {
 		const swup = new Swup();
-		const handler: Handler<'popState'> = vi.fn();
+		const handler: Handler<'history:popstate'> = vi.fn();
 		const ctx = swup.context;
 		const args = { event: new PopStateEvent('') };
 
-		swup.hooks.on('popState', handler);
-		await swup.hooks.trigger('popState', args);
+		swup.hooks.on('history:popstate', handler);
+		await swup.hooks.trigger('history:popstate', args);
 
 		expect(handler).toBeCalledTimes(1);
 		expect(handler).toBeCalledWith(ctx, args, undefined);
@@ -204,15 +204,18 @@ describe('Types', () => {
 		const swup = new Swup();
 
 		// @ts-expect-no-error
-		swup.hooks.on('popState', (ctx: Context, { event }: { event: PopStateEvent }) => {});
+		swup.hooks.on(
+			'history:popstate',
+			(ctx: Context, { event }: { event: PopStateEvent }) => {}
+		);
 		// @ts-expect-no-error
-		await swup.hooks.trigger('popState', { event: new PopStateEvent('') });
+		await swup.hooks.trigger('history:popstate', { event: new PopStateEvent('') });
 
 		// @ts-expect-error
-		swup.hooks.on('popState', ({ event: MouseEvent }) => {});
+		swup.hooks.on('history:popstate', ({ event: MouseEvent }) => {});
 		// @ts-expect-error
-		swup.hooks.on('popState', (ctx: Context, { event }: { event: MouseEvent }) => {});
+		swup.hooks.on('history:popstate', (ctx: Context, { event }: { event: MouseEvent }) => {});
 		// @ts-expect-error
-		await swup.hooks.trigger('popState', { event: new MouseEvent('') });
+		await swup.hooks.trigger('history:popstate', { event: new MouseEvent('') });
 	});
 });