This content originally appeared on Bits and Pieces - Medium and was authored by Murat Özalp
How to create a routing system in LitElement to build dynamic single-page apps.
Routing is an important part of building web apps. It helps users move between pages smoothly. In a single-page app, it’s even more important because the whole experience is about changing content on one page. In this article, we’ll look at how to add routing to LitElement, a library for creating UI components. By the end, you’ll know how to create a routing system in LitElement to build dynamic single-page apps.
💡 As an aside, any UI component built with Lit can be incorporated into existing modular React/Vue/Angular component libraries built using tools like Bit. Learn more here.
Initialize A Scaffolding Project By OpenWC
By the command below, you will have ready packages and tools for your project that most modern web project has. Surely as prerequisites, NodeJS and NPM are a must.
npm init @open-wc && npm i && npm start
If you need more guidance about it, you can check out the link.
Adding Routing System By Vaadin Router
We install vaadin/router to implement routing for our SPA.
npm i @vaadin/router
After installing the package, we clean everything but just keep the lines below inside the function of render. Additionally adding new code lines in firstUpdated.
// main-index.js
import { LitElement, html, css } from 'lit';
import { Router } from '@vaadin/router';
import './pages/home-view.js';
import './pages/about-view.js';
class LitRealWorldIndex extends LitElement {
firstUpdated() {
super.firstUpdated();
const router = new Router(this.shadowRoot.querySelector('#outlet'));
router.setRoutes([
{ path: '/', component: 'home-view' },
{ path: '/about', component: 'about-view' },
{ path: '(.*)', redirect: '/' },
]);
}
// ...
render() {
return html`
<main>
<div id="outlet"></div>
</main>
`;
}
}
customElements.define('lit-real-world', LitRealWorldIndex);
// ./pages/about-view.js
import { LitElement, html, css } from 'lit';
export class AboutView extends LitElement {
static styles = [
css`
:host {
display: block;
}
`,
];
render() {
return html` <h1>About</h1>
<p>This is the about page.</p>`;
}
}
customElements.define('about-view', AboutView);
// ./pages/home-view.js
import { LitElement, html, css } from 'lit';
export class Home extends LitElement {
static styles = [
css`
:host {
display: block;
}
`,
];
render() {
return html` <h1>Home</h1>
<p>This is the home page.</p>`;
}
}
customElements.define('home-view', Home);
As you can see, in this approach, all routes are added statically from the outset. This may be a viable solution if your web project only has a few pages with no sub-pages or complex components. However, if your project requires many dynamic routes and transitions, with events beyond simply presenting static content on a few pages, this approach may not be the most effective.
Let’s now add another page for blogs and blog detail, which can be organized by creating a new folder named blogs.
We created an index file to export the variable for all routes of blogs.
// ./pages/blogs/index.js
import './blog-detail-view.js';
import './blogs-view.js';
export const routes = [
{ path: '/blogs', component: 'blogs-view' },
{ path: '/blogs/:blog', component: 'blog-detail-view' },
];
To implement lazy loading, we need to import and dynamically add the necessary routes to the Router object in our main-index file.
// main-index.js
...
import { routes as blogsRoutes } from './pages/blogs/index.js';
export class LitRealWorldIndex extends LitElement {
...
firstUpdated() {
super.firstUpdated();
const router = new Router(this.shadowRoot.querySelector('#outlet'));
router.setRoutes([
{ path: '/', component: 'home-view' },
{ path: '/about', component: 'about-view' },
...blogsRoutes,
{ path: '(.*)', redirect: '/' },
]);
}
...
render() {
return html`
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/blogs">Blog</a>
</nav>
<main>
<div id="outlet"></div>
</main>
`;
}
}
customElements.define('lit-real-world', LitRealWorldIndex);
Here, the implementation differs in the blog-detail-view file where we get the route parameter and display it on the screen.
// ./pages/blogs/blog-detail-view.js
import { LitElement, html, css } from 'lit';
export class BlogDetailView extends LitElement {
static styles = [
css`
:host {
display: block;
}
`,
];
static properties = {
blogId: { type: Number },
};
firstUpdated() {
super.firstUpdated();
this.blogId = this.location.params?.blog;
}
render() {
return html` <h1>Blog Detail</h1>
<p>This is Blog ${this.blogId}</p>`;
}
}
customElements.define('blog-detail-view', BlogDetailView);
Loading Routes Lazily
We have created a folder structure that is convenient for managing large projects. However, as the number of pages increases, it becomes difficult to handle all the pages that need to be attached to the routing system during the initial loading of the application. To avoid this problem, we can import and add routes asynchronously, only when they are needed, rather than loading all the routes at once during the initial loading of the application. This approach can improve the application’s performance by reducing the initial loading time and memory usage. By using this approach, we can dynamically load routes when they are requested, which enables us to better manage large-scale projects with many routes.
Let’s take a look at the solution.
// main-index.js
import { LitElement, html, css } from 'lit';
import { Router } from '@vaadin/router';
import './pages/home-view.js';
import './pages/about-view.js';
/* delete */
// import { routes as blogsRoutes } from './pages/blogs/index.js';
export class LitRealWorldIndex extends LitElement {
firstUpdated() {
super.firstUpdated();
const router = new Router(this.shadowRoot.querySelector('#outlet'));
router.setRoutes([
{ path: '/', component: 'home-view' },
{ path: '/about', component: 'about-view' },
/* update */
{
path: '/blogs',
children: () =>
import('./pages/blogs/index.js').then(module => module.routes),
},
{ path: '(.*)', redirect: '/' },
]);
}
render() {
return html`
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/blogs">Blog</a>
</nav>
<main>
<div id="outlet"></div>
</main>
`;
}
}
customElements.define('lit-real-world', LitRealWorldIndex);
// ./pages/blogs/index.js
import './blog-detail-view.js';
import './blogs-view.js';
export const routes = [
{ path: '/', component: 'blogs-view' },
{ path: '/:blog', component: 'blog-detail-view' },
];
Since our app is simple, we may not see a significant increase in performance. However, we can still observe a slight difference by checking the network tab in the browser’s DevTools. This difference may become more noticeable and significant as the size of the SPA increases. Therefore, implementing lazy loading for larger SPAs can be a powerful technique for improving the overall performance and user experience.
Resources
Routing with Lit was originally published in Bits and Pieces on Medium, where people are continuing the conversation by highlighting and responding to this story.
This content originally appeared on Bits and Pieces - Medium and was authored by Murat Özalp
Murat Özalp | Sciencx (2023-05-30T15:16:03+00:00) Routing with Lit. Retrieved from https://www.scien.cx/2023/05/30/routing-with-lit/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.