Svelte Single-Page Applications with Svelte Router SPA

Single-page applications (SPAs) provide user experiences that mirror those on desktop/mobile applications. Navigating from one page to the next no longer requires waiting for the browser to fully refresh the page on each transition, which keeps users engaged and productive. Because only a single page is served to a user, only certain sections of the view are dynamically updated by the browser (rather than the entire view via a full-page refresh) when the user navigates to a new page. To coordinate which sections of the view are dynamically updated for each route a user can navigate to, the single-page application must leverage a router to map these updates to each possible URL and render accordingly.

When a router is added to an application powered by a modern front-end library/framework, such as React.js and Svelte, developers are able to quickly build single-page applications due to the declarative APIs offered by these libraries/frameworks.

Compared to applications built with React.js, those built with Svelte are smaller (in bundle size) and faster. Svelte's compiler strips out the runtime overhead from outputted bundles and only ships the code that's needed. Additionally, Svelte does not rely on a virtual DOM and diffing algorithms (or other such techniques) for rendering updates. Instead, Svelte's compiler can determine ahead of time which parts of the application should change for a given state change, and when such state changes occur, it will surgically modify the DOM directly.

While not as large as the React.js ecosystem, the Svelte ecosystem continues to grow and gain traction. When considering all of the routers in the React.js ecosystem, there are two that are often chosen for their maturity, robustness and simple-to-use APIs: @reach/router and react-router.

To introduce the concept of integrating a router into a Svelte application, we will use an "easy to start with" router: svelte-router-spa.

Using this router and Svelte, we will be building the following dashboard as a single-page application:

Svelte Dashboard - Single-Page Application

Preparation#

To start, download this project from GitHub:

This repository was scaffolded from the sveltejs/template project template, and it will contain the all of the dashboard's components and views. We will integrate svelte-router-spa into this dashboard so it can...

  • Tie these components and views together.

  • Update certain content (in this case, the title bar and page view) as a user navigates from one page to the next.

    Title Bar + Page View

Installation#

Install svelte-router-spa as a dependency.

To run the application, execute the dev script:

Visit localhost:5000 in a browser. As you hover over each navigation bar item, notice that each item has an href value of /. Our objective will be to make these items link to their correct corresponding routes and render the correct page view.

To preview any of the other page views, go to the src/layouts/DashboardLayout.svelte and swap out the current <Home /> view with another view.

Example: Preview the <Projects /> view.

(src/layouts/DashboardLayout.svelte)

The browser should automatically refresh the application to reflect this change via hot reloading. Note that the title bar does not update to "Dashboard > Project" when the current view is <Projects />. This also needs to be fixed.

Dashboard Routes#

This dashboard will support the following routes:

Integrating the Router#

Inside of the src directory, add a routes.js file. Inside of this file, let's define the dashboard routes based on the information in the table above:

(src/routes.js)

Each object in routes represents an individual route. For each route, a URL pathname (name) is mapped to a Svelte component (directly via component or layout, or indirectly via redirectTo).

  • name - The URL pathname of a route.

  • redirectTo - A URL (or URL pathname) of a route to redirect to when visiting the route the redirectTo is defined within. In this case, visiting / will redirect the user to /dashboard, which will redirect the user to /dashboard/home.

  • component - A component that is rendered when the route is active.

  • layout - A component that is section of the application and is shared across multiple pages. It contains a child component defined within a nested route.

  • nestedRoutes - An array of routes with URL pathnames prefixed by a parent route's name. In this case, the component <Projects /> for nested route "projects" would be rendered when the user visits the route /dashboard/projects (/dashboard is the name of the nested route's parent). When the name of a nested route is set to "index," the redirect or rendering of a component (or layout) defined in this nested route will occur when its parent route is visited. In this case, visiting /dashboard will redirect the user to /dashboard/home.

Next, let's add the <Router /> component to src/App.svelte to indicate the top-most area in the application where updates will be made to the application when these routes are visited. Anything outside of <Router /> will not be affected. The layout (or component) that matches the current active route will be rendered inside of <Router />. In this case, when visiting /dashboard (or /dashboard/home, etc.), the <DashboardLayout /> will be rendered where <Router /> is placed.

(src/App.svelte)

Since <DashboardLayout /> is a layout component, it needs to define a place to render the child components defined in the nested routes of /dashboard. Let's add a <Route /> component to handle this. To render the correct child component for the current route, <Route /> must be passed information about the current route (and child routes) via the currentRoute object. This object is passed to the child components it renders.

(src/layouts/DashboardLayout.svelte)

In the browser, visit localhost:5000. Notice the redirection to localhost:5000/dashboard/home and the <Home /> view being rendered for this route. Now visit localhost:5000/dashboard/projects. The <Projects /> view is rendered for this route.

Fixing the Navigation Bar#

There are currently two issues with the navigation bar:

  1. The href value of each navigation bar item is incorrect. For example, when a user clicks on the "Projects" item, the browser's address bar should be updated to localhost:5000/dashboard/projects, and the browser should load the <Projects /> view.

  2. The navigation bar item that corresponds to the current active route is not highlighted. For example, if the current active route is localhost:5000/dashboard/home, then the colors of the text and background of the "Home" item should be changed to reflect that the user is currently visiting its route.

First, let's pass currentRoute to the <Navbar /> component in the <DashboardLayout /> layout component:

(src/layouts/DashboardLayout.svelte)

Next, let's propagate currentRoute to the <NavbarMenu /> component in the <Navbar /> component:

(src/components/Navbar.svelte)

With the removal of the dashboardRoutes prop, we will need to directly import the list of routes into the <NavbarMenu /> component to render the navigation items. Currently, routes is not formatted properly for this task. We will need to refactor the routes.js file to export a dashboardRoutes array, which will be used only to render these navigation items, and add a custom flag (isExcludedFromNav) to the "projects/:id" nested route object to tell <NavbarMenu /> to not render it as an item in the navigation bar.

(src/routes.js)