@spaceavocado/svelte-router

Simple Svelte Router for Single Page Applications (SPA).

Showing:

Popularity

Downloads/wk

42

GitHub Stars

54

Maintenance

Last Commit

10mos ago

Contributors

1

Package

Dependencies

1

Size (min+gzip)

9.5KB

License

MIT

Type Definitions

Tree-Shakeable

Yes?

Categories

Readme

Svelte Router

Simple Svelte Router for Single Page Applications (SPA), inspired by Vue Router. Features include:

  • Nested route/view mapping.
  • Modular, component-based router configuration.
  • Route params, query, wildcards (powered by path-to-regexp).
  • Navigation guards for navigation control.
  • Links with automatic active CSS classes.
  • HTML5 history mode or hash mode (powered by history).

To see the details code documentation, please read the Code Documentation.

Quick Links

Table of Content

Installation via NPM or Yarn

npm install -D @spaceavocado/svelte-router
yarn add @spaceavocado/svelte-router -D

Webpack Setup

Please see the Svelte Webpack Template. Important setup in the webpack.config.js:

resolve: {
  // This alias is important to prevent svelte mismatch
  // between your code, and the 3rd party components.
  alias: {
    svelte: path.resolve('node_modules', 'svelte')
  },
  extensions: ['.mjs', '.js', '.svelte'],
  mainFields: ['svelte', 'browser', 'module', 'main']
},

module: {
  rules: [
    {
      test: /\.svelte$/,
      // Do not exclude: /(node_modules)/ since the router 
      // components are located in the node_modules
      use: {
        loader: 'svelte-loader',
        options: {
          emitCss: true,
          hotReload: true
        }
      }
    }
  ]
}

Webpack Boilerplate Project Template

For a quick start, you can use svelte-router-template - Webpack boilerplate template project for spaceavocado/svelte-router.

Live Preview.

Rollup Setup

rollup.config.js:

import babel from 'rollup-plugin-babel';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import svelte from 'rollup-plugin-svelte';

export default {
  input: './src/index.js',
  output: {
    file: './dist/bundle.js',
    format: 'iife'
  },
  plugins: [
    resolve(),
    commonjs({
      include: 'node_modules/**',
    }),
    svelte(),
    babel({
      exclude: 'node_modules/**',
    }),
  ]
}

Essentials

Note: All code below uses ES2015+.

Setup the Router

All we need to do is map our components to the routes and add root RouterView component, here's a basic example of app component uses as the main Svelte component.

index.js:

// The base component
import App from './app.svelte';

// Turn on the engine
new App({
  target: document.body,
});

app.svelve:

<script>
import createRouter from '@spaceavocado/svelte-router';
import RouterView from '@spaceavocado/svelte-router/component/view';

// View components
import ViewHome from './views/home.svelte';
import View404 from './views/404.svelte';

createRouter({
  routes: [
    { 
      path: '/',
      name: 'HOME',
      component: ViewHome,
    },
    {
      path: '*',
      component: View404,
    },
  ],
});
</script>

<RouterView />

This is an example of the minimalist plug & play setup of the Svelte Router. For more details:

Route Configuration

A route can be configure with these properties:

PropertyDescriptionType
pathA string that equals the path of the current route, always resolved as an absolute path. e.g. "/foo/bar". Please see Dynamic Route Configuration for advanced usage.string
redirectRedirection to different route, or to external site. Please see Route Redirection for advanced usage.boolean, object, function
nameThe name of the current route, optional.string
componentSvelte component. It could be be omitted if the route has nested routes.function
metaRoute meta object, meta is used a bucket for your custom data on route object.object
propsDeclaration of component properties passed to the component by the route. Please see Passing Props to Route Components for more details.boolean, object, function
childrenCollection of children/nested routes. Please see Nested Routes for more details.object[]
createRouter({
  routes: [
    { 
      path: '/',
      name: 'HOME',
      component: ViewHome,
      meta: {
        static: 5,
        dynamic: () => {return 5;},
      }
    },
    { 
      path: '/services',
      name: 'SERVICES',
      children: [
        { 
          path: '/',
          name: 'SERVICES_HOME',
          component: ViewServices,
        },
        { 
          path: '/design',
          name: 'SERVICES_DESIGN',
          component: ViewServicesDesign,
          props: {
            title: 'Services / Design',
          }
        }
      ]
    },
    { 
      path: '/users/:id(\\d+)',
      name: 'USER_PROFILE',
      component: ViewUserProfile,
      props: (route) => {
        return {
          userId: route.params.id,
        };
      },
    }
  ],
});

Dynamic Route Configuration

  • The route path could contain dynamic parameters, e.g. path: '/users/:id'. Please see path-to-regexp for more information how to configure the name, optional, etc,. parameters.
  • Special case is path: '*' which means any URL, this route should be the last route in your routes definition.
  • All resolved dynamic parameters are accessible on the resolved route object.

Route Redirection

To redirect a route, you can configure it like so:

// Redirect from /a to /b
{ 
  path: '/a',
  redirect: '/b',
}

// External site redirect
// Must start with 'http' or 'https'
{ 
  path: '/a',
  redirect: 'https://github.com/spaceavocado/svelte-router',
}

// Redirect to named route
{ 
  path: '/a',
  redirect: {
    name: 'ROUTE_NAME',
  },
}

// Dynamic redirecting
// "to" is Route Object
// Return value must be URL or a Route.
{ 
  path: '/a',
  redirect: (to) => {
    return {
      name: 'ROUTE_NAME',
    }
  },
}

Please see Route Object for more details.

Passing Props to Route Components

By default, props are not automatically passed to the route component, this could be change to:

Automatically Pass Route Params as Component Props

All route dynamic props are auto-passed to the component.

// app.svelte
{ 
  path: '/users/:id(\\d+)',
  name: 'USER_PROFILE',
  component: ViewUserProfile,
  props: true,
}

// ViewUserProfile.svelte
export let id;

Pass Custom Object as Component Props

// app.svelte
{ 
  path: '/',
  name: 'HOME',
  component: ViewHome,
  props: {
    title: 'Hello World',
  },
}

// ViewHome.svelte
export let title;

Use a Function to Resolve the Component Props

Please see Route Object for more details.

// app.svelte
{ 
  path: '/users/:id(\\d+)',
  name: 'USER_PROFILE',
  component: ViewUserProfile,
  props: (route) => {
    return {
      title: 'User Profile',
      id: route.params.id,
    }
  },
}

// ViewUserProfile.svelte
export let title;
export let id;

Auto Passed Route Prop

Route property of type Route Object is auto-passed to the route component.

component.svelte:

<script>
export let route;
</script>

Nested Routes

  • Please see Route Configuration for the base information about the routes configuration.

  • Nested route has access to its own parameters and all parent's route parameters:

    { 
      path: '/users/:id(\\d+)',
      children: [
        {
          path: '/',
          name: 'USER_DETAIL',
          component: ViewUserDetail,
        },
        {
          path: '/message/:msgId(\\d+)',
          name: 'USER_MESSAGE',
          component: ViewUserMessageDetail,
        }
      ],
    },
    {
      path: '/services',
      component: ViewServices,
      children: [
        path: '/design',
        component: ViewServicesDesign,
      ]
    }
    
    // Resolved USER_MESSAGE route will have access to 
    // id, and msgId params.
    
  • The component could be omitted if the route has nested routes (please see the example above), it this case ViewRouter component will be used to pass through the components, please see Router View for more details.

  • If the route has nested routes, and it has defined component, than the component must internally use Router View component to pass though the routed view components on it's own nested routes.

  • All routes should start with trailing slash.

<script>
import RouterLink from '@spaceavocado/svelte-router/component/link';

<!-- URL -->
<RouterLink to='/services/design'>Navigate by URL</RouterLink>

<!-- Location Object -->
<RouterLink to={{name: 'USER_DETAIL', params:{id: 5}}>Navigate by Location</RouterLink>

The route link is the base component for routing action. The route link renders a < a > DOM element with click action triggering route change. The active class is auto-resolved. Component parameters:

PropertyDescriptionType
tonavigation URL or navigation Location, please see Location Object for more details.string, Location
replaceReplace rather the than push into the history, defaults to false.boolean
exactThe default active class matching behavior is inclusive match, i.e. it matches if the URL starts with link url. In the exact mode, the url must be be the same (excluding query param and hash param). Defaults to false.boolean
clsLink base class name, defaults to ''.string
activeClassLink active class name, if not defined, it defaults to the active class defined on the router.string
disabledDisable navigation action, and set "disabled" css class. Defaults to false.boolean
<script>
import RouterLink from '@spaceavocado/svelte-router/component/link';

// Navigation has been completed
function handleOnCompleted() {}

// Navigation has been aborted
function handleOnAborted() {}
</script>

<RouterLink to='/services/design' on:completed={handleOnCompleted} on:aborted={handleOnAborted}>Navigate by URL</RouterLink>

Router View Component

<script>
import RouterView from '@spaceavocado/svelte-router/component/view';
</script>

<RouterView />
  • The route view acts as SLOT for resolved route component, i.e. it will be replaced run-time with the component defined on the given route.
  • For nested routes, if the parent route (the one having nested routes) has defined it's own component, the component must use internally ViewRouter component to pass through the nested route components. If the component is not defined on the parent route, it auto pass through.

Advanced

Programmatic Navigation

Besides the Router Link Component, the route could be changed like so:

import {router} from '@spaceavocado/svelte-router';

// URL
$router.push('/services/design');

// Location Object
$router.push({
  name: 'USER_DETAIL',
  params: {
    id: 5,
  },
  query: {
    code: 'system_code',
  },
  hash: 'sidebar',
});

Note: the router can be accessed after the createRouter function is executed, and it must be accessed as $router since it is Svelte read-able store object.

More information:

The navigation guards are primarily used to guard navigations either by redirecting it or canceling it.

Create a Navigation Guard

import {router} from '@spaceavocado/svelte-router';

// The guard callback is called when a navigation occurs,
// the next function must be called to continue the navigation.
$router.navigationGuard((from, to, next) => {
  next();
});

Note: the router can be accessed after the createRouter function is executed, and it must be accessed as $router since it is Svelte read-able store object.

"from, to" are Route Object.

Continue
next();

Move on to the next hook in the pipeline. If no hooks are left, the navigation is confirmed.

Abort
next(false);

Abort the current navigation.

Redirect
// URL
next('/services/design');

// Location Object
next({
  name: 'USER_DETAIL',
  params: {
    id: 5,
  },
  query: {
    code: 'system_code',
  },
  hash: 'sidebar',
});

Abort the current navigation and redirect to new navigation.

More information:

Error
next(new Error('internal error'));

Abort the current navigation, and trigger onError event on the router.

More information:

Lazy Loaded Component

View components could be loaded asynchronously, i.e. lazy loading, e.g:

app.svelve

<script>
import createRouter from '@spaceavocado/svelte-router';
import RouterView from '@spaceavocado/svelte-router/component/view';

// View components
import ViewHome from './views/home.svelte';
import View404 from './views/404.svelte';

// Webpack dynamic import
const asyncView = (view) => {
  return new Promise((resolve) => {
    const component = import(/* webpackChunkName: "view-[request]" */ `./view/${view}.svelte`);
    resolve(component);
  });
};

createRouter({
  routes: [
    { 
      path: '/',
      name: 'HOME',
      component: ViewHome,
    },
    {
      path: 'some-page',
      component: asyncView('some-page'),
    }
    {
      path: '*',
      component: View404,
    },
  ],
});
</script>

<RouterView />

Note: To load async component, the load function must return a Promise, and resolve must return a module object:

{
  // The default (default module export function) must the Svelte Component function
  // Please see the webpack example above.
  default: function(){}
}.

API

To see the details code documentation, please read the Code Documentation

Create Router

import createRouter from '@spaceavocado/svelte-router';

// Please see the opts below.
const routerOpts = {};

// Router is Svelte read-able store, therefore it must be
// accessed with $ prefix, e.g. $router.
// To access the router instance in other files, please see "Access Router Instance".
const router = createRouter(routerOpts);

Throws when the route options are invalid.

Router Options

PropertyDescriptionType
modeHistory mode. Supported values: 'HISTORY', 'HASH'.string
basenameThe base URL of the app, defaults to ''.string
hashTypeHash type. Relevant only for HISTORY_MODE.HASH. Supported values: 'PUSH', 'REPLACE'.string
routesrouter routes.object[]
activeClassCSS class applied on the active route link. Defaults to "active".string

Note: History modes could be accessed like so:

import {ROUTER_MODE} from '@spaceavocado/svelte-router';

Note: Hash types could be accessed like so:

import {HASH_TYPE} from '@spaceavocado/svelte-router';

More information:

Access Router Instance

import {router} from '@spaceavocado/svelte-router';

Note: It must be accessed as $router since it is the Svelte read-able store object, to resolved auto subscribe/unsubscribe.

Router Methods

All route methods are accessible on the router, please see Access Router Instance.

start

$router.start();

This method is auto-called by the root Router View Component; it handles the on page load route resolution.

push

Push to navigation.

$router.push(rawLocation, onComplete, onAbort);

Throws when the rawLocation is invalid or when the path is invalid.

Parameters:

NameDescriptionType
rawLocationraw path or location object.string, Location
onCompleteOn complete callback function. Optional.function
onAbortOn abort callback function. Optional.function

replace

Replace in navigation.

$router.replace(rawLocation, onComplete, onAbort);

Throws when the rawLocation is invalid or when the path is invalid.

Parameters:

NameDescriptionType
rawLocationraw path or location object.string, Location
onCompleteOn complete callback function. Optional.function
onAbortOn abort callback function. Optional.function

back

Go one step back in the navigation history.

$router.back();

forward

Go one step forward in the navigation history.

$router.forward();

go

Go to a specific history position in the navigation history.

$router.go(n);

Parameters:

NameDescriptionType
nnumber of steps to forward or backwards (negative number).number

routeURL

Generate route URL from the the raw location.

const url = $router.routeURL(rawLocation);

Parameters:

NameDescriptionType
rawLocationraw location object.Location

Register a navigation guard which will be called whenever a navigation is triggered. All registered navigation guards are resolved in sequence. Navigation guard must call the next() function to continue the execution of navigation change. Please see Navigation Guard

const unregister = $router.navigationGuard(guard);

// To unregister the navigation guard:
unregister();
unregister = null;

Parameters:

NameDescriptionType
guardguard callback function with (from, to, next) signature. Please see Navigation Guard Next Actionfunction

"from, to" are Route Object.

onBeforeNavigation

Register a callback which will be called before execution of navigation guards.

const unregister = $router.onBeforeNavigation(callback);

// To unregister the event:
unregister();
unregister = null;

Parameters:

NameDescriptionType
callbackcallback function with fn(from, to) signature.function

"from, to" are Route Object.

onNavigationChanged

Register a callback which will be called when all navigation guards are resolved, and the final navigation change is resolved.

const unregister = $router.onNavigationChanged(callback);

// To unregister the event:
unregister();
unregister = null;

Parameters:

NameDescriptionType
callbackcallback function with fn(from, to) signature.function

"from, to" are Route Object.

onError

Register a callback which will be called when an error is caught during a route navigation.

const unregister = $router.onError(callback);

// To unregister the event:
unregister();
unregister = null;

Parameters:

NameDescriptionType
callbackcallback function with fn(error) signature.function

Location Object

PropertyDescriptionType
nameName of the route.string
paramsRoute params dictionary object, if the route has defined dynamic route parameters, this object is required, with valid params to resolve the route URL.object
queryRoute query dictionary object. Optional.object
hashRoute hash parameter. Optional.string
// Location object example
{
  name: 'USER_DETAIL',
  params: {
    id: 5,
  },
  query: {
    code: 'system_code',
  },
  hash: 'sidebar',
}

// Considering that USER_DETAIL is /user/:id(\\d+)
// the resolved URL will be:
// /user/5?code=system_code#sidebar

Route Object

PropertyDescriptionType
namename of the route.string
pathlocation path use to resolve the route.string
hashurl hash.string
fullPaththe full resolved URL including query and hash.string
paramsroute resolved params.object
queryquery parameters.object
metaroute meta.object
actionroute action.string
matchedresolved route records, please see Route Record Object In the case of nested route, it contains all matched routes, starting from root to the deepest route node.object[]

Route Record Object

PropertyDescriptionType
pathlocation path use to resolve the route.string
namename of the route.string
componentsvelte component.string
metaroute meta.string
paramsroute resolved params.string
propsprops passed to component, please see Passing Props to Route Components.boolean, object, function

Changes

To see the changes that were made in a given release, please lookup the tag on the releases page. The full changelog could be seen here changelog.md

About

This project was inspired by Vue Router, designed mainly to explore and test Svelte in SPA realm. Any feedback, contribution to this project is welcomed.

The project is in a beta phase, therefore there might be major changes in near future, the annotation should stay the same, though.

Contributing

When contributing to this repository, please first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before making a change.

Pull Request Process

  1. Fork it
  2. Create your feature branch (git checkout -b ft/new-feature-name)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin ft/new-feature-name)
  5. Create new Pull Request

    Please make an issue first if the change is likely to increase.

License

Svelte Router is released under the MIT license. See LICENSE.txt.

Rate & Review

Great Documentation0
Easy to Use0
Performant0
Highly Customizable0
Bleeding Edge0
Responsive Maintainers0
Poor Documentation0
Hard to Use0
Slow0
Buggy0
Abandoned0
Unwelcoming Community0
100