with npm
npm i svelte-simple-router
Ensure your local server is configured in SPA mode. In a default Svelte installation you need to edit your package.json and add --single to sirv public
.
"start": "sirv public --single"
create routes.js file into your project directory
import AdminLayout from './views/layouts/Admin.svelte';
import PublicLayout from './views/layouts/Public.svelte';
import Dashboard from './views/pages/Dashboard.svelte';
import MembersList from './views/pages/MembersList.svelte';
import Page404 from './views/pages/404.svelte';
const routes = {
groupGuard: [
{
url: [/^members/],
with: async function (routerData, route) {
return true;
},
redirectOnFail: function (routerData, route) {
return '/dashboard';
}
}
],
routes: [
{
name: "dashboard",
url: [/^dashboard$/, /^\s*$/],
guard: {
with: async function (routerData, route) {
return true;
},
redirectOnFail: '/404'
},
layout: AdminLayout,
component: Dashboard
},
{
name: "members",
url: [/^members/],
urlPathMapKeys: ['', 'key1', 'key2'],
searchFilter: async function (routerData, route) {
return true;
},
guard: {
with: async function (routerData, route) {
return true;
},
redirectOnFail: '/404'
},
layout: AdminLayout,
component: MembersList
},
{
name: "404",
url: [/^404$/],
layout: PublicLayout,
component: Page404
}
]
};
export { routes }
<script>
import { Router } from 'svelete-simple-router';
import { routes } from './routes.js';
</script>
<Router routes={routes} />
for example : import AdminLayout from './views/layouts/Admin.svelte';
we created Admin.svelte file
<script>
import {Route} from 'svelete-simple-router';
export let currentRoute = {};
</script>
<div>
<h1>Admin Layout</h1>
<a href="/dashboard">Dashboard</a>
<a href="/members">Member List</a>
<a href="/404">404</a>
<Route {currentRoute} />
</div>
for example : import Dashboard from './views/pages/Dashboard.svelte';
we created Dashboard.svelte file
<script>
export let currentRoute = {};
</script>
<h1>Dashboard</h1>
import AdminLayout from './views/layouts/Admin.svelte';
import PublicLayout from './views/layouts/Public.svelte';
import Dashboard from './views/pages/Dashboard.svelte';
import MembersList from './views/pages/MembersList.svelte';
import Page404 from './views/pages/404.svelte';
const routes = {
groupGuard: [
{
url: [/^members/],
with: async function (routerData, route) {
return true;
},
redirectOnFail: function (routerData, route) {
return '/dashboard';
}
},
{
url: [/^dashboard/],
with: async function (routerData, route) {
return true;
},
redirectOnFail:"/login"
}
],
routes: [
{
name: "dashboard",
url: [/^dashboard$/, /^\s*$/],
guard: {
with: async function (routerData, route) {
return true;
},
redirectOnFail: '/404'
},
layout: AdminLayout,
component: Dashboard
},
{
name: "members",
url: [/^members/],
urlPathMapKeys: ['', 'key1', 'key2'],
searchFilter: async function (routerData, route) {
return true;
},
guard: {
with: async function (routerData, route) {
return true;
},
redirectOnFail: '/404'
},
layout: AdminLayout,
component: MembersList
},
{
name: "404",
url: [/^404$/],
layout: PublicLayout,
component: Page404
}
]
};
export { routes }
groupGuard: [
{
url: [/^members/],
with: async function (routerData, route) {
return true;
},
redirectOnFail: function (routerData, route) {
return '/dashboard';
}
}
]
Using group guard you can guard the multiple routes using matching starting path or any regular expression that you want to check.
Fields | Description |
---|---|
groupGuard: | array of object hold all your group routes for your application |
groupGuard:url: | array of regular expression that you want to match for that particular group route |
groupGuard:with: | a callback function for checking and guarding particular route. must return either true or false |
groupGuard:redirectOnFail: | a function or string url for redirect if guard is fail. if it is a function then must return valid url |
groupGuard always check and run after specific url guard is completed.
routes: [
{
name: "dashboard",
url: [/^dashboard$/, /^\s*$/],
searchFilter: async function (routerData, route) {
return true;
},
guard: {
with: async function (routerData, route) {
return true;
},
redirectOnFail: '/404'
},
layout: AdminLayout,
component: Dashboard
},
{
name: "404",
url: [/^404$/],
layout: PublicLayout,
component: Page404
}
]
Fields | Description |
---|---|
routes: | array of object hold all your routes for your application |
routes:name: | you need to provide unique name for each of your routes. |
routes:url: | array of regular expression that you want to match for that particular route |
routes:urlPathMapKeys: | array of keyname that assign to each path item. |
routes:searchFilter: | a callback function for extra custom matching function if you required other than regular expression |
routes:guard: | if you want to guard that particular route for some conditional check. |
routes:guard:with: | a callback function for checking and guarding particular route. must return either true or false |
routes:guard:redirectOnFail: | a function or string url for redirect if guard is fail. if it is a function then must return valid url |
routes:layout: | layout component that you want to load, if you dont provide then only component is loaded. |
routes:component: | page component that you want to load under the layout or without layout. |
{
name: "404",
url: [/^404$/],
layout: PublicLayout,
component: Page404
}
404 named route is required to display the component if no component or layout is found.
every layout and component passed currentRoute props to easily available some router related information.
for example if you visit this url
http://localhost:5000/members/name/rajdeep?quervar1=value1
{
"routePosition": 1,
"routeName": "members",
"pathName": "/members/name/rajdeep",
"pageName": "members",
"singleParams": [
"members",
"name",
"rajdeep"
],
"namedParams": {
"name": "rajdeep"
},
"queryParams": {
"quervar1": "value1"
},
"layout": {
"viewed": false
},
"component": {
"viewed": false
}
}
you can define all above callback function in Synchronous or Asynchronous manner as per your application need.
all above function in your routes object passed two parameter
routerData contains
for example
"pathName": "/members/name/rajdeep",
"pageName": "members",
"singleParams": [
"members",
"name",
"rajdeep"
],
"namedParams": {
"name": "rajdeep"
},
"queryParams": {
"quervar1": "value1"
}
route contains whole object of your route that you defined in routes object
for example
{
name: "dashboard",
url: [/^dashboard$/, /^\s*$/],
searchFilter: async function (routerData, route) {
return true;
},
guard: {
with: async function (routerData, route) {
return true;
},
redirectOnFail: '/404'
},
layout: AdminLayout,
component: Dashboard
}
<a href="">
in routerBy default router will capture all the <a href="">
onclick event and based on the href it will match the route
if it's not same domain link then it will just do the regular redirect
if it's same domain link then it will start the matching process and load the matched route
<a href="" class="no-follow">
if you dont want capture some of your <a>
link in router by default.
then just add the class="no-follow" and those click event will not goes into matching.
import {RouterRedirect} from 'svelte-simple-router';
will provide you redirect functionality at code level.
usage example
RouterRedirect('/some-other-url');
RouterRedirect('https://someother.url');
noFollow you can also pass no noFollow true or false in second parameter to tell router whether you bypass the matching or not.
By default noFollow is set to false
usage example
RouterRedirect('/some-other-url',true); //will not goes into route matching and directly do regular page redirect
there is example in examples/ssr folder on github.
import svelte from 'rollup-plugin-svelte';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import livereload from 'rollup-plugin-livereload';
import { terser } from 'rollup-plugin-terser';
const production = !process.env.ROLLUP_WATCH;
export default [
//browser bundel
{
input: 'src/main.js',
output: {
sourcemap: true,
format: 'iife',
name: 'app',
file: 'public/build/bundle.js'
},
plugins: [
svelte({
// enable run-time checks when not in production
dev: !production,
// we'll extract any component CSS out into
// a separate file - better for performance
css: css => {
css.write('public/build/bundle.css');
},
hydratable: true
}),
// If you have external dependencies installed from
// npm, you'll most likely need these plugins. In
// some cases you'll need additional configuration -
// consult the documentation for details:
// https://github.com/rollup/plugins/tree/master/packages/commonjs
resolve({
browser: true,
dedupe: ['svelte']
}),
commonjs(),
// In dev mode, call `npm run start` once
// the bundle has been generated
!production && serve(),
// Watch the `public` directory and refresh the
// browser on changes when not in production
!production && livereload('public'),
// If we're building for production (npm run build
// instead of npm run dev), minify
production && terser()
],
watch: {
clearScreen: false
}
},
//server ssr bundel
{
input: "src/App.svelte",
output: {
sourcemap: false,
format: "cjs",
name: "app",
file: "public/App.js"
},
plugins: [
svelte({
generate: "ssr"
}),
resolve(),
commonjs(),
production && terser()
]
}
];
function serve() {
let started = false;
return {
writeBundle() {
if (!started) {
started = true;
require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
stdio: ['ignore', 'inherit', 'inherit'],
shell: true
});
}
}
};
}
<script>
import { Router } from 'svelte-simple-router';
import { routes } from './routes.js';
// Used for SSR. A falsy value is ignored by the Router.
export let url = "";
</script>
<Router routes={routes} url="{url}" />
import App from './App.svelte';
const app = new App({
target: document.body,
hydrate: true
});
export default app;
you can use any server of your choice
const http = require('http');
const App = require('./public/App.js');
const port = 3000
const requestHandler = (request, response) => {
let url = 'http://' + request.headers.host + request.url;
const { head, html, css } = App.render({ url: url });
let output = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel='stylesheet' href='http://localhost:5000/global.css'>
<link rel='stylesheet' href='http://localhost:5000/build/bundle.css'>
<script defer src='http://localhost:5000/build/bundle.js'></script>
</head>
<body>${html}</body>
</html>`;
response.end(output);
}
const server = http.createServer(requestHandler)
server.listen(port, (err) => {
if (err) {
return console.log('something bad happened', err)
}
console.log(`server is listening on ${port}`)
})
there is example in examples/code-splitting folder on github.
import svelte from 'rollup-plugin-svelte';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import livereload from 'rollup-plugin-livereload';
import { terser } from 'rollup-plugin-terser';
const production = !process.env.ROLLUP_WATCH;
export default [
//browser bundel for code splitting
{
input: 'src/main.js',
output: {
sourcemap: true,
format: 'es',
dir: 'public/module/'
},
plugins: [
svelte({
// enable run-time checks when not in production
dev: !production,
// we'll extract any component CSS out into
// a separate file - better for performance
css: css => {
css.write('public/build/bundle.css');
},
hydratable: true
}),
// If you have external dependencies installed from
// npm, you'll most likely need these plugins. In
// some cases you'll need additional configuration -
// consult the documentation for details:
// https://github.com/rollup/plugins/tree/master/packages/commonjs
resolve({
browser: true,
dedupe: ['svelte']
}),
commonjs(),
// In dev mode, call `npm run start` once
// the bundle has been generated
!production && serve(),
// Watch the `public` directory and refresh the
// browser on changes when not in production
!production && livereload('public'),
// If we're building for production (npm run build
// instead of npm run dev), minify
production && terser()
],
watch: {
clearScreen: false
}
}
];
function serve() {
let started = false;
return {
writeBundle() {
if (!started) {
started = true;
require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
stdio: ['ignore', 'inherit', 'inherit'],
shell: true
});
}
}
};
}
import AdminLayout from './views/layouts/Admin.svelte';
import PublicLayout from './views/layouts/Public.svelte';
import Page404 from './views/pages/404.svelte';
const routes = {
groupGuard: [
{
url: [/^members/],
with: async function (routerData, route) {
return true;
},
redirectOnFail: function (routerData, route) {
return '/dashboard';
}
}
],
routes: [
{
name: "dashboard",
url: [/^dashboard$/, /^\s*$/],
guard: {
with: async function (routerData, route) {
return true;
},
redirectOnFail: '/404'
},
layout: AdminLayout,
component: async function () {
let com = await import('./views/pages/Dashboard.svelte');
return com.default;
}
},
{
name: "members",
url: [/^members/],
searchFilter: async function (routerData, route) {
return true;
},
guard: {
with: async function (routerData, route) {
return true;
},
redirectOnFail: '/404'
},
layout: AdminLayout,
component: async function () {
let com = await import('./views/pages/MembersList.svelte');
return com.default;
}
},
{
name: "404",
url: [/^404$/],
layout: PublicLayout,
component: Page404
}
]
};
export { routes }
instead of import required component from the start and direct assign into component
we are loading it dynamically using function, only when that route is called
without code splitting its look like this
import Dashboard from './views/pages/Dashboard.svelte';
{
component: Dashboard
}
with code splitting its like this
{
component: async function () {
let com = await import('./views/pages/Dashboard.svelte');
return com.default;
}
}
as you notice instead of loading the bundel.js
now we are loading main.js which is main entry point of application which load other chunk dynamically.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<title>Svelte app</title>
<link rel='icon' type='image/png' href='http://localhost:5000/favicon.png'>
<link rel='stylesheet' href='http://localhost:5000/global.css'>
<link rel='stylesheet' href='http://localhost:5000/build/bundle.css'>
<script type="module" src='http://localhost:5000/module/main.js'></script>
</head>
<body>
</body>
</html>
If you are using Code Splitting + SSR, then you also need to change the rollup.config.js for SSR part
All other setting for SSR and Code Splitting remain as it is
as you can see now instead of output single file now we also change to directory public/ssr/ which hold the App.js file and chunk files
//server ssr bundel
{
input: "src/App.svelte",
output: {
sourcemap: false,
format: "cjs",
name: "app",
dir: "public/ssr/"
},
plugins: [
svelte({
generate: "ssr"
}),
resolve(),
commonjs(),
production && terser()
]
}
you can use any server of your choice
onst http = require('http');
const App = require('./public/ssr/App.js');
const port = 3000
const requestHandler = (request, response) => {
let url = 'http://' + request.headers.host + request.url;
const { head, html, css } = App.render({ url: url });
let output = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel='stylesheet' href='http://localhost:5000/global.css'>
<link rel='stylesheet' href='http://localhost:5000/build/bundle.css'>
<script type="module" src='http://localhost:5000/module/main.js'></script>
</head>
<body>${html}</body>
</html>`;
response.end(output);
}
const server = http.createServer(requestHandler)
server.listen(port, (err) => {
if (err) {
return console.log('something bad happened', err)
}
console.log(`server is listening on ${port}`)
})