Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 6 additions & 34 deletions Advanced Usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,10 +248,10 @@ const routes = {

In case a condition fails, the router emits the `conditionsFailed` event, with the same `detail` dictionary.

You can listen to the `conditionsFailed` event and perform actions in case no route wasn't loaded because of a failed pre-condition:
You can listen to the `conditionsFailed` event and perform actions in case no route wasn't loaded because of a failed pre-condition. In Svelte 5, event handlers are passed as props with the `on` prefix:

````svelte
<Router {routes} on:conditionsFailed={conditionsFailed} on:routeLoaded={routeLoaded} />
<Router {routes} onconditionsFailed={conditionsFailed} onrouteLoaded={routeLoaded} />

<script>
// Handles the "conditionsFailed" event dispatched by the router when a component can't be loaded because one of its pre-condition failed
Expand Down Expand Up @@ -310,35 +310,7 @@ const routes = {

## `routeEvent` event

The custom `routeEvent` event can be used to bubble events from a component displayed by the router, to the router's parent component.

For example, assume that your Svelte component `App` contains the router's component `Router`. Inside the router, the current view is displaying the `Foo` component. If `Foo` emitted an event, `Router` would receive it and would ignore it by default

Using the custom event **`routeEvent`**, instead, allows your components within the router (such as `Foo`) to bubble an event to the `Router` component's parent.

Example for `App.svelte`:

```svelte
<Router {routes} on:routeEvent={routeEvent} />
<script>
import Router from 'svelte-spa-router'
import Foo from './Foo.svelte'
const routes = {'*': Foo}
function routeEvent(event) {
// Do something
}
</script>
```

Example for `Foo.svelte`:

```svelte
<button on:click={() => dispatch('routeEvent', {foo: 'bar'})}>Hello</button>
<script>
import {createEventDispatcher} from 'svelte'
const dispatch = createEventDispatcher()
</script>
```
> **Note:** The `routeEvent` mechanism has been removed in Svelte 5, as automatic event forwarding is no longer supported. Instead, you should explicitly pass callback props to your components or use other patterns like stores or context for communication between components.

## `routeLoading` and `routeLoaded` events

Expand Down Expand Up @@ -387,13 +359,13 @@ event.detail = {
}
```

For example:
For example (Svelte 5 syntax with event handler props):

````svelte
<Router
{routes}
on:routeLoading={routeLoading}
on:routeLoaded={routeLoaded}
onrouteLoading={routeLoading}
onrouteLoaded={routeLoaded}
/>

<script>
Expand Down
83 changes: 83 additions & 0 deletions MIGRATION_SVELTE5.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Migration Guide: Svelte 4 to Svelte 5

This guide will help you migrate your svelte-spa-router usage from Svelte 4 to Svelte 5.

## Event Handling Changes

The main breaking change in Svelte 5 is how events are handled. Instead of using `on:` directives, events are now passed as props with the `on` prefix.

### Before (Svelte 4)

```svelte
<Router
{routes}
on:routeLoading={handleRouteLoading}
on:routeLoaded={handleRouteLoaded}
on:conditionsFailed={handleConditionsFailed}
/>
```

### After (Svelte 5)

```svelte
<Router
{routes}
onrouteLoading={handleRouteLoading}
onrouteLoaded={handleRouteLoaded}
onconditionsFailed={handleConditionsFailed}
/>
```

## Removed Features

### `routeEvent`

The `routeEvent` mechanism for bubbling events from child components has been removed, as Svelte 5 no longer supports automatic event forwarding.

**Alternative solutions:**
- Use callback props explicitly
- Use Svelte stores for state management
- Use Svelte context API for component communication

### Before (Svelte 4)

```svelte
<!-- Parent component -->
<Router {routes} on:routeEvent={handleRouteEvent} />

<!-- Child component -->
<button on:click={() => dispatch('routeEvent', {foo: 'bar'})}>
Click me
</button>
```

### After (Svelte 5)

Use callback props instead:

```svelte
<!-- Parent component -->
<Router {routes} />

<!-- Pass callbacks to child components via route props or stores -->
```

## What Stays the Same

The following features continue to work without changes:

- Route definitions
- Dynamic imports with `wrap`
- Pre-conditions
- Static props
- Stores (`$location`, `$querystring`, `$params`)
- Navigation functions (`push`, `pop`, `replace`)
- `link` action
- Nested routers

## Summary

The migration is minimal - you only need to:
1. Update event handler syntax from `on:eventName` to `oneventName`
2. Remove any usage of `routeEvent` and replace with explicit callbacks or stores
3. Update your `package.json` to use Svelte 5
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
[![npm](https://img.shields.io/npm/v/svelte-spa-router.svg)](https://www.npmjs.com/package/svelte-spa-router)
[![GitHub](https://img.shields.io/github/license/ItalyPaleAle/svelte-spa-router.svg)](https://github.com/ItalyPaleAle/svelte-spa-router/blob/master/LICENSE.md)

This module is a router for [Svelte 3 and 4](https://github.com/sveltejs/svelte) applications, specifically optimized for Single Page Applications (SPA).
This module is a router for [Svelte 5](https://github.com/sveltejs/svelte) applications, specifically optimized for Single Page Applications (SPA).

Main features:

Expand Down Expand Up @@ -67,7 +67,7 @@ The sample will be running at http://localhost:5050

## Starter template

You can find a starter template with Svelte 4 and svelte-spa-router at [italypaleale/svelte-spa-router-template](https://github.com/italypaleale/svelte-spa-router-template).
You can find a starter template with Svelte and svelte-spa-router at [italypaleale/svelte-spa-router-template](https://github.com/italypaleale/svelte-spa-router-template).

To use the template:

Expand All @@ -80,7 +80,7 @@ More information can be found on the [template's repo](https://github.com/italyp

## Using svelte-spa-router

You can include the router in any project using Svelte 3 or 4.
You can include the router in any project using Svelte 5.

### Install from NPM

Expand Down
92 changes: 52 additions & 40 deletions Router.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
///<reference types="svelte" />

import {SvelteComponent, ComponentType} from 'svelte'
import {Component} from 'svelte'
import {Readable} from 'svelte/store'

/** Dictionary with route details passed to the pre-conditions functions, as well as the `routeLoading` and `conditionsFailed` events */
Expand All @@ -24,7 +24,7 @@ export interface RouteDetail {
/** Detail object for the `routeLoaded` event */
export interface RouteDetailLoaded extends RouteDetail {
/** Svelte component */
component: ComponentType
component: Component

/** Name of the Svelte component that was loaded (note: might be minified in production) */
name: string
Expand All @@ -34,7 +34,7 @@ export interface RouteDetailLoaded extends RouteDetail {
* This is a Svelte component loaded asynchronously.
* It's meant to be used with the `import()` function, such as `() => import('Foo.svelte')}`
*/
export type AsyncSvelteComponent = () => Promise<{default: ComponentType}>
export type AsyncSvelteComponent = () => Promise<{default: Component}>

/**
* Route pre-condition function. This is a callback that receives a RouteDetail object as argument containing information on the route that we're trying to load.
Expand All @@ -50,7 +50,7 @@ export type RoutePrecondition = (detail: RouteDetail) => (boolean | Promise<bool
/** Object returned by the `wrap` method */
export interface WrappedComponent {
/** Component to load (this is always asynchronous) */
component: ComponentType
component: Component

/** Route pre-conditions to validate */
conditions?: RoutePrecondition[]
Expand Down Expand Up @@ -152,8 +152,8 @@ export const params: Readable<Record<string, string> | undefined>
// Note: the above is implemented as writable but exported as readable because consumers should not modify the value

/** List of routes */
export type RouteDefinition = Record<string, ComponentType | WrappedComponent> |
Map<string | RegExp, ComponentType | WrappedComponent>
export type RouteDefinition = Record<string, Component | WrappedComponent> |
Map<string | RegExp, Component | WrappedComponent>

/** Generic interface for events from the router */
interface RouterEvent<T> {
Expand All @@ -169,41 +169,53 @@ export type RouteLoadingEvent = RouterEvent<RouteDetail>
/** Event type for routeLoaded */
export type RouteLoadedEvent = RouterEvent<RouteDetailLoaded>

/**
* Router component props
*/
export interface RouterProps {
/**
* Dictionary of all routes, in the format `'/path': component`.
*
* For example:
* ````js
* import HomeRoute from './routes/HomeRoute.svelte'
* import BooksRoute from './routes/BooksRoute.svelte'
* import NotFoundRoute from './routes/NotFoundRoute.svelte'
* routes = {
* '/': HomeRoute,
* '/books': BooksRoute,
* '*': NotFoundRoute
* }
* ````
*/
routes: RouteDefinition
/**
* Optional prefix for the routes in this router. This is useful for example in the case of nested routers.
*/
prefix?: string | RegExp
/**
* If set to true, the router will restore scroll positions on back navigation
* and scroll to top on forward navigation.
*/
restoreScrollState?: boolean
/**
* Event handler for when a route fails its pre-conditions
*/
onconditionsFailed?: (event: ConditionsFailedEvent) => void
/**
* Event handler for when a route is loading
*/
onrouteLoading?: (event: RouteLoadingEvent) => void
/**
* Event handler for when a route has loaded
*/
onrouteLoaded?: (event: RouteLoadedEvent) => void
}

/**
* Router component
*/
export default class Router extends SvelteComponent {
// Props
$$prop_def: {
/**
* Dictionary of all routes, in the format `'/path': component`.
*
* For example:
* ````js
* import HomeRoute from './routes/HomeRoute.svelte'
* import BooksRoute from './routes/BooksRoute.svelte'
* import NotFoundRoute from './routes/NotFoundRoute.svelte'
* routes = {
* '/': HomeRoute,
* '/books': BooksRoute,
* '*': NotFoundRoute
* }
* ````
*/
routes: RouteDefinition,
/**
* Optional prefix for the routes in this router. This is useful for example in the case of nested routers.
*/
prefix?: string | RegExp,
/**
* If set to true, the router will restore scroll positions on back navigation
* and scroll to top on forward navigation.
*/
restoreScrollState?: boolean
}

$on(event: 'routeEvent', callback: (event: CustomEvent) => void): () => void
$on(event: 'conditionsFailed', callback: (event: ConditionsFailedEvent) => void): () => void
$on(event: 'routeLoading', callback: (event: RouteLoadingEvent) => void): () => void
$on(event: 'routeLoaded', callback: (event: RouteLoadedEvent) => void): () => void
export default class Router {
constructor(options: { target: Element; props: RouterProps })
$$prop_def: RouterProps
}
Loading