Using Lit Components in an Enhance App

macdonst

Simon MacDonald

Posted on December 15, 2022

Using Lit Components in an Enhance App

Recently, on the Enhance Discord, there was a question about how to use Lit web components in an Enhance application. Since Lit components extend standard web components, adding Lit to your Enhance app is easy.

Making Lit available in your Enhance application

Lit is a browser module. We’ll need to bundle in the dependency, so it is available in the browser at run time.

First, we need to install Lit by executing the command:

npm install lit
Enter fullscreen mode Exit fullscreen mode

Then we need to add Lit to our run time bundles by editing our .arc file. Under the @bundles pragma in your .arc file, add the following lines:

@bundles
lit './node_modules/lit/index.js'
Enter fullscreen mode Exit fullscreen mode

Luckily, Lit ships with a good entry point that includes all of the dependencies we will need.

You don’t need to name your bundle the same as the npm package, but it makes sense to keep the same name in this case.

Including a Lit component

Next, we’ll create a new file in our project's public folder called my-element.js. We’ll use the source from a Lit component we found on lit.dev as an example.

// public/my-element.js
import {LitElement, html, css} from 'lit';
export class MyElement extends LitElement {
 static properties = {
   greeting: {},
   planet: {},
 };
 static styles = css`
   :host {
     display: inline-block;
     padding: 10px;
     background: lightgray;
   }
   .planet {
     color: var(--planet-color, blue);
   }
 `;

 constructor() {
   super();
   this.greeting = 'Hello';
   this.planet = 'World';
 }

 render() {
   return html`
     <span @click=${this.togglePlanet}
       >${this.greeting}
       <span class="planet">${this.planet}</span>
     </span>
   `;
 }

 togglePlanet() {
   this.planet = this.planet === 'World' ? 'Mars' : 'World';
 }
}
customElements.define('my-element', MyElement);
Enter fullscreen mode Exit fullscreen mode

The Lit component can be used as-is with a tiny change. We need to modify where we import the Lit package from in our component. Change the import line from:

import {LitElement, html, css} from 'lit';
Enter fullscreen mode Exit fullscreen mode

To:

import {LitElement, html, css} from '/_static/bundles/lit.mjs'
Enter fullscreen mode Exit fullscreen mode

Using a Lit component in a page

Now we can use Lit components just like our Enhance components. For example, let’s create a new page app/pages/lit.html and populate it with the following lines:

<script type="module" src="/_static/my-element.js"></script>
<style>
 .mars {
   --planet-color: red;
 }
</style>
<my-element></my-element>
<hr />
<my-element class="mars" planet="Mars"></my-element>
Enter fullscreen mode Exit fullscreen mode

The above HTML produces a page that looks like this:

Lit Demo

If you are using this component on multiple pages, you should look at including the script tag in the Head of your Enhance app.

What about TypeScript components?

If you prefer to use TypeScript based Lit components the instructions are similar, but they do require a few extra steps.

First we’ll install typescript as a dev dependency.

npm install typescript –save-dev
Enter fullscreen mode Exit fullscreen mode

Next, we’ll add lit and lit-decorators to our run time bundles by editing our .arc file. Under the @bundles pragma in your .arc file, add the following lines:

@bundles
lit './node_modules/lit/index.js'
lit-decorators './node_modules/@lit/reactive-element/decorators.js'
Enter fullscreen mode Exit fullscreen mode

Incidentally, it’s Lit’s use of experimental decorators which will require us to run tsc as esbuild doesn't support decorators yet.

Finally, we’ll need a tsconfig.json file to tell tsc how to compile our Lit TS web components. In the root of your project create a tsconfig.json file and add the following lines:

{
   "compilerOptions": {
       "target": "es2019",
       "module": "es2015",
       "moduleResolution": "node",
       "lib": ["es2019", "dom"],
       "declaration": true,
       "declarationMap": true,
       "experimentalDecorators": true,
       "useDefineForClassFields": false
     },
   "include": ["ts/**/*"]
}
Enter fullscreen mode Exit fullscreen mode

Including a Lit component

Next, we’ll create a ts folder in the root of our project. Then we'll add a new file to the ts folder called my-element-ts.js. We’ll use the source from a Lit component we found on lit.dev as an example.

// ts/my-element-ts.ts
import {LitElement, html, css} from 'lit';
import {customElement, property} from 'lit/decorators.js';

@customElement("my-element-ts")
export class MyElementTS extends LitElement {
 static styles = css`
   :host {
     display: inline-block;
     padding: 10px;
     background: lightgray;
   }
   .planet {
     color: var(--planet-color, blue);
   }
 `;

 @property() greeting = "Hello";
 @property() planet = "World";

 render() {
   return html`
     <span @click=${this.togglePlanet}
       >${this.greeting}
       <span class="planet">${this.planet}</span>
     </span>
   `;
 }

 togglePlanet() {
   this.planet = this.planet === "World" ? "Mars" : "World";
 }
}
Enter fullscreen mode Exit fullscreen mode

Then we will need to run tsc to compile our TypeScript component into JavaScript.

tsc
Enter fullscreen mode Exit fullscreen mode

This will create the ts/my-element-ts.js file. Open this file in your text editor and replace the lines:

import { LitElement, html, css } from 'lit';
import { customElement, property } from 'lit/decorators.js';
Enter fullscreen mode Exit fullscreen mode

With:

import {LitElement, html, css} from '/_static/bundles/lit.mjs'
import { customElement, property } from  '/_static/bundles/lit-decorators.mjs'
Enter fullscreen mode Exit fullscreen mode

Then copy ts/my-element-ts.js to public/my-element-ts.js.

Using a Lit TS component in a page

Now we can use Lit TS components the same way we used a Lit JavaScript component. For example, let’s create a new page app/pages/lit-ts.html and populate it with the following lines:

<script type="module" src="/_static/my-element-ts.js"></script>
<style>
 .mars {
   --planet-color: red;
 }
</style>
<my-element-ts></my-element-ts>
<hr />
<my-element-ts class="mars" planet="Mars"></my-element-ts>
Enter fullscreen mode Exit fullscreen mode

The above HTML produces a page that looks like this: \

Lit Demo

Which is indistinguishable from the JavaScript version.

Next Steps

Well, getting Lit to work in an Enhance app wasn’t too bad. The downside is this component only works with client-side rendering. I wonder if we can refactor it to be server-side rendered with Enhance?

💖 💪 🙅 🚩
macdonst
Simon MacDonald

Posted on December 15, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related