Technical Documentation: lit/lit-element
ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference
Overview (`README.md`)
LitElement
LitElement is simple base class for creating fast, lightweight web components with lit-html.
Looking for Lit?
LitElement is now part of the Lit library monorepo. Lit 2 includes lit-html 2.x and LitElement 3.x.
This repo contains the code for LitElement 2.x.
Documentation
For LitElement 2.x documentation, see the legacy documentation site.
For Lit 2, the next version of LitElement and lit-html, see the Lit site.
Overview
LitElement uses lit-html to render into the
element's Shadow DOM
and adds API to help manage element properties and attributes. LitElement reacts to changes in properties
and renders declaratively using lit-html. See the lit-html guide
for additional information on how to create templates for lit-element.
import {LitElement, html, css, customElement, property} from 'lit-element';
// This decorator defines the element.
@customElement('my-element')
export class MyElement extends LitElement {
// This decorator creates a property accessor that triggers rendering and
// an observed attribute.
@property()
mood = 'great';
static styles = css`
span {
color: green;
}`;
// Render element DOM by returning a `lit-html` template.
render() {
return html`Web Components are <span>${this.mood}</span>!`;
}
}<my-element mood="awesome"></my-element>Note, this example uses decorators to create properties. Decorators are a proposed
standard currently available in TypeScript or Babel. LitElement also supports a vanilla JavaScript method of declaring reactive properties.
Examples
Runs in browsers with JavaScript Modules: Stackblitz, JSFiddle, JSBin,
CodePen.You can also copy this HTML file into a local file and run it in any browser that supports JavaScript Modules.
Installation
From inside your project folder, run:
$ npm install lit-elementTo install the web components polyfills needed for older browsers:
$ npm i -D @webcomponents/webcomponentsjsSupported Browsers
The last 2 versions of all modern browsers are supported, including
Chrome, Safari, Opera, Firefox, Edge. In addition, Internet Explorer 11 is also supported.
Edge and Internet Explorer 11 require the web components polyfills.
Forward Compatibility With Lit 2
Lit 2 (LitElement 3.0) has a few breaking changes and deprecations that have been back-ported to LitElement 2.5 in order to ease upgrading.
To prepare for Lit 2, update these APIs:
| LitElement 2.4 | LitElement 2.5/Lit 2 |
|---|---|
Decorator imports:import {customElement} from 'lit-element'; |
import {customElement} from 'lit-element/decorators.js'; |
@internalProperty() foo; |
@state() foo; |
Override _getUpdateComplete() |
Override getUpdateComplete() |
Shadow root options: Override createRenderRoot(). |
Add static shadowRootOptions. |
import {UpdatingElement} from 'lit-element'; |
import {ReactiveElement} from 'lit-element'; |
Contributing
Please see CONTRIBUTING.md.
Overview (`docs/README.md`)
lit-element Documentation
This folder holds the source for the lit-element documentation site. Guides are written in Markdown, and the site is built using the Jekyll static site generator.
This section describes how to build the documentation site.
Prerequisites
To build the documentation site, you need Jekyll, Node, npm, and Polymer CLI.
If you already have Ruby and the gem pacakge manager, run the following command to install Jekyll:
gem install bundler jekyll
For more information, see the Jekyll website.
To install Polymer CLI:
npm install polymer-cli
Previewing the docs requires the App Engine Standard Environment for Python 2.7.
Setup
Clone or download a copy of this repo.
Install npm dependencies for the docs site.
textcd lit-element/docs npm install
Building and previewing
To build the documentation site and preview it from a local server:
cd docs
npm run build
npm run servePoint a browser at localhost:8080 to preview the site.
To use a different port, run:
npm run serve -- --port <PORTNUMBER>The generated API docs are checked in, so you don't need to build the
API docs unless you're making changes to the doc comments in the lit-element source.
To regenerate the API docs and rebuild the site, run the following command:
npm run build-all
Deployment
To build and deploy a new version of the site:
npm run deploy(You must have permission to update the App Engine project to deploy the site.)
The site is staged at a new URL (for example, https://20181207t151022-dot-polymer-lit-element.appspot.com). When ready, use the App Engine console to migrate traffic to the new version.
Sometimes it's useful to stage to a specific, named version. For example, when
iterating on a new doc or design change, you may want to stage to an easily-to-remember
version name, like new-tooling-section.
To push to a named App Engine version, specify the version on the command line:
npm run deploy -- --version=<VERSION>This command stages the docs at https://<VERSION>-dot-polymer-lit-element.appspot.com.
By specifying the version on the command line, you can update an existing staged version.
To make a staged version live, migrate traffic in the App Engine console or from the
command line:
gcloud app services set-traffic --splits <VERSION>=1Overview (`docs/_api/api-readme.md`)
LitElement API Documentation
Modules
[lit-element](/api/modules/_lit_element_.html)
The main LitElement module, which defines the LitElement base class and related APIs.
LitElement components can define a template and a set of observed properties. Changing an observed property triggers a re-render of the element.
Import LitElement and html from this module to create a component:
import {LitElement, html} from 'lit-element';
class MyElement extends LitElement {
// Declare observed properties
static get properties() {
return {
adjective: {}
}
}
constructor() {
this.adjective = 'awesome';
}
// Define the element's template
render() {
return html`<p>your ${adjective} template here</p>`;
}
}
customElements.define('my-element', MyElement);LitElement extends UpdatingElement and adds lit-html templating.
[lit-element/lib/updating-element.js](/api/modules/_lib_updating_element_.html)
import {UpdatingElement} from 'lit-element/lib/updating-element.js';Custom Element base class that supports declaring observable properties, reflecting attributes to properties, and the core update lifecycle methods.
If you want to build a custom element base class that includes these features but not lit-html templating, extend UpdatingElement.
Index (`docs/index.md`)
layout: default
Black Lives Matter. Support the
Equal Justice Initiative and
Campaign Zero.
{{ site.name }}
{{ site.description }}
Get Started
Easily Create Fast, Lightweight Web Components
LitElement makes it easy to define Web Components – ideal for sharing elements across your organization or building a UI design system.
Use your components anywhere you use HTML: in your main document, a CMS, Markdown, or a framework like React or Vue.
{% include projects/index-typescript/simple-greeting.ts %}<simple-greeting name="Everyone"></simple-greeting>{% include project.html label="Launch Code Editor (TypeScript)" folder="index-typescript" openFile="simple-greeting.ts" %}
{% include project.html label="Launch Code Editor (JavaScript)" folder="index" openFile="simple-greeting.js" %}
Why use LitElement?
Delightfully declarative
LitElement's simple, familiar development model makes it easier than ever to build Web Components.
Express your UI declaratively, as a function of state. No need to learn a custom templating language – you can use the full power of JavaScript in your templates. Elements update automatically when their properties change.
Fast and light
Whether your end users are in emerging markets or Silicon Valley, they’ll appreciate that LitElement is extremely fast.
LitElement uses lit-html to define and render HTML templates. DOM updates are lightning-fast, because lit-html only re-renders the dynamic parts of your UI – no diffing required.
Seamlessly interoperable
LitElement follows the Web Components standards, so your components will work with any framework.
LitElement uses Custom Elements for easy inclusion in web pages, and Shadow DOM for encapsulation. There’s no new abstraction on top of the web platform.
Next Steps
One.
[Try LitElement]({{ site.baseurl }}/try) in our live tutorial. You don’t need to install anything.Two.
When you’re ready to dive in, [set up LitElement locally]({{ site.baseurl }}/guide/start) and start building components!Browser Compatibility
LitElement works in all major browsers (Chrome, Firefox, IE, Edge, Safari, and Opera).
Guide Index (`docs/_guide/index.md`)
layout: guide title: Introduction permalink: /guide lit_dev_path: /docs/
{::options toc_levels="1..3" /}
- ToC
{:toc}
What is LitElement?
LitElement is a simple base class for creating fast, lightweight web components that work in any web page with any framework.
LitElement uses lit-html to render into shadow DOM, and adds API to manage properties and attributes. Properties are observed by default, and elements update asynchronously when their properties change.
Next steps
- Getting Started: Set up LitElement and create a component.
- Templates: Write templates with lit-html syntax.
- Properties: Manage properties and attributes.
- Lifecycle: Work with the LitElement lifecycle API.
Try Index (`docs/_try/index.md`)
layout: try title: Try LitElement permalink: /try
Try LitElement in live-editable code without installing anything. Build your first component, use it in a web page, and add style with CSS.
This tutorial has live code samples that you can edit, like this:
{% include project.html folder="try/create/after" openFile="my-element.js" %}
Click Preview at any time to see your code in action.
{:.alert .alert-info}
If you're doing the tutorial in your local development environment, you'll need to make some changes to the code on this page. The code samples on this page are written for the live StackBlitz editor. To work locally, see the Getting Started page.
Config.Yml (`docs/_config.yml`)
name: LitElement
exclude: [package.json, polymer.json, src, node_modules, package-lock.json]
markdown: kramdown
kramdown:
parse_block_html: true
google_analytics: UA-39334307-23
projects: includes/projects
collections:
includes:
output: true
guide:
output: true
permalink: /:collection/:title
try:
output: true
permalink: /:collection/:title
include:
- "*.html"
- "*.*.html"
App.Yaml (`docs/app.yaml`)
runtime: python27
api_version: 1
threadsafe: yes
libraries:
- name: jinja2
version: "2.6" - name: webapp2
version: "2.5.2"
default_expiration: "1m"
handlers:
- url: /api
static_dir: api
secure: always - url: /build
static_dir: build
secure: always - url: /css
static_dir: css
secure: always - url: /images
static_dir: images
secure: always - url: /includes
static_dir: includes
secure: always - url: /.*
script: main.app
secure: always
Package.Json (`docs/package.json`)
{
"private": true,
"scripts": {
"build": "rimraf build && rollup -c && jekyll build",
"build-all": "(cd .. && npm run gen-docs) && npm run build",
"serve": "dev_appserver.py _site/app.yaml",
"deploy": "npm run build && gcloud app deploy _site/app.yaml --project polymer-lit-element --no-promote",
"deploy-staging": "npm run build && gcloud app deploy _site/app.yaml --project polymer-lit-element-staging"
},
"dependencies": {
"@stackblitz/sdk": "^1.3.0",
"@webcomponents/webcomponentsjs": "^2.1.3",
"core-js": "^3.6.5",
"lit-element": "^2.1.0",
"regenerator-runtime": "^0.13.5"
},
"devDependencies": {
"@babel/core": "^7.9.0",
"@babel/preset-env": "^7.9.5",
"@rollup/plugin-node-resolve": "^7.1.3",
"polymer-cli": "^1.9.11",
"rimraf": "^3.0.2",
"rollup": "^2.6.1",
"rollup-plugin-babel": "^4.4.0",
"rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-copy": "^3.3.0",
"rollup-plugin-filesize": "^7.0.0",
"rollup-plugin-minify-html-literals": "^1.2.3",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-terser": "^5.3.0"
}
}
Polymer.Json (`docs/polymer.json`)
{
"npm": true,
"moduleResolution": "node",
"entrypoint": "src/index.html",
"shell": "src/index.js",
"extraDependencies": [
"node_modules/@webcomponents/webcomponentsjs/**"
],
"builds": [{
"name": "gh-pages",
"bundle": true,
"js": {
"minify": true
}
}]
}
Data Guide.Yml (`docs/_data/guide.yml`)
title: LitElement Documentation
toc:
introduction:
title: Introduction
url: /guide
toplevel: true
getting started:
title: Getting Started
url: /guide/start
toplevel: true
components:
title: Writing components
header: true
templates:
title: Templates
url: /guide/templates
styles:
title: Styles
desc: Style your elements with CSS.
url: /guide/styles
properties:
title: Properties
url: /guide/properties
events:
title: Events
url: /guide/events
lifecycle:
title: Lifecycle
url: /guide/lifecycle
decorators:
title: Using decorators
url: /guide/decorators
tools:
title: Tools
header: true
publish:
title: Publish a component
url: /guide/publish
use:
title: Use a component
url: /guide/use
build:
title: Build for production
url: /guide/build
resources:
title: Resources
header: true
community:
title: Community
url: /guide/community
lithtml:
title: lit-html Guides
url: https://lit-html.polymer-project.org
Data Try.Yml (`docs/_data/try.yml`)
title: LitElement Documentation
toc:
introduction:
title: Try LitElement
url: /try
create:
title: 1. Create
url: /try/create
properties:
title: 2. Properties
url: /try/properties
logic:
title: 3. Logic
url: /try/logic
events:
title: 4. Events
url: /try/events
style:
title: 5. Style
url: /try/style
Guide Build (`docs/_guide/build.md`)
layout: guide title: Build for production slug: build lit_dev_path: /docs/tools/production/
{::options toc_levels="1..3" /}
- ToC
{:toc}
When building an app that includes LitElement components, you can use common JavaScript build tools like Rollup or webpack.
We recommend Rollup because it's designed to work with the standard ES module format.
For a set of sample build configurations using Rollup, see Building with Rollup.
If you're interested in building with a different tool, or integrating LitElement into an existing build system, see Build requirements.
Building with Rollup {#building-with-rollup}
There are many ways to set up Rollup to bundle your project. This section describes a two basic builds:
- A modern build that runs on evergreen browsers.
- A universal build that runs on browsers back to Internet Explorer 11.
If you only need to support evergreen browsers, you can use the modern build by itself. If you want to support the widest range of browsers with a single build, you can use the backwards-compatible build.
The last part of this section discusses ways to use the two builds together to provide the faster, smaller modern build to browsers that support it while also supporting older browsers.
The example configurations here use the Shop demo app as an example. You can find all of the configurations described here in a branch of the Shop repo:
Modern browser build {#modern-browser-build}
The modern browser build uses the following npm packages:
rollup. The Rollup bundler.@rollup/plugin-node-resolve. For resolving bare module specifiers. (See Bare module specifiers for more information.)rollup-plugin-terser. To minify JavaScript. This isn't strictly required, but if you're bundling for production, you probably want to minify JavaScript.rollup-plugin-copy. For copying static assets to the build folder.rollup-plugin-minify-html-literals. An optional optimization.
The Rollup configuration file for this build looks like this: \
import resolve from '@rollup/plugin-node-resolve';
import { terser } from 'rollup-plugin-terser';
import minifyHTML from 'rollup-plugin-minify-html-literals';
import copy from 'rollup-plugin-copy';
// Static assets will vary depending on the application
const copyConfig = {
targets: [
{ src: 'node_modules/@webcomponents', dest: 'build-modern/node_modules' },
{ src: 'images', dest: 'build-modern' },
{ src: 'data', dest: 'build-modern' },
{ src: 'index.html', dest: 'build-modern' },
],
};
// The main JavaScript bundle for modern browsers that support
// JavaScript modules and other ES2015+ features.
const config = {
input: 'src/components/shop-app.js',
output: {
dir: 'build-modern/src/components',
format: 'es',
},
plugins: [
minifyHTML(),
copy(copyConfig),
resolve(),
],
preserveEntrySignatures: false,
};
if (process.env.NODE_ENV !== 'development') {
config.plugins.push(terser());
}
export default config;Even simpler starter {#even-simpler-starter}
The rollup-starter-app repo is a bare-bones starter project for building an app with Rollup. Although the repo doesn't include LitElement, it includes everything you need to bundle a LitElement app. If you want to use this project as a model for your own project, the most important files to look at are package.json and rollup.config.js.
Note that in addition to the required plugins, rollup-starter-app includes the CommonJS plugin, @rollup/plugin-commonjs. This plugin isn't required for LitElement, but can be useful if you want to import packages that are only distributed as CommonJS modules.
Universal build {#universal-build}
The universal build is compiled to ES5 for older browsers (primarily IE11), and uses System.js as a module system (because older browsers don't support native JavaScript modules).
The universal build requires all of the packages used by the modern build, plus the following:
Babel:
Polyfills used by Babel:
Rollup plugins:
SystemJS module loader:
If you just want to look at the code, here are the most important parts of the universal build:
- Rollup configuration: https://github.com/Polymer/shop/blob/rollup-examples-v2/rollup-universal.js
index.html: https://github.com/Polymer/shop/blob/rollup-examples-v2/index-universal.html
The following sections describe aspects of the build.
Babel build and polyfill bundle {#babel-build-and-polyfill-bundle}
Unlike the modern build, this build produces two separate bundles: one for the application code, and one for the polyfills required by Babel. Many Babel configurations produce a single bundle, which includes the polyfills as well as the application code. However, this can cause problems with other polyfills, including the Web Components polyfills. Loading the Babel polyfills separately, prior to loading the Web Components polyfills, allows both sets of polyfills to work correctly. The Babel polyfills are supplied as separate Common JS modules.
Babel configuration {#babel-configuration}
The Babel configuration for this build is fairly small. It tells Babel to use the @babel/preset-env plugin to compile to code compatible with Internet Explorer 11.
import babel from 'rollup-plugin-babel';
...
const babelConfig = {
babelrc: false,
...{
presets: [
[
'@babel/preset-env',
{
targets: {
ie: '11',
},
},
],
],
},
};Application bundle {#application-bundle}
The Rollup configuration for the application bundle looks similar to the configuration for the model build:
const configs = [
// The main JavaScript bundle for older browsers that don't support
// JavaScript modules or ES2015+.
{
input: ['src/components/shop-app.js'],
output: {
dir: 'build-universal/nomodule/src/components',
format: 'systemjs',
},
plugins: [
minifyHTML(),
babel(babelConfig),
resolve(),
copy(copyConfig),
],
preserveEntrySignatures: false,
},There are three main differences from the modern configuration:
- It defines a
configsarray, instead of a single configuration object. - The main app bundle uses a different format (SystemJS instead of ES modules).
- The main app bundle is compiled using the Babel plugin.
Polyfill bundle {#polyfill-bundle}
The entrypoint for the Babel polyfills bundle is a JavaScript file that imports two Common JS modules, which in turn import a number of smaller modules:
import 'core-js/stable';
import 'regenerator-runtime/runtime';The Rollup config bundles all of these modules into a single file:
const configs = [
...
// Babel polyfills for older browsers that don't support ES2015+.
{
input: 'src/babel-polyfills-nomodule.js',
output: {
file: 'build-universal/nomodule/src/babel-polyfills-nomodule.js',
format: 'iife',
},
plugins: [commonjs({ include: ['node_modules/**'] }), resolve()],
},
];Loading it all {#loading-it-all}
The index.html file loads all of the bundles in the correct order:
- The Babel polyfills bundle.
- The Web Components polyfill loader, which performs feature detection and loads any required Web Components polyfills.
- The SystemJS loader.
- The application bundle.
(The example index.html also includes a small application-specific polyfill for the fetch API.)
With extra material removed, the portion of index.html that loads script looks like this:
<script src="nomodule/src/babel-polyfills-nomodule.js"></script>
<script src="node_modules/@webcomponents/webcomponentsjs/webcomponents-loader.js"></script>
<script src="node_modules/systemjs/dist/s.min.js"></script>
<script>
System.import('./nomodule/src/components/shop-app.js');
</script>Using multiple builds {#using-multiple-builds}
In theory, it's ideal to deliver a modern ES6 bundle to modern browsers and the larger ES5 bundle to older browsers. In practice, this can be challenging. Here are three possible techniques for delivering different builds to different browsers:
moduleandnomodulescript tags.Client-side feature detection.
Differential serving.
These approaches are discussed in the following sections.
Module/nomodule gambit
Since browsers that don't support modules won't execute module scripts (<script type="module">), and browsers that support modules won't load nomodule scripts (<script nomodule>), you can include the legacy bundles using nomodule. In practice, the browsers that support modules also support the other ES6 language features used by LitElement, and don't require transpilation.
You can see this in practice in the Shop example app. See the index-modnomod.html file.
The advantage of this technique is that it doesn't require any logic on the server side.
The main drawback to this technique is that Edge versions 16–18 support JavaScript modules, but don't support dynamic imports. That's not an issue if your application can be packaged in a single bundle, but if you have a larger application that uses dynamic imports and you need to support older versions of Edge, this technique won't work for you.
Another drawback is that IE 11 (and possibly some other older browsers) may download (but not execute) <script type="module"> bundles, and Edge 16-18 may download (but not execute) <script nomodule> bundles. This represents a performance penalty on these older browsers.
Client-side feature detection
In this technique, the initial page load includes a small script that runs feature detection code and then imperatively loads one of the bundles.
This is another technique that doesn't require any server logic. However, it can introduce delays, since the browser won't start downloading the application bundle until after the initial JavaScript payload has downloaded and executed.
Differential serving
In this technique, the server uses the User-Agent request header to determine which bundle to serve to the browser. This technique (also called "browser sniffing") has drawbacks. It's less correct than client-side feature detection, in that it relies on a static list of features supported by each browser. Also, some browsers may send an incorrect User-Agent header. However, it tends to have a performance advantage over the other approaches, since the browser only receives the bundles it needs.
The prpl-server project is a Node.js web server that supports differential serving.
Build requirements {#build-requirements}
This section describes the requirements for building applications using LitElement. Use this section if you're creating your own build setup.
LitElement is packaged as a set of ES modules, written in modern JavaScript (ES 2017). These are supported natively in modern browsers (Chrome, Safari, Firefox, and Edge, for example). LitElement also uses bare module specifiers, which are not supported by any browsers yet.
When building an app using LitElement, your build system will need to handle the following:
- Resolving bare (or Node-style) module identifiers. LitElement uses bare module specifiers.
- Transforming ES modules to another module system, if required, to support older browsers.
- Transpiling modern JavaScript syntax to ES 5, if required, to support older browsers.
For older browsers, you'll also need to load certain polyfills:
- Web Components polyfills
- Dynamic imports polyfill. Required by browsers (in particular, Edge 16 through 18) that support static imports for ES modules, but not dynamic imports.
Bare module specifiers {#bare-module-specifiers}
LitElement uses bare module specifiers to import modules from the lit-html library, like this:
import {html} from 'lit-html';Browsers currently only support loading modules from URLs or relative paths, not bare names referring to e.g. an npm package, so the build system needs to handle them: either by transforming the specifier to one that works for ES modules in the browser, or by producing a different type of module as output.
Webpack automatically handles bare module specifiers; for Rollup, you'll need a plugin (@rollup/plugin-node-resolve).
Why bare module specifiers? Bare module specifiers let you import modules without knowing exactly where the package manager has installed them. A standards proposal called Import maps would let browsers support bare module specifiers. In the meantime, bare import specifiers can easily be transformed as a build step. There are also some polyfills and module loaders that support import maps.
Supporting older browsers {#supporting-older-browsers}
Supporting older browsers (specifically Internet Explorer 11), requires a number of extra steps:
- Transpiling modern JavaScript syntax to ES 5.
- Transforming ES modules to another module system.
- Loading polyfills.
- Babel polyfills.
- Web Components polyfills.
You may need other polyfills depending on your application.
Transpiling to ES5 {#transpiling-to-es5}
Rollup, webpack and other build tools have plugins to support transpiling modern JavaScript for older browsers. Babel is the most commonly used transpiler.
Unlike some libraries, LitElement is delivered as a set of ES modules using modern JavaScript. When you build your app, you need to compile LitElement as well as your own code.
If you have a build already set up, it may be configured to ignore the node_modules folder when transpiling. If this is the case, we recommend updating this to transpile LitElement and lit-html. For example, if you're using the Rollup Babel plugin, you might have a configuration like this to exclude the node_modules folder from transpilation:
exclude: [ 'node_modules/**' ]You can replace this with a rule to explicitly include folders to transpile:
include: [ 'src/**', 'node_modules/lit-element/**', 'node_modules/lit-html/**']Babel uses a set of helpers and polyfills for implementing various modern JavaScript features. Babel can package these polyfills in with your application code. However, this causes issues with the Web Components polyfills. To avoid issues, bundle the Babel polyfills separately. See Polyfill bundle for an example of building this bundle with Rollup, and see Loading it all for an example index.html showing the load order.
Why no ES5 build? The LitElement npm package doesn't include an ES5 build because modern JavaScript is smaller and generally faster. When building an application, you can compile modern JavaScript down to create the exact build (or builds) you need based on the browsers you need to support.
If LitElement included multiple builds, individual elements could end up depending on different builds of LitElement—resulting in multiple versions of the library being shipped down to the browser.
Transforming modules {#transforming-modules}
When producing output for IE11, there are three common output formats:
- No modules (IIFE). Code is bundled as a single file, wrapped in an immediately-invoked function expression (IIFE).
- AMD modules. Uses the Asynchronous Module Definition format; requires a module loader script, such as require.js.
- SystemJS modules. SystemJS is a module loader that defines its own module format. It also supports AMD, CommonJS, and standard JavaScript modules.
The IIFE format works fine if all of your code can be bundled into a single file. To use code splitting with older browsers like IE11, you'll need to produce output in either the AMD or SystemJS module format.
The universal build in the Building with Rollup section uses SystemJS format. You can see an example of loading the main SystemJS module here:
https://github.com/Polymer/shop/blob/rollup-examples-v2/index-universal.html#L109
Polyfills {#polyfills}
In addition to any application-specific polyfills, you'll need to load the Babel polyfills and the Web Components polyfills.
Note that the Babel polyfills should be bundled separately from the application bundle, and loaded before the Web Components polyfills. This is discussed in Babel build and polyfill bundle. To see an example of generating the Babel polyfill bundle in Rollup, see Polyfill bundle. For an example of loading the bundles, see Loading it all.
Optimizations {#optimizations}
LitElement projects benefit from the same optimizations as other web projects:
- Bundling (for example, using Rollup or webpack).
- Code minification/optimization (Terser works well for LitElement, because it supports modern JavaScript).
- Compression (such as gzip).
In addition, there are a few smaller optimizations that are more specific to LitElement:
- Minify template literals. Lit-html templates are defined using template literals, which don't get processed by standard HTML minifiers. Adding a plugin that minifies template literals can result in a modest decrease in code size. (Unless your templates are very large, this is a small optimization).
- Compile out shady-render. If you're only supporting modern browsers, you can compile out the shady-render module used to support older browsers.
The example builds presented in Building with Rollup include most of these optimizations.
Code minification {#code-minification}
There are many options for code minification. Terser works well with the modern JavaScript used by LitElement. For more information, the terser package description is an excellent overview.
Minify template literals {#minify-template-literals}
Lit-html templates are defined using template literals, which don't get processed by standard HTML minifiers. Adding a plugin that minifies template literals can result in a modest decrease in code size. (Unless your templates are very large, this is a small optimization).
Several packages are available to perform this optimization:
- Rollup: rollup-plugin-minify-html-literals
- Webpack: minify-template-literal-loader
Compile out the shady-render module {#compile-out-the-shady-render-module}
If you're building for modern browsers only, you can remove LitElement's built-in support for shady DOM, the shadow DOM polyfill, saving about 12KB of bundle size.
To do so, configure your build system to replace the shady-render module with the base lit-html module, which provides a generic version of render. This saves about 12KB from your bundle.
For a Rollup build:
Install the
aliasplugin.bashnpm i -D @rollup/plugin-aliasConfigure the alias plugin to replace references to the
shady-rendermodule with references to the mainlit-htmlmodule.jsalias({ entries: [{ find: 'lit-html/lib/shady-render.js', replacement: 'node_modules/lit-html/lit-html.js' }] }),
For a webpack build:
Add the following resolve.alias setting to your webpack configuration:
jsresolve: { alias: { 'lit-html/lib/shady-render.js': path.resolve(__dirname, './node_modules/lit-html/lit-html.js') } },
TypeScript {#typescript}
The TypeScript language extends JavaScript by adding types and type checking. The TypeScript compiler, tsc, compiles TypeScript into standard JavaScript.
While it's possible to run the TypeScript compiler as part of the bundling process, we recommend running it separately, to generate an intermediate JavaScript version of your project. Because of issues with the TypeScript compiler's output for older browsers. We recommend configuring TypeScript to output modern JavaScript (ES2017 target and ES modules).
For example, if you have a tsconfig.json file, you'd include the following options to output modern JavaScript:
{
"compilerOptions": {
"target": "es2017",
"module": "es2015",
...Use this intermediate version as input to your build tools. To support older browsers, use Babel to recompile the intermediate, modern JavaScript version into backward-compatible JavaScript, as described in Transpiling to ES5 and Transforming modules.
Guide Community (`docs/_guide/community.md`)
layout: guide title: Community slug: community lit_dev_path: /docs/tools/publishing/
{::options toc_levels="1..3" /}
- ToC
{:toc}
There are many great resources and locations to learn about LitElement and web components,
share what you've built, and more. All community participation is subject to the Lit
Code of Conduct—be
excellent to each other!
Join us on Slack! The
Lit & Friends Slack workspace has thousands of users discussing LitElement, web components, and related topics.
Check out#general,#beginners, or#toolsto get started.Follow us on Twitter
for the latest on Lit and web components. Many
of our team members can be found tweeting about LitElement, lit-html,
and the latest developments in the web platform.On StackOverflow use
the[lit-element](https://stackoverflow.com/tags/lit-element)or[lit-html](https://stackoverflow.com/tags/lit-html)tags when
looking for answers. You can also find help on underlying web standards like
web components, and try your
hand at answering other people’s queries.
Guide Decorators (`docs/_guide/decorators.md`)
layout: guide title: Using decorators slug: decorators lit_dev_path: /docs/components/decorators/
Decorators are special expressions that can alter the behavior of class, class method, and class field declarations. LitElement supplies a set of decorators that reduce the amount of boilerplate code you need to write when defining a component.
For example, the @customElement and @property decorators make a basic element definition more compact:
import {LitElement, html, customElement, property} from 'lit-element';
@customElement('my-element')
class MyElement extends LitElement {
// Declare observed properties
@property()
adjective = 'awesome';
// Define the element's template
render() {
return html`<p>your ${this.adjective} template here</p>`;
}
}The @customElement decorator defines a custom element, equivalent to calling:
customElements.define('my-element', MyElement);The @property decorator declares a reactive property. The lines:
@property()
adjective = 'awesome';Are equivalent to:
static get properties() {
return {
adjective: {}
};
}
constructor() {
this.adjective = 'awesome';
}Enabling decorators
To use decorators, you need to use a compiler such as Babel or the TypeScript compiler.
The decorators proposal. Decorators are a stage 2 proposal for addition to the ECMAScript standard, which means they're neither finalized nor implemented in browsers yet. Compilers like Babel and TypeScript provide support for proposed features like decorators by compiling them into standard JavaScript a browser can run.
To use decorators with TypeScript
To use decorators with TypeScript, enable the experimentalDecorators compiler option.
"experimentalDecorators": true,Enabling emitDecoratorMetadata is not required and not recommended.
To use decorators with Babel
If you're compiling JavaScript with Babel, you can enable decorators by adding the following plugins:
To enable the plugins, you'd add code like this to your Babel configuration:
plugins = [
'@babel/plugin-proposal-class-properties',
['@babel/plugin-proposal-decorators', {decoratorsBeforeExport: true}],
];LitElement decorators
LitElement provides the following decorators:
@customElement. Define a custom element.@eventOptions. Add event listener options for a declarative event listener.@propertyandinternalProperty. Define properties.@query,queryAll, andqueryAsync. Create a property getter that returns specific elements from your component's render root.@queryAssignedNodes. Create a property getter that returns the children assigned to a specific slot.
All of the decorators can be imported directly from the lit-element module.
import {eventOptions} from 'lit-element';Guide Events (`docs/_guide/events.md`)
layout: guide title: Events slug: events lit_dev_path: /docs/components/events/
{::options toc_levels="1..3" /}
- ToC
{:toc}
Where to add your event listeners
You need to add event listeners in a method that is guaranteed to fire before the event occurs. However, for optimal loading performance, you should add your event listener as late as possible.
The most common ways to add an event listener:
- Declaratively, in your component's template
- In the component constructor, for listeners added on the component itself.
- In the
connectedCallback, for listeners that need to reference DOM outside the component (for example,WindoworDocument). - After first paint, if you're adding a lot of listeners and first paint performance is critical.
Add declarative event listeners
You can use lit-html @event bindings in your template to add event listeners to your component.
Example
render() {
return html`<button @click="${this._handleClick}">`;
}Declarative event listeners are added when the template is rendered. This is usually the best way to add listeners to elements in your templated DOM.
Add event listeners in the constructor
If you need to listen for an event that might occur before your component has been added to DOM, you might need to add the event listener in your component's constructor.
The component constructor is a good place to add event listeners on the host element itself.
Example
constructor() {
super();
this.addEventListener('focus', this._handleFocus);
}Add event listeners in `connectedCallback`
connectedCallback is a lifecycle callback in the custom elements API. connectedCallback fires each time a custom element is appended into a document-connected element. See the MDN documentation on using custom elements lifecycle callbacks for more information.
If your component adds an event listener to anything except itself or its children–for example, to Window, Document, or some element in the main DOM–you should add the listener in connectedCallback and remove it in disconnectedCallback.
Removing the event listener in
disconnectedCallbackensures that any memory allocated by your component will be cleaned up when your component is destroyed or disconnected from the page.Adding the event listener in
connectedCallback(instead of, for example, the constructor orfirstUpdated) ensures that your component will re-create its event listener if it is disconnected and subsequently reconnected to DOM.
Example
connectedCallback() {
super.connectedCallback();
window.addEventListener('resize', this._handleResize);
}
disconnectedCallback() {
window.removeEventListener('resize', this._handleResize);
super.disconnectedCallback();
}Add event listeners after first paint
Sometimes, you may want to defer adding an event listener until after first paint—for example, if you're adding a lot of listeners and first paint performance is critical.
LitElement doesn't have a specific lifecycle callback called after first paint, but you can use this pattern with the firstUpdated lifecycle callback:
async firstUpdated() {
// Give the browser a chance to paint
await new Promise((r) => setTimeout(r, 0));
this.addEventListener('click', this._handleClick);
}firstUpdated fires after the first time your component has been updated and called its render method, but before the browser has had a chance to paint. The Promise/setTimeout line yields to the browser
See firstUpdated in the Lifecycle documentation for more information.
Using `this` in event listeners
Event listeners added using the declarative (@event) syntax in the template are automatically bound to the component.
Therefore, you can use this to refer to your component instance inside any declarative event handler:
class MyElement extends LitElement {
render() {
return html`<button @click="${this._handleClick}">click</button>`;
}
_handleClick(e) {
console.log(this.prop);
}
}When adding listeners imperatively with addEventListener, you'll need to bind the event listener yourself if you need a reference to the component instance. For example:
this.boundResizeHandler = this.handleResize.bind(this);
window.addEventListener('resize', this.boundResizeHandler);Or use an arrow function as a class field:
export class MyElement extends LitElement {
private _handleResize = () => { /* handle the event */ }
constructor() {
window.addEventListener('resize', this._handleResize);
}
}See the documentation for this on MDN for more information.
Setting event listener options
When you add an event listener imperatively, using addEventListener, you can specify various event listener options. For example, to use a passive event listener in plain JavaScript you'd do something like this:
someElement.addEventListener('touchstart', this._handleTouchStart, {passive: true});The eventOptions decorator allows you to add event listener options to a listener that's added declaratively in your template.
import {LitElement, html, eventOptions} from 'lit-element';
...
@eventOptions({passive: true})
private _handleTouchStart() { ... }
render() {
return html`
<div @touchstart=${this._handleTouchStart}><div>
`;
}Using decorators. Decorators are a proposed JavaScript feature, so you’ll need to use a compiler like Babel or TypeScript to use decorators. See Using decorators for details.
The object passed to eventOptions is used as the options parameter to addEventListener.
More information:
- EventTarget.addEventListener() on MDN for a description of the event listener options.
Use cases
- Fire a custom event from a LitElement-based component.
- Handle a custom event fired by a LitElement-based component.
- Handle an event fired by a shadow DOM child of your component.
- Add event listeners imperatively.
Fire an event from a LitElement-based component {#fire-event}
Fire a custom event:
class MyElement extends LitElement {
render() {
return html`<div>Hello World</div>`;
}
firstUpdated(changedProperties) {
let event = new CustomEvent('my-event', {
detail: {
message: 'Something important happened'
}
});
this.dispatchEvent(event);
}
}Fire a standard event:
class MyElement extends LitElement {
render() {
return html`<div>Hello World</div>`;
}
updated(changedProperties) {
let click = new Event('click');
this.dispatchEvent(click);
}
}Handle an event fired by a LitElement-based component {#handle-fired-event}
If you want to listen to an event fired from a LitElement-based component from within another LitElement or from a lit-html template, you can use the lit-html declarative event syntax:
<my-element @my-event="${(e) => { console.log(e.detail.message) }}"></my-element>To listen to events fired from a LitElement-based component in other contexts, like HTML or another framework, use the standard mechanism for listening to DOM events.
In plain HTML and JavaScript, this would be the addEventListener API:
const myElement = document.querySelector('my-element');
myElement.addEventListener('my-event', (e) => {console.log(e)});Working with events and shadow DOM
When working with events and shadow DOM, there are a few things you need to know about.
Event bubbling
Some events bubble up through the DOM tree, so that they are detectable by any element on the page.
Whether or not an event bubbles depends on the value of its bubbles property. To check if a particular event bubbles:
handleEvent(e){
console.log(e.bubbles);
}See the MDN documentation on the Event interface for more information.
Event retargeting
Bubbling events fired from within shadow DOM are retargeted so that, to any listener external to your component, they appear to come from your component itself.
Example
<my-element onClick="(e) => console.log(e.target)"></my-element>render() {
return html`
<button id="mybutton" @click="${(e) => console.log(e.target)}">
click me
</button>`;
}When handling such an event, you can find where it originated from with composedPath:
handleMyEvent(event) {
console.log('Origin: ', event.composedPath()[0]);
}Custom events
By default, a bubbling custom event fired inside shadow DOM will stop bubbling when it reaches the shadow root.
To make a custom event pass through shadow DOM boundaries, you must set both the composed and bubbles flags to true:
firstUpdated(changedProperties) {
let myEvent = new CustomEvent('my-event', {
detail: { message: 'my-event happened.' },
bubbles: true,
composed: true });
this.dispatchEvent(myEvent);
}See the MDN documentation on custom events for more information.
Guide Lifecycle (`docs/_guide/lifecycle.md`)
layout: guide title: Lifecycle slug: lifecycle lit_dev_path: /docs/components/lifecycle/
{::options toc_levels="1..3" /}
- ToC
{:toc}
Overview
LitElement-based components update asynchronously in response to observed property changes. Property changes are batched—if more properties change after an update is requested, but before the update starts, all of the changes are captured in the same update.
At a high level, the update lifecycle is:
- A property is set.
- Check whether an update is needed. If an update is needed, request one.
- Perform the update:
- Process properties and attributes.
- Render the element.
- Resolve a Promise, indicating that the update is complete.
LitElement and the browser event loop
The browser executes JavaScript code by processing a queue of tasks in the event loop. In each iteration of the event loop, the browser takes a task from the queue and runs it to completion.
When the task completes, before taking the next task from the queue, the browser allocates time to perform work from other sources—including DOM updates, user interactions, and the microtask queue.
By default, LitElement updates are requested asynchronously, and queued as microtasks. This means that Step 3 above (Perform the update) is executed at the end of the next iteration of the event loop.
You can change this behavior so that Step 3 awaits a Promise before performing the update. See performUpdate for more information.
For a more detailed explanation of the browser event loop, see Jake Archibald's article.
Lifecycle callbacks {#lifecyclecallbacks}
LitElement also inherits the default lifecycle callbacks from the Web Component standard:
connectedCallback: Invoked when a component is added to the document's DOM.disconnectedCallback: Invoked when a component is removed from the document's DOM.adoptedCallback: Invoked when a component is moved to a new document.attributeChangedCallback: Invoked when component attribute changes.
Be aware that adoptedCallback is not polyfilled.
All lifecycle methods need to call the super method.
Example:
connectedCallback() {
super.connectedCallback()
console.log('connected')
}Promises and asynchronous functions
LitElement uses Promise objects to schedule and respond to element updates.
Using async and await makes it easy to work with Promises. For example, you can await the updateComplete Promise:
// `async` makes the function return a Promise & lets you use `await`
async myFunc(data) {
// Set a property, triggering an update
this.myProp = data;
// Wait for the updateComplete promise to resolve
await this.updateComplete;
// ...do stuff...
return 'done';
}Because async functions return a Promise, you can await them, too:
let result = await myFunc('stuff');
// `result` is resolved! You can do something with itSee the Web Fundamentals primer on Promises for a more in-depth tutorial.
Assorted use cases
Common reasons to hook into the custom element lifecycle or the LitElement update lifecycle are initializations, managing derived data, and dealing with events that originate outside of your element's template. The following list provides some common use cases and approaches. In several cases there is more than one way to achieve a certain goal. Reading this list along with the detailed technical reference will provide you with a rather complete picture and enable you to decide what fits your component's needs best.
- Use property.hasChanged for checking "Is this a change? Do I want to run the update lifecycle?".
- Use the element constructor for initializing LitElement properties with default values. (Attribute values from the DOM are not available when the constructor runs.)
- Use firstUpdated for initializing private fields from DOM attributes (as the constructor doesn't have access to them). Note that render has already run at this point and your changes might trigger another update lifecycle. If it's imperative that you get access to attribute values before the first render happens, consider using connectedCallback, but you'll need to do the extra logic for figuring out the "first" update yourself as connectedCallback can be called multiple times.
- Use updated for keeping derived data up to date or reacting to changes. If you find, that you're causing re-renders, consider using update instead.
- Use custom JS property getters for derived data that is "cheap" to calculate and if its not likely to change often and your element doesn't re-render often.
- Use requestUpdate to trigger an update lifecycle when LitElement cannot pick it up. (E.g. if you have an observed property that is an array, and you add an item to that array instead of replacing the entire array, LitElement won't "see" this change, because the reference to the array didn't change.)
- Use connectedCallback to register event handlers for outside your element's template, but don't forget to remove them in disconnectedCallback!
Methods and properties reference {#reference}
In call order, the methods and properties in the update lifecycle are:
- someProperty.hasChanged
- requestUpdate
- performUpdate
- shouldUpdate
- update
- render
- firstUpdated
- updated
- updateComplete
someProperty.hasChanged {#haschanged}
All declared properties have a function, hasChanged, which is called whenever the property is set; if hasChanged returns true, an update is scheduled.
See the Properties documentation for information on configuring hasChanged to customize what constitutes a property change.
requestUpdate {#requestupdate}
// Manually start an update
this.requestUpdate();
// Call from within a custom property setter
this.requestUpdate(propertyName, oldValue);| Params | propertyName``oldValue| Name of property to be updated. Previous property value. |
| **Returns** | Promise | Returns the updateComplete Promise, which resolves on completion of the update. |
| Updates? | No | Property changes inside this method will not trigger an element update. |
If hasChanged returned true, requestUpdate fires, and the update proceeds.
To manually start an element update, call requestUpdate with no parameters.
To implement a custom property setter that supports property options, pass the property name and its previous value as parameters.
Example: Manually start an element update
{% include projects/lifecycle/requestupdate/my-element.js %}{% include project.html folder="lifecycle/requestupdate" openFile="my-element.js" %}
Example: Call requestUpdate from a custom property setter
{% include projects/properties/customsetter/my-element.js %}{% include project.html folder="properties/customsetter" openFile="my-element.js" %}
performUpdate {#performupdate}
/**
* Implement to override default behavior.
*/
performUpdate() { ... }| Returns | void or Promise | Performs an update. |
| Updates? | No | Property changes inside this method will not trigger an element update. |
By default, performUpdate is scheduled as a microtask after the end of the next execution of the browser event loop. To schedule performUpdate, implement it as an asynchronous method that awaits some state before calling super.performUpdate(). For example:
async performUpdate() {
await new Promise((resolve) => requestAnimationFrame(() => resolve()));
super.performUpdate();
}{% include project.html folder="lifecycle/performupdate" openFile="my-element.js" %}
shouldUpdate {#shouldupdate}
/**
* Implement to override default behavior.
*/
shouldUpdate(changedProperties) { ... }| Params | changedProperties| Map. Keys are the names of changed properties; Values are the corresponding previous values. |
| Returns | Boolean | If true, update proceeds. Default return value is true. |
| Updates? | Yes | Property changes inside this method will trigger an element update. |
Controls whether an update should proceed. Implement shouldUpdate to specify which property changes should cause updates. By default, this method always returns true.
Example: Customize which property changes should cause updates
{% include projects/lifecycle/shouldupdate/my-element.js %}{% include project.html folder="lifecycle/shouldupdate" openFile="my-element.js" %}
update {#update}
| Params | changedProperties| Map. Keys are the names of changed properties; Values are the corresponding previous values. |
| Updates? | No | Property changes inside this method do not trigger an element update. |
Reflects property values to attributes and calls render to render DOM via lit-html. Provided here for reference. You don't need to override or call this method. But if you override it, make sure to call super.update(changedProperties) or render will never be called.
render {#render}
/**
* Implement to override default behavior.
*/
render() { ... }| Returns | TemplateResult | Must return a lit-html TemplateResult. |
| Updates? | No | Property changes inside this method will not trigger an element update. |
Uses lit-html to render the element template. You must implement render for any component that extends the LitElement base class.
See the documentation on Templates for more information.
firstUpdated {#firstupdated}
/**
* Implement to override default behavior.
*/
firstUpdated(changedProperties) { ... }| Params | changedProperties| Map. Keys are the names of changed properties; Values are the corresponding previous values. |
| Updates? | Yes | Property changes inside this method will trigger an element update. |
Called after the element's DOM has been updated the first time, immediately before updated is called.
Implement firstUpdated to perform one-time work after the element's template has been created.
Example: Focus an input element on first update
{% include projects/lifecycle/firstupdated/my-element.js %}{% include project.html folder="lifecycle/firstupdated" openFile="my-element.js" %}
updated {#updated}
/**
* Implement to override default behavior.
*/
updated(changedProperties) { ... }| Params | changedProperties| Map. Keys are the names of changed properties; Values are the corresponding previous values. |
| Updates? | Yes | Property changes inside this method will trigger an element update. |
Called when the element's DOM has been updated and rendered. Implement to perform some task after an update.
Example: Focus an element after update
{% include projects/lifecycle/updated/my-element.js %}{% include project.html folder="lifecycle/updated" openFile="my-element.js" %}
updateComplete {#updatecomplete}
// Await Promise property.
await this.updateComplete;| Type | Promise | Resolves with a Boolean when the element has finished updating. |
| Resolves | true if there are no more pending updates. false if this update cycle triggered another update. |
The updateComplete Promise resolves when the element has finished updating. Use updateComplete to wait for an update:
await this.updateComplete;
// do stuffthis.updateComplete.then(() => { /* do stuff */ });Example
{% include projects/lifecycle/updatecomplete/my-element.js %}{% include project.html folder="lifecycle/updatecomplete" openFile="my-element.js" %}
Overriding updateComplete {#overriding-updatecomplete}
To await additional state before fulfilling the updateComplete promise, override the _getUpdateComplete method. For example, it may be useful to await the update of a child element here. First await super._getUpdateComplete(), then any subsequent state.
It's recommended to override the _getUpdateComplete method instead of the updateComplete getter to ensure compatibility with users who are using TypeScript's ES5 output (see TypeScript#338).
class MyElement extends LitElement {
async _getUpdateComplete() {
await super._getUpdateComplete();
await this._myChild.updateComplete;
}
}Examples {#examples}
Control when updates are processed
async performUpdate() {
await new Promise((resolve) => requestAnimationFrame(() => resolve());
super.performUpdate();
}{% include project.html folder="lifecycle/performupdate" openFile="my-element.js" %}
Customize which property changes should cause an update
shouldUpdate(changedProps) {
return changedProps.has('prop1');
}{% include project.html folder="lifecycle/shouldupdate" openFile="my-element.js" %}
Customize what constitutes a property change
Specify hasChanged for the property. See the Properties documentation.
Manage property changes and updates for object subproperties
Mutations (changes to object subproperties and array items) are not observable. Instead, either rewrite the whole object, or call requestUpdate after a mutation.
// Option 1: Rewrite whole object, triggering an update
this.prop1 = Object.assign({}, this.prop1, { subProp: 'data' });
// Option 2: Mutate a subproperty, then call requestUpdate
this.prop1.subProp = 'data';
this.requestUpdate();{% include project.html folder="lifecycle/subproperties" openFile="my-element.js" %}
Update in response to something that isn't a property change
Call requestUpdate:
// Request an update in response to an event
this.addEventListener('load-complete', async (e) => {
console.log(e.detail.message);
console.log(await this.requestUpdate());
});{% include project.html folder="lifecycle/requestupdate" openFile="my-element.js" %}
Request an update regardless of property changes
Call requestUpdate():
this.requestUpdate();Request an update for a specific property
Call requestUpdate(propName, oldValue):
let oldValue = this.prop1;
this.prop1 = 'new value';
this.requestUpdate('prop1', oldValue);{% include project.html folder="lifecycle/requestupdate" openFile="my-element.js" %}
Do something after the first update
Implement firstUpdated:
firstUpdated(changedProps) {
console.log(changedProps.get('prop1'));
}{% include project.html folder="lifecycle/firstupdated" openFile="my-element.js" %}
Do something after every update
Implement updated:
updated(changedProps) {
console.log(changedProps.get('prop1'));
}{% include project.html folder="lifecycle/updated" openFile="my-element.js" %}
Do something when the element next updates
Await the updateComplete promise:
await this.updateComplete;
// do stuffthis.updateComplete.then(() => {
// do stuff
});Wait for an element to finish updating
Await the updateComplete promise:
let done = await updateComplete;updateComplete.then(() => {
// finished updating
});{% include project.html folder="lifecycle/updatecomplete" openFile="my-element.js" %}
Guide Properties (`docs/_guide/properties.md`)
layout: guide title: Properties slug: properties lit_dev_path: /docs/components/properties/
{::options toc_levels="1..3" /}
- ToC
{:toc}
Overview {#overview}
LitElement manages your declared properties and their corresponding attributes. By default, LitElement will:
- Ensure that an element update is scheduled when any declared property changes.
- Capture instance values for declared properties. Apply any property values that are set before the browser registers a custom element definition.
- Set up an observed (not reflected) attribute with the lowercased name of each property.
- Handle attribute conversion for properties declared as type
String,Number,Boolean,Array, andObject. - Use direct comparison (
oldValue !== newValue) to test for property changes. - Apply any property options and accessors declared by a superclass.
{:.alert .alert-warning}
Remember to declare all of the properties that you want LitElement to manage. For the property features above to be applied, you must declare the property.
Declare properties {#declare}
Declare your element's properties using a static properties field, or using decorators:
Properties field
static get properties() {
return {
propertyName: options
};
}
Decorator (requires TypeScript or Babel)
export class MyElement extends LitElement {
@property(options)
propertyName;
In either case, you can pass an options object to configure features for the property.
Property options
The options object can have the following properties:
attributeWhether the property is associated with an attribute, or a custom name for the associated attribute. Default: true. See Configure observed attributes. If
attributeis false, theconverter,reflectandtypeoptions are ignored.converterA custom converter for converting between properties and attributes. If unspecified, use the default attribute converter.
hasChangedA function that takes an
oldValueandnewValueand returns a boolean to indicate whether a property has changed when being set. If unspecified, LitElement uses a strict inequality check (newValue !== oldValue) to determine whether the property value has changed.noAccessorSet to true to avoid generating the default property accessor. Default: false.
reflectWhether property value is reflected back to the associated attribute. Default: false. See Configure reflected attributes.
typeA type hint for converting between properties and attributes. This hint is used by LitElement's default attribute converter, and is ignored if
converteris set. Iftypeis unspecified, behaves liketype: String. See Use LitElement's default attribute converter.- The property's setter is called.
- The setter calls the property's
hasChangedfunction. ThehasChangedfunction takes the property's old and new values, and returns true if the change should trigger an update. (The defaulthasChangeduses a strict inequality test (oldValue !== newValue) to determine if the property has changed.) - If
hasChangedreturns true, the setter callsrequestUpdateto schedule an update. The update itself happens asynchronously, so if several properties are updated at once, they only trigger a single update. - The component's
updatemethod is called, reflecting changed properties to attributes and re-rendering the component's templates. To observe an attribute (set a property from an attribute), the attribute value must be converted from a string to match the property type.
To reflect an attribute (set an attribute from a property), the property value must be converted to a string.
- For Strings, when the attribute is defined, set the property to the attribute value.
- For Numbers, when the attribute is defined, set the property to
Number(attributeValue). - For Booleans, when the attribute is:
- non-
null, set the property totrue. nullorundefined, set the property tofalse.
- non-
- For Objects and Arrays, when the attribute is:
- Defined, set the property value to
JSON.parse(attributeValue).
- Defined, set the property value to
- For Strings, when the property is:
null, remove the attribute.undefined, don't change the attribute.- Defined and not
null, set the attribute to the property value.
- For Numbers, when the property is:
null, remove the attribute.undefined, don't change the attribute.- Defined and not
null, set the attribute to the property value.
- For Booleans, when the property is:
- truthy, create the attribute.
- falsy, remove the attribute.
- For Objects and Arrays, when the property is:
nullorundefined, remove the attribute.- Defined and not
null, set the attribute value toJSON.stringify(propertyValue).
If
toAttributereturnsnull, the attribute is removed.If
toAttributereturnsundefined, the attribute is not changed.If
toAttributereturnsnull, the attribute is removed.If
toAttributereturnsundefined, the attribute is not changed.If
toAttributeitself is undefined, the attribute value is set to the property value without conversion.hasChangedreturnstrueifnewVal !== oldVal.hasChangedreturnsfalseif both the new and old values areNaN.- ToC
{:toc} - ToC
{:toc} Creating reusable components to share with other developers. Individual components or sets of related components are usually published to npm. To create a reusable component project, see Create a LitElement component project.
Creating app-specific components. The source for these components may live as part of an application project. To add LitElement components to your app, see Add LitElement to an existing project.
- Node.js and npm for managing dependencies. Requires Node.js 10 or greater.
- A local dev server, ES dev server.
- Linting with ESLint and lit-analyzer.
- Testing with Karma.
- A static doc site built with web component analyzer and eleventy.
Download the starter project from GitHub as a zip file:
Uncompress the zip file.
Install dependencies.
bashcd <project folder> npm iIf you're using the TypeScript version of the starter, build the JavaScript version of your project:
bashnpm run buildTo watch files and rebuild when the files are modified, run the following command in a separate shell:
bashnpm run build:watchNo build step is required if you're using the JavaScript version of the starter project.
Run the dev server:
bashnpm run serveOpen the project demo page in a browser tab. For example:
Your server may use a different port number. Check the URL in the terminal output for the correct port number.
- JavaScript. Edit the
my-element.jsfile in the project root. - TypeScript. Edit the
my-element.tsfile in thesrcdirectory. The code defines a class for the component (
MyElement) and registers it with the browser as a custom element named<my-element>.JavaScript
jsexport class MyElement extends LitElement { ... } customElements.define('my-element', MyElement);TypeScript
ts@customElement('my-element') export class MyElement extends LitElement { ... }The component's
rendermethod defines a template that will be rendered as a part of the component. In this case, it includes some text, some data bindings, and a button. For more information, see Templates.jsexport class MyElement extends LitElement { ... render() { return html` <h1>Hello, ${this.name}!</h1> <button @click=${this._onClick}> Click Count: ${this.count} </button> <slot></slot> `; } }The component defines some properties. The component responds to changes in these properties (for example, by re-rendering the template when necessary). For more information, see Properties.
JavaScript
jsexport class MyElement extends LitElement { static get properties() { return { name: {type: String} } } constructor() { super(); this.name = 'World'; } ... }TypeScript
tsexport class MyElement extends LitElement { ... @property({type: String}) name = 'World'; ... }If you're using the TypeScript version, remove generated files:
bashnpm run cleanSearch and replace "my-element" with your new component name in all files in your project (except in the
node_modulesfolder).Search and replace "MyElement" with your new class name in all files in your project (except in the
node_modulesfolder).Rename the source and test files to match the new component name:
JavaScript:
src/my-element.jssrc/test/my-element_test.js
TypeScript:
src/my-element.tssrc/test/my-element_test.ts
If you're using the TypeScript version, rebuild the project:
bashnpm run buildTest and make sure your component is still working:
bashnpm run serveInstall es-dev-server
bashnpm i -D es-dev-serverAdd a dev server command to
package.json:json{ "scripts": { "serve": "es-dev-server --app-index index.html --node-resolve --watch --open" } }Start ES dev server:
bashnpm run serve- ToC
{:toc} A single tagged template literal.
jsstatic get styles() { return css`...`; }An array of tagged template literals.
jsstatic get styles() { return [ css`...`, css`...`]; }- The shadow tree (your component's rendered template).
- The component itself.
- The component's children.
:hostselects the host element.:host(<var>selector</var>)selects the host element, but only if the host element matches selector.::slotted(*)matches all slotted elements.::slotted(p)matches slotted paragraphs.p ::slotted(*)matches slotted elements where the<slot>is a descendant of a paragraph element.html<p> <slot></slot> </p>- Add styles using a
<style>element. - Add styles using an external style sheet.
Separate styles that require per-instance evaluation from those that don't.
Evaluate per-instance CSS properties by creating an expression that captures that property inside a complete
<style>block. Include it in your template.The ShadyCSS polyfill doesn't support external style sheets.
External styles can cause a flash-of-unstyled-content (FOUC) while they load.
The URL in the
hrefattribute is relative to the main document. This is okay if you're building an app and your asset URLs are well-known, but avoid using external style sheets when building a reusable element.Import
classMapand/orstyleMap:jsimport { classMap } from 'lit-html/directives/class-map'; import { styleMap } from 'lit-html/directives/style-map';Use
classMapand/orstyleMapin your element template:jsconstructor() { super(); this.classes = { mydiv: true, someclass: true }; this.styles = { color: 'green', fontFamily: 'Roboto' }; } render() { return html` <div class=${classMap(this.classes)} style=${styleMap(this.styles)}> Some content </div> `; }Use CSS inheritance to propagate style information to LitElement components and their rendered templates.
html<style> html { --themeColor: #123456; font-family: Roboto; } </style> <my-element></my-element>Use CSS variables and custom properties to configure styles per-instance.
html<style> html { --my-element-background-color: /* some color */; } .stuff { --my-element-background-color: /* some other color */; } </style> <my-element></my-element> <my-element class="stuff"></my-element>js// MyElement's static styles static get styles() { return css` :host { background-color: var(--my-element-background-color); } `; }colorfont-familyand otherfont-*properties- All CSS custom properties (
--*) - ToC
{:toc} - DOM scoping. DOM APIs like
document.querySelectorwon't find elements in the
component's shadow DOM, so it's harder for global scripts to accidentally break your component. - Style scoping. You can write encapsulated styles for your shadow DOM that don't
affect the rest of the DOM tree. - Composition. The component's shadow DOM (managed by the component) is separate from the component's children. You can choose how children are rendered in your templated DOM. Component users can add and remove children using standard DOM APIs without accidentally breaking anything in your shadow DOM.
Write your template in HTML inside a JavaScript template literal by enclosing the raw HTML in back-ticks
(````).Tag your template literal with the
html
tag function.The component's
rendermethod can return anything that lit-html can render. Typically, it
returns a singleTemplateResultobject (the same type returned by thehtmltag function).- Does not change the element's state.
- Does not have any side effects.
- Only depends on the element's properties.
- Returns the same result when given the same property values.
- Text content:
${...} - Attribute: ``
- Boolean attribute:
?disabled="${...}" - Property:
.value="${...}" - Event handler:
@event="${...}" Named slots only accept children with a matching
slotattribute.For example,
<slot name="one"></slot>only accepts children with the attributeslot="one".Children with a
slotattribute will only be rendered in a slot with a matchingnameattribute.For example,
...will only be placed in<slot name="one"></slot>.- Element.querySelector() on MDN.
- Element.querySelectorAll() on MDN.
- HTMLSlotElement on MDN.
- Shadow DOM v1: Self-Contained Web Components on Web Fundamentals.
- Using shadow DOM on MDN.
- ToC
{:toc} From your project folder, install the component from npm.
textnpm install some-package-nameImport the component.
In a JavaScript module:
jsimport 'some-package-name';In an HTML page:
html<script type="module"> import './path-to/some-package-name/some-component.js'; </script>Or:
html<script type="module" src="./path-to/some-package-name/some-component.js"></script>Add the component to your application or component:
html<some-component></some-component>From your project folder, install the
@webcomponents/webcomponentsjspackage:textnpm install --save-dev @webcomponents/webcomponentsjsAdd the polyfills to your HTML entrypoint:
html<head> <script src="path-to/webcomponents-loader.js" defer> </script> <script type="module"> // Take care of cases in which the browser runs this // script before it has finished running // webcomponents-loader.js (e.g. Firefox script execution order) window.WebComponents = window.WebComponents || { waitFor(cb){ addEventListener('WebComponentsReady', cb) } } WebComponents.waitFor(async () => { import('./path-to/some-element.js'); }); </script> </head> <body> <some-element></some-element> </body>Ensure that
node_modules/@webcomponents/webcomponentsjs/webcomponents-loader.jsandnode_modules/@webcomponents/webcomponentsjs/bundles/**.*are included in your build.Import the
LitElementbase class andhtmlhelper function.In my-element.js, replace the existing
importstatement with the following code:jsimport { LitElement, html } from 'lit-element';Create a class for your element that extends the LitElement base class.
In my-element.js, replace the existing class definition with the following code:
jsclass MyElement extends LitElement { render() { return html` <p>Hello world! From my-element</p> `; } }The
renderfunction defines your component's template. You must implementrenderfor every LitElement component.Register the new element with the browser.
In my-element.js, replace the existing call to
customElements.define()with the following code:jscustomElements.define('my-element', MyElement);Add a button with an event listener to your template.
In my-element.js, add the following `` element to your HTML template:
html<button @click=${this.clickHandler}>Click</button>The binding syntax
@click=${this.clickHandler}adds a listener for theclickevent, which calls theclickHandlermethod.Add the
clickHandlermethod to your class.Add the following method to your
MyElementclass:jsclickHandler(event) { console.log(event.target); this.myBool = !this.myBool; }The
clickHandlermethod toggles the boolean property,myBool, which you defined in the previous step.Add a boolean property and an array property to your properties getter.
Replace the static properties getter with the following code:
jsstatic get properties() { return { message: { type: String }, myBool: { type: Boolean }, myArray: { type: Array } }; }Initialize the boolean and array properties.
Replace the constructor with the following code:
jsconstructor() { super(); this.message = 'Hello world! From my-element'; this.myBool = true; this.myArray = ['an','array','of','test','data']; }Add a loop to your template.
To loop over the new
myArrayproperty, add the following code to your template:js<ul>${this.myArray.map(item => html`<li>${item}</li>`)}</ul>Add a conditional to your template.
To render conditionally based on the value of
myBool, add the following code to your template:js${this.myBool ? html`<p>Render some HTML if myBool is true</p>` : html`<p>Render some other HTML if myBool is false</p>`}Declare a property.
In my-element.js, add the following
propertiesgetter to theMyElementclass:jsstatic get properties() { return { message: { type: String } }; }The code snippet above adds a string property called
messageto your element class.Initialize the property.
A good place to initialize property values is in your element constructor.
In my-element.js, add the following method to the
MyElementclass:jsconstructor() { super(); this.message = 'Hello world! From my-element'; }The first line of code in the constructor (
super();) calls the parent class constructor.Add the property to your template.
You can add properties to your LitElement templates with JavaScript expressions.
In my-element.js, replace the existing
renderfunction with the following code:jsrender() { return html` <p>${this.message}</p> `; }Import the
csshelper function.In my-element.js, replace the
importstatement with the following code:jsimport { LitElement, html, css } from 'lit-element';Define your styles.
To define your styles, add a static
stylesgetter to your class:jsstatic get styles() { return css` p { font-family: Roboto; font-size: 16px; font-weight: 500; } .red { color: red; } .blue { color: blue; } `; }Apply your styles.
Use
myBoolto apply the styles conditionally. Add the following paragraph to your template:html<p class="${this.myBool ? 'red' : 'blue' }">styled paragraph</p>- Files Extracted: 105
- Estimated Token Budget: ~48170 tokens
- Canonical Reference: https://codewiki.google/github.com/lit/lit-element
An empty options object is equivalent to specifying the default value for all options.
An options object by another name. This guide uses the descriptive term "options object." In practice the options object is an instance of PropertyDeclaration, so you'll see that name if you're using an IDE, or looking at the API reference. By either name, it's an object that defines a set of options.
Declare properties in a static properties field
To declare properties in a static properties field:
static get properties() {
return {
greeting: {type: String},
data: {attribute: false},
items: {}
};
}An empty option object is equivalent to specifying the default value for all options.
Declared properties are initialized like standard class fields—either in the constructor, or with a field initializer if you're using decorators.
Example: Declare properties with a static properties field
{% include projects/properties/declare/my-element.js %}{% include project.html folder="properties/declare" openFile="my-element.js" %}
Declare properties with decorators {#declare-with-decorators}
Use the @property decorator to declare properties (instead of the static properties field).
@property({type: String})
mode = 'auto';
@property()
data = {};The argument to the @property decorator is an options object. Omitting the argument is equivalent to specifying the default value for all options.
Using decorators. Decorators are a proposed JavaScript feature, so you'll need to use a transpiler like Babel or the TypeScript compiler to use decorators. See Using decorators for details.
There is also an @internalProperty decorator for private or protected properties that should trigger an update cycle. Properties declared with @internalProperty shouldn't be referenced from outside the component.
@internalProperty()
protected active = false;The @internalProperty decorator automatically sets attribute to false; the only option you can specify for an internal property is the hasChanged function.
The @internalProperty decorator can serve as a hint to a code minifier that the property name can be changed during minification.
Example: Declare properties with decorators
{% include projects/properties/declaretypescript/my-element.ts %}{% include project.html folder="properties/declaretypescript" openFile="my-element.ts" %}
What happens when properties change
A property change can trigger an asynchronous update cycle, which causes the component to re-render its template.
When a property changes, the following sequence occurs:
There are many ways to hook into and modify the update lifecycle. For more information, see Lifecycle.
Initialize property values {#initialize}
Typically, you initialize property values in the element constructor.
When using decorators, you can initialize the property value as part of the declaration (equivalent to setting the value in the constructor).
You may want to defer initializing a property if the value is expensive to compute and is not required for the initial render of your component. This is a fairly rare case.
Initialize property values in the element constructor {#initialize-constructor}
If you implement a static properties field, initialize your property values in the element constructor:
static get properties() { return { /* Property declarations */ }; }
constructor() {
// Always call super() first
super();
// Initialize properties
this.greeting = 'Hello';
}{:.alert .alert-warning}
Remember to call super() first in your constructor, or your element won't render at all.
Example: Initialize property values in the element constructor
{% include project.html folder="properties/declare" openFile="my-element.js" %}
Initialize property values when using decorators
When using the @property decorator, you can initialize a property as part of the declaration:
@property({type : String})
greeting = 'Hello';Example: Initialize property values when using decorators
{% include project.html folder="properties/declaretypescript" openFile="my-element.ts" %}
Configure attributes {#attributes}
Convert between properties and attributes {#conversion}
While element properties can be of any type, attributes are always strings. This impacts the observed attributes and reflected attributes of non-string properties:
Use the default converter {#conversion-type}
LitElement has a default converter which handles String, Number, Boolean, Array, and Object property types.
To use the default converter, specify the type option in your property declaration:
// Use LitElement's default converter
prop1: { type: String },
prop2: { type: Number },
prop3: { type: Boolean },
prop4: { type: Array },
prop5: { type: Object }The information below shows how the default converter handles conversion for each type.
Convert from attribute to property
Convert from property to attribute
Example: Use the default converter
{% include projects/properties/defaultconverter/my-element.js %}{% include project.html folder="properties/defaultconverter" openFile="my-element.js" %}
Configure a custom converter {#conversion-converter}
You can specify a custom property converter in your property declaration with the converter option:
myProp: {
converter: // Custom property converter
}converter can be an object or a function. If it is an object, it can have keys for fromAttribute and toAttribute:
prop1: {
converter: {
fromAttribute: (value, type) => {
// `value` is a string
// Convert it to a value of type `type` and return it
},
toAttribute: (value, type) => {
// `value` is of type `type`
// Convert it to a string and return it
}
}
}If converter is a function, it is used in place of fromAttribute:
myProp: {
converter: (value, type) => {
// `value` is a string
// Convert it to a value of type `type` and return it
}
}If no toAttribute function is supplied for a reflected attribute, the attribute is set to the property value without conversion.
During an update:
Example: Configure a custom converter
{% include projects/properties/attributeconverter/my-element.js %}{% include project.html folder="properties/attributeconverter" openFile="my-element.js" %}
Configure observed attributes {#observed-attributes}
An observed attribute fires the custom elements API callback attributeChangedCallback whenever it changes. By default, whenever an attribute fires this callback, LitElement sets the property value from the attribute using the property's fromAttribute function. See Convert between properties and attributes for more information.
By default, LitElement creates a corresponding observed attribute for all declared properties. The name of the observed attribute is the property name, lowercased:
// observed attribute name is "myprop"
myProp: { type: Number }To create an observed attribute with a different name, set attribute to a string:
// Observed attribute will be called my-prop
myProp: { attribute: 'my-prop' }To prevent an observed attribute from being created for a property, set attribute to false. The property will not be initialized from attributes in markup, and attribute changes won't affect it.
// No observed attribute for this property
myProp: { attribute: false }An observed attribute can be used to provide an initial value for a property via markup. See Initialize properties with attributes in markup.
Example: Configure observed attributes
{% include projects/properties/attributeobserve/my-element.js %}{% include project.html folder="properties/attributeobserve" openFile="my-element.js" %}
Configure reflected attributes {#reflected-attributes}
You can configure a property so that whenever it changes, its value is reflected to its observed attribute. For example:
// Value of property "myProp" will reflect to attribute "myprop"
myProp: {reflect: true}When the property changes, LitElement uses the toAttribute function in the property's converter to set the attribute value from the new property value.
{:.alert .alert-info}
LitElement tracks reflection state during updates. LitElement keeps track of state information to avoid creating an infinite loop of changes between a property and an observed, reflected attribute.
Example: Configure reflected attributes
{% include projects/properties/attributereflect/my-element.js %}{% include project.html folder="properties/attributereflect" openFile="my-element.js" %}
Set property values from attributes in markup {#initialize-markup}
If a property is configured with attribute: true (the default), users can set the property values from observed attributes in static markup:
index.html
<my-element
mystring="hello world"
mynumber="5"
mybool
myobj='{"stuff":"hi"}'
myarray='[1,2,3,4]'></my-element>See observed attributes and converting between properties and attributes for more information on setting up initialization from attributes.
Attributes versus property bindings. Setting a static attribute value is not the same as binding to a property. See Bind to a property.
Configure property accessors {#accessors}
By default, LitElement generates a getter/setter pair for all declared properties. The setter is invoked whenever you set the property:
// Declare a property
static get properties() { return { myProp: { type: String } }; }
...
// Later, set the property
this.myProp = 'hi'; // invokes myProp's generated property accessorGenerated accessors automatically call requestUpdate, initiating an update if one has not already begun.
Create your own property accessors {#accessors-custom}
To specify how getting and setting works for a property, you can define your getter/setter pair. For example:
static get properties() { return { myProp: { type: String } }; }
set myProp(value) {
// Implement setter logic here...
// retrieve the old property value and store the new one
this.requestUpdate('myProp', oldValue);
}
get myProp() { ... }
...
// Later, set the property
this.myProp = 'hi'; // Invokes your setterIf your class defines its own accessors for a property, LitElement will not overwrite them with generated accessors. If your class does not define accessors for a property, LitElement will generate them, even if a superclass has defined the property or accessors.
The setters that LitElement generates automatically call requestUpdate. If you write your own setter you must call requestUpdate manually, supplying the property name and its old value.
Example
A common pattern for accessors is to store the property value using a private property that's only accessed inside the component. This example uses an underscore prefix (_prop) to identify the private property—you could also use TypeScript's private or protected keywords.
{% include projects/properties/customsetter/my-element.js %}If you want to use your own property accessor with the @property decorator, you can achieve this by putting the decorator on the getter:
private _myProp: string = '';
@property({ type: String })
get myProp(): string {
return this._myProp;
}
set myProp(value: string) {
const oldValue = this._myProp;
this._myProp = value;
this.requestUpdate('myProp', oldValue);
}Prevent LitElement from generating a property accessor {#accessors-noaccessor}
In rare cases, a subclass may need to change or add property options for a property that exists on its superclass.
To prevent LitElement from generating a property accessor that overwrites the superclass's defined accessor, set noAccessor to true in the property declaration:
static get properties() {
return { myProp: { type: Number, noAccessor: true } };
}You don't need to set noAccessor when defining your own accessors.
Example
Subclass element
{% include projects/properties/accessorssubclassing/sub-element.js %}{% include project.html folder="properties/accessorssubclassing" openFile="sub-element.js" %}
Configure property changes {#haschanged}
All declared properties have a function, hasChanged, which is called when the property is set.
hasChanged compares the property's old and new values, and evaluates whether or not the property has changed. If hasChanged returns true, LitElement starts an element update if one is not already scheduled. See the Element update lifecycle documentation for more information on how updates work.
By default:
To customize hasChanged for a property, specify it as a property option:
myProp: { hasChanged(newVal, oldVal) {
// compare newVal and oldVal
// return `true` if an update should proceed
}}hasChanged may not be called for every change. If a property's hasChanged returns true once, it won't be called again until after the next update, even if the property is changed multiple times. If you want to be notified each time a property is set, you should create a custom setter for the property, as described in Create your own property accessors.
Example: Configure property changes
{% include projects/properties/haschanged/my-element.js %}{% include project.html folder="properties/haschanged" openFile="my-element.js" %}
Guide Publish (`docs/_guide/publish.md`)
layout: guide title: Publish an element slug: publish lit_dev_path: /docs/tools/publishing/
{::options toc_levels="1..3" /}
This page describes how to publish a LitElement component to npm.
We recommend publishing JavaScript modules in standard ES2017. If you're writing your element in standard ES2017, you don't need to transpile for publication. If you're using TypeScript, or ES2017+ features such as decorators or class fields, you will need to transpile your element for publication.
Publishing to npm
To publish your component to npm, see the instructions on contributing npm packages.
Your package.json configuration should have both the main and module fields:
package.json
{
"main": "my-element.js",
"module": "my-element.js"
}You should also create a README describing how to consume your component. A basic guide to consuming LitElement components is documented at Use a component.
Transpiling with TypeScript
When compiling your code from TypeScript to JavaScript, we recommend targeting ES2017 with Node.js module resolution.
The following JSON sample is a partial tsconfig.json that uses recommended options for targeting ES2017:
"compilerOptions": {
"target": "es2017",
"module": "es2015",
"moduleResolution": "node",
"lib": ["es2017", "dom"],
"experimentalDecorators": true
}See the tsconfig.json documentation for more information.
Transpiling with Babel
To transpile a LitElement component that uses proposed JavaScript features, use Babel.
Install Babel and the Babel plugins you need. For example:
npm install --save-dev @babel/core
npm install --save-dev @babel/plugin-proposal-class-properties
npm install --save-dev @babel/plugin-proposal-decoratorsConfigure Babel. For example:
babel.config.js
const plugins = [
'@babel/plugin-proposal-class-properties',
['@babel/plugin-proposal-decorators', { decoratorsBeforeExport: true } ],
];
module.exports = { plugins };You can run Babel via a bundler plugin such as rollup-plugin-babel, or from the command line. See the Babel documentation for more information.
Guide Start (`docs/_guide/start.md`)
layout: guide title: Getting Started slug: start lit_dev_path: /docs/getting-started/
{::options toc_levels="1..3" /}
There are two main ways to use LitElement:
Create a LitElement component project {#component-project}
Get started writing a reusable LitElement component that could be published for others to use.
To get started working on a component locally, you can use one of these starter projects:
Both projects define a LitElement component. They also add a set of optional tools for developing, linting, and testing the component:
None of these tools is required to work with LitElement. They represent one possible set of tools for a good developer experience.
Alternative starting point. As an alternative to the official LitElement starter projects, the open-wc project has a project generator for web components using LitElement. The open-wc script asks a series of questions and scaffolds out a project for you.
Download the starter project
The quickest way to try out a project locally is to download one of the starter projects as a zip file.
Want it on GitHub? If you're familiar with git you may want to create a GitHub repository for your starter project,
instead of just downloading the zip file. You can use the GitHub template repository feature to create your own repository from the JavaScript starter project or the TypeScript starter project. Then clone your new repository and install dependencies, as above.
Try out your project
Edit your component
Edit your component definition. The file you edit depends on which language you're using:
A couple of things to look for in the code:
Rename your component
You'll probably want to change the component name from "my-element" to something more appropriate. This is easiest to do using an IDE or other text editor that lets you do a global search and replace through an entire project.
Next steps
Ready to add features to your new component? Head over to Templates for details on writing templates for your LitElement component.
For details on running tests and using other tools, see the starter project README:
Add LitElement to an existing project {#existing-project}
You don't need a lot of tooling for LitElement projects. Chances are if you have an existing set of tools, you can integrate LitElement into it with very limited changes. These instructions assume you're using npm for package management, and using a tool like Rollup or webpack to bundle your code for production.
For details on building projects, including some sample Rollup configurations, see Build for production.
If you're starting from scratch, the open-wc project has a project generator that can scaffold out an application project using LitElement.
Prerequisites
Your build system will need to handle consuming bare module specifiers:
import {LitElement, html} from 'lit-element';Webpack automatically handles bare module specifiers; for Rollup, you'll need a plugin (@rollup/plugin-node-resolve).
Install LitElement
LitElement is available from npm:
npm i lit-elementAdd an element
Pick a location to store your LitElement components, and create a test element:
components/my-element.js
import {LitElement, html} from 'lit-element';
class MyElement extends LitElement {
render() {
return html`
<div>Hello from MyElement!</div>
`;
}
}
customElements.define('my-element', MyElement);Import your component:
Index.js
import './components/my-elements.js';Use your component:
index.html
<my-element></my-element>At this point, you should be able to build and run your project and see the "Hello from MyElement!" message.
Adjust for your build system
How you import the component may vary slightly depending on your build system and project structure. If you're writing in TypeScript, you'll use TypeScript files and use LitElement's decorators. (You can find a sample TypeScript element in the TypeScript starter project).
For more details and sample build configurations, see Build for production.
Optional: Use ES dev server
If you already have a dev server that works with your build system, it should work with LitElement. ES dev server is an alternative dev server that provides a simple, bundler-free workflow. It performs a small number of useful transforms, including transforming bare module specifiers, transpiling to ES5 when needed, and adding polyfills for older browsers.
Supporting older browsers
To support older browsers that don't support ES6 and the web components specifications, you'll need to take a few extra steps to produce code that will run on the older browsers.
See Build for production for more information.
Next steps
Ready to add features to your project? Head over to Templates for details on writing templates for your LitElement component.
For more on building applications that use web components, see the open-wc recommendations on Building.
Guide Styles (`docs/_guide/styles.md`)
layout: guide title: Styles slug: styles lit_dev_path: /docs/components/styles/
{::options toc_levels="1..3" /}
This page describes how to add styles to your component.
Your component's template is rendered to its shadow DOM tree. The styles you add to your component are automatically scoped to the shadow tree, so they don't leak out and affect other elements.
Add styles to your component {#add-styles}
For optimal performance, define scoped styles in a static styles property.
Define styles in a tagged template literal, using the css tag function:
import { LitElement, css, html } from 'lit-element';
class MyElement extends LitElement {
static get styles() {
return css`
div { color: red; }
`;
}
render() {
return html`
<div>I'm styled!</div>
`;
}
}The styles you add to your component are scoped using shadow DOM. For a quick overview of shadow DOM styling, see Shadow DOM styling overview.
The value of the static styles property can be:
The static styles property is usually the best way to add styles to your component, but
there are some use cases you can't handle this way—for example, linking to an external style sheet.
For alternate ways to add styles, see Define scoped styles in the template.
Expressions in static styles {#expressions}
Static styles apply to all instances of a component. Any expressions in CSS are evaluated once, then reused for all instances.
To allow for theming or per-instance style customization, use CSS variables and custom properties to create configurable styles.
To prevent LitElement-based components from evaluating potentially malicious code, the css tag only allows nested expressions that are themselves css tagged strings or numbers.
{% include projects/style/nestedcss/my-element.js %}{% include project.html folder="style/nestedcss" openFile="my-element.js" %}
This restriction exists to protect applications from security vulnerabilities whereby malicious styles, or even malicious code, can be injected from untrusted sources such as URL parameters or database values.
If you must use an expression in a css literal that is not itself a css literal, and you are confident that the expression is from a fully trusted source such as a constant defined in your own code, then you can wrap the expression with the unsafeCSS function:
{% include projects/style/unsafecss/my-element.js %}{% include project.html folder="style/unsafecss" openFile="my-element.js" %}
{:.alert .alert-warning}
Only use the unsafeCSS tag with trusted input. Injecting unsanitized CSS is a security risk. For example,
malicious CSS can "phone home" by adding an image URL that points to a third-party server.
Inheriting styles
Using an array of tagged template literals, a component can inherit the styles from a LitElement superclass, and add its own styles:
class MyElement extends SuperElement {
static get styles() {
return [
super.styles,
css`...`
];
}
}{% include project.html folder="style/superstyles" openFile="my-element.js" %}
Sharing styles
You can share styles between components by creating a module that exports tagged
styles:
import { css } from 'lit-element';
export const buttonStyles = css`
.blue-button {
color: white;
background-color: blue;
}
.blue-button:disabled {
background-color: grey;
}`;Your element can then import the styles and add them to its static styles property:
import { buttonStyles } from './button-styles.js';
class MyElement extends LitElement {
static get styles() {
return [
buttonStyles,
css`
:host { display: block;
border: 1px solid black;
}`
]
}
...
}You can also import an external style sheet by adding a <link> element to your template, but this has a number of limitations. For details, see Import an external stylesheet.
Shadow DOM styling overview {#shadow-dom}
This section gives a brief overview of shadow DOM styling.
Styles you add to a component can affect:
Style the shadow tree {#shadowroot}
LitElement templates are rendered into a shadow tree by default. Styles scoped to an element's shadow tree don't affect the main document or other shadow trees. Similarly, with the exception of inherited CSS properties, document-level styles don't affect the contents of a shadow tree.
When you use standard CSS selectors, they only match elements in your component's shadow tree.
class MyElement extends LitElement {
static get styles() {
// Write styles in standard CSS
return css`
* { color: red; }
p { font-family: sans-serif; }
.myclass { margin: 100px; }
#main { padding: 30px; }
h1 { font-size: 4em; }
`;
}
render() {
return html`
<p>Hello World</p>
<p class="myclass">Hello World</p>
<p id="main">Hello World</p>
<h1>Hello World</h1>
`;
}
}{% include project.html folder="style/styleatemplate" openFile="my-element.js" %}
Style the component itself
You can style the component itself using special :host selectors. (The element that owns, or "hosts" a shadow tree is called the host element.)
To create default styles for the host element, use the :host CSS pseudo-class and :host() CSS pseudo-class function.
static get styles() {
return css`
/* Selects the host element */
:host {
display: block;
}
/* Selects the host element if it is hidden */
:host([hidden]) {
display: none;
}
`;
}{% include project.html folder="style/host" openFile="my-element.js" %}
Note that the host element can be affected by styles from outside the shadow tree, as well, so you should consider
the styles you set in :host and :host() rules as default styles that can be overridden by the user. For example:
my-element {
display: inline-block;
}Style the component's children
Your component may accept children (like a `` element can have <li> children). To render children, your template needs to include one or more <slot> elements, as described in Render children with the slot element.
The <slot> element acts as a placeholder in a shadow tree where the host element's children are displayed. For example:
class MyElement extends LitElement {
render() {
return html`<slot></slot>`;
}
}<my-element><p>Slotted content</p></my-element>{% include project.html folder="style/slottedbase" openFile="my-element.js" %}
Use the ::slotted() CSS pseudo-element to select children that are included in your template via <slot>s.
{% include projects/style/slottedselector/my-element.js %}{% include project.html folder="style/slottedselector" openFile="my-element.js" %}
Note that only direct slotted children can be styled with ::slotted().
<my-element>
<div>Stylable with ::slotted()</div>
</my-element>
<my-element>
<div><p>Not stylable with ::slotted()</p></div>
</my-element>Also, children can
be styled from outside the shadow tree, so you should regard your ::slotted() styles as
default styles that can be overridden.
my-element div {
// Outside style targetting a slotted child can override ::slotted() styles
}{:.alert .alert-info}
Watch out for limitations in the Shady CSS polyfill around slotted content! See the Shady CSS limitations for details on how to use the ::slotted() syntax in a polyfill-friendly way.
Configurable styles with custom properties {#configurable}
Static styles are evaluated once per class. Use CSS variables and custom properties to make styles that can be configured at runtime:
static get styles() {
return css`
:host { color: var(--themeColor); }
`;
}<style>
html {
--themeColor: #123456;
}
</style>
<my-element></my-element>See the section on CSS custom properties for more information.
Define scoped styles in the template {#styles-in-the-template}
We recommend using static styles for optimal performance. However, sometimes you may want to
define styles in the LitElement template. There are two ways to add scoped styles in the template:
Each of these techniques has its own set of advantages and drawbacks.
In a style element
We recommend using static styles for optimal performance. However, static styles are evaluated once per class. Sometimes, you might need to evaluate styles per instance.
We recommend using CSS properties to create customizable styles. However, you can also include <style> elements in a LitElement template. These are updated per instance.
render() {
return html`
<style>
/* updated per instance */
</style>
<div>template content</div>
`;
}Expressions and style elements
The most intuitive way to evaluate per-instance styles has some important limitations and performance issues. We consider the example below to be an anti-pattern:
// Anti-pattern!
render() {
return html`
<style>
:host {
/* Warning: this approach has limitations & performance issues! */
color: ${myColor}
}
</style>
<div>template content</div>
`;
}Expressions inside a <style> element won't update per instance in ShadyCSS, due to limitations of the ShadyCSS polyfill. See the ShadyCSS readme for more information.
Additionally, evaluating an expression inside a <style> element is inefficient. When any text inside a <style> element changes, the browser must re-parse the whole <style> element, resulting in unnecessary work.
If you need to evaluate expressions inside a <style> element, use the following strategy to avoid creating performance problems:
Example
{% include projects/style/perinstanceexpressions/my-element.js %}{% include project.html folder="style/perinstanceexpressions" openFile="my-element.js" %}
Import an external stylesheet {#external-stylesheet}
We recommend placing your styles in a static styles property for optimal performance. However, you can include an external style sheet in your template with a <link>:
{% include projects/style/where/my-element.js %}{% include project.html folder="style/where" openFile="my-element.js" %}
There are some important caveats though:
Dynamic classes and styles
One way to make styles dynamic is to add bindings to the class or style attributes in your template.
The lit-html library offers two directives, classMap and styleMap, to conveniently apply classes and styles in HTML templates.
For more information on these and other lit-html directives, see the documentation on lit-html built-in directives.
To use styleMap and/or classMap:
{% include project.html folder="style/maps" openFile="my-element.js" %}
classMap syntax {#classmap}
classMap applies a set of classes to an HTML element:
<div class=${classMap({alert:true,info:true})}>Content.</div>{% include project.html folder="style/classmap" openFile="my-element.js" %}
styleMap syntax {#stylemap}
styleMap applies a set of CSS rules to an HTML element:
<button style=${styleMap({
backgroundColor: 'blue',
border: '1px solid black'
})}>A button</button>{% include project.html folder="style/stylemap" openFile="my-button.js" %}
To refer to hyphenated properties such as font-family, use the camelCase equivalent (fontFamily) or place the hyphenated property name in quotes ('font-family').
To refer to custom CSS properties such as --custom-color, place the whole property name in quotes ('--custom-color').
| Inline style or CSS | styleMap equivalent |
|---|---|
background-color: blue; |
backgroundColor: 'blue' or 'background-color': 'blue' |
font-family: Roboto, Arial, sans-serif; |
fontFamily: 'Roboto, Arial, sans-serif' or 'font-family': 'Roboto, Arial, sans-serif' |
--custom-color: #FFFABC; |
'--custom-color': '#FFFABC;' |
--otherCustomColor: #FFFABC; |
'--otherCustomColor': '#FFFABC;' |
color: var(--customprop, blue); |
color: 'var(--customprop, blue)' |
Examples
Inline style syntax:
<div style="
background-color:blue;
font-family:Roboto;
--custom-color:#e26dd2;
--otherCustomColor:#77e26d;">
</div>Equivalent CSS syntax:
div {
background-color: blue;
font-family: Roboto;
--custom-color: #e26dd2;
--otherCustomColor: #77e26d;
}Equivalent styleMap syntax:
html`
<div style=${styleMap({
'background-color': 'blue',
fontFamily: 'Roboto',
'--custom-color': '#e26dd2',
'--otherCustomColor': '#77e26d'
})}></div>
`{% include project.html folder="style/stylemap2" openFile="index.html" %}
Theming
CSS inheritance {#inheritance}
CSS inheritance lets parent and host elements propagate certain CSS properties to their descendents.
Not all CSS properties inherit. Inherited CSS properties include:
See CSS Inheritance on MDN for more information.
You can use CSS inheritance to set styles on an ancestor element that are inherited by its descendents:
<style>
html {
font-family: Roboto;
}
</style>
<div>
<p>Uses Roboto</p>
</div>Similarly, host elements pass down inheritable CSS properties to their shadow trees.
You can use the host element's type selector to style it:
<style>
my-element { font-family: Roboto; }
</style>
<my-element></my-element>class MyElement extends LitElement {
render() {
return html`<p>Uses Roboto</p>`;
}
}{% include project.html folder="style/inherited3" openFile="index.html" %}
You can also use the :host CSS pseudo-class to style the host from inside its own template:
static get styles() {
return css`
:host {
font-family: Roboto;
}
`;
}
render() {
return html`
<p>Uses Roboto</p>
`;
}{% include project.html folder="style/inherited" openFile="my-element.js" %}
{:.alert .alert-info}
Type selectors have higher specificity than :host.
An element type selector has higher specificity than the :host pseudo-class selector. Styles set for a custom element tag will override styles set with :host and :host():
<style>
my-element { font-family: Courier; }
</style>
<my-element></my-element>class MyElement extends LitElement {
static get styles() {
return css`:host { font-family: Roboto; }`
}
render() {
return html`<p>Will use courier</p>`;
}
}{% include project.html folder="style/specificity" openFile="index.html" %}
CSS custom properties {#customprops}
All CSS custom properties (--<var>custom-property-name</var>) inherit. You can use this to make your component's styles configurable from outside.
The following component sets its background color to a CSS variable. The CSS variable uses the value of --my-background if it's available, and otherwise defaults to yellow:
class MyElement extends LitElement {
static get styles() {
return css`
:host {
background-color: var(--my-background, yellow);
}
`;
}
render() {
return html`<p>Hello world</p>`;
}
}Users of this component can set the value of --my-background, using the my-element tag as a CSS selector:
<style>
my-element {
--my-background: rgb(67, 156, 144);
}
</style>
<my-element></my-element>--my-background is configurable per instance of my-element:
<style>
my-element {
--my-background: rgb(67, 156, 144);
}
my-element.stuff {
--my-background: #111111;
}
</style>
<my-element></my-element>
<my-element class="stuff"></my-element>If a component user has an existing app theme, they can easily set the host's configurable properties to use theme properties:
{% include projects/style/customproperties/index.html %}{% include project.html folder="style/customproperties" openFile="index.html" %}
See CSS Custom Properties on MDN for more information.
A simple example theme {#example-theme}
index.html
{% include projects/style/theming/index.html %}my-element.js
{% include projects/style/theming/my-element.js %}{% include project.html folder="style/theming" openFile="theme.css" %}
Guide Templates (`docs/_guide/templates.md`)
layout: guide title: Templates slug: templates lit_dev_path: /docs/components/rendering/
{::options toc_levels="1..3" /}
Add a template to your component to define internal DOM to implement your component.
To encapsulate the templated DOM LitElement uses
shadow DOM.
Shadow DOM provides three benefits:
Where native shadow DOM isn't available, LitElement
uses the Shady CSS polyfill.
Define and render a template
To define a template for a LitElement component, write a render function for your element class:
import { LitElement, html } from 'lit-element';
class MyElement extends LitElement {
render() {
return html`<p>template content</p>`;
}
}Example
{% include projects/docs/templates/define/my-element.js %}{% include project.html folder="docs/templates/define" openFile="my-element.js" %}
LitElement uses lit-html templates; this page summarizes the features of lit-html templates,
for more details, see Writing templates
and the Template syntax reference
in the lit-html documentation.
Design a performant template
LitElement renders and re-renders asynchronously, updating in response to batched property changes (see Element update lifecycle for more information).
During an update, only the parts of the DOM that change are re-rendered. To get the performance benefits of this model, you should design your element's template as a pure function of its properties.
To do this, make sure the render function:
Also, avoid making DOM updates outside of render. Instead, express the element's template as a function of its state, and capture its state in properties.
The following code uses inefficient DOM manipulation:
dom-manip.js
// Anti-pattern. Avoid!
constructor() {
super();
this.addEventListener('stuff-loaded', (e) => {
this.shadowRoot.getElementById('message').innerHTML=e.detail;
});
this.loadStuff();
}
render() {
return html`
<p id="message">Loading</p>
`;
}We can improve the template by declaring the message as a property, and binding the property into the template. Declaring a property tells your component to re-render its template when the property changes.
update-properties.js
static get properties() {
return {
message: {type: String}
}
}
constructor() {
super();
this.message = 'Loading';
this.addEventListener('stuff-loaded', (e) => { this.message = e.detail } );
this.loadStuff();
}
render() {
return html`
<p>${this.message}</p>
`;
}{% include project.html folder="docs/templates/design" openFile="update-properties.js" %}
The following sections discuss different types of property bindings. See Properties for information on declaring properties.
Use properties, loops, and conditionals in a template
When defining your element's template, you can bind the element's properties to the
template; the template is re-rendered whenever the properties change.
Properties
To add a property value to a template, insert it with ${this.propName}:
static get properties() {
return {
myProp: {type: String}
};
}
...
render() {
return html`<p>${this.myProp}</p>`;
}Loops
Iterate over an array:
html`<ul>
${this.myArray.map(i => html`<li>${i}</li>`)}
</ul>`;repeat directive. In most cases, Array.map is the most efficient way to create a repeating template. In some cases, you may want to consider lit-html's repeat directive. In particular, if the repeated elements are stateful, or very expensive to regenerate. For more information, see Repeating templates with the repeat directive in the lit-html docs.
Conditionals
Render based on a Boolean condition:
html`
${this.myBool?
html`<p>Render some HTML if myBool is true</p>`:
html`<p>Render some other HTML if myBool is false</p>`}
`;Examples
{% include projects/docs/templates/expressions/my-element.js %}{% include project.html folder="docs/templates/expressions" openFile="my-element.js" %}
Bind properties to templated elements
You can insert JavaScript expressions as placeholders for HTML text content, attributes, Boolean attributes, properties, and event handlers.
JavaScript expressions can include your element's properties. LitElement observes and reacts to property changes, so your templates update automatically.
Data bindings are always one-way (parent to child). To share data from a child element to its parent, fire an event and capture the relevant data in the detail property.
Bind to text content
Bind prop1 to text content:
html`<div>${this.prop1}</div>`Bind to an attribute
Bind prop2 to an attribute:
html`<div id="${this.prop2}"></div>`Attribute values are always strings, so an attribute binding should return a value that can be converted into a string.
Bind to a boolean attribute
Bind prop3 to a boolean attribute:
html`<input type="text" ?disabled="${this.prop3}">`Boolean attributes are added if the expression evaluates to a truthy value, and removed if it evaluates to a falsy value.
Bind to a property
Bind prop4 to a property:
html`<input type="checkbox" .value="${this.prop4}"/>`Bind to an event handler
Bind clickHandler to a click event:
html`<button @click="${this.clickHandler}">pie?</button>`The default event context for @event expressions is this, so there is no need to bind the handler function.
Examples
my-element.js
{% include projects/docs/templates/databinding/my-element.js %}{% include project.html folder="docs/templates/databinding" openFile="my-element.js" %}
Render children with the slot element {#slots}
Your component may accept children (like a `` element can have <li> children).
<my-element>
<p>A child</p>
</my-element>By default, if an element has a shadow tree, its children don't render at all.
To render children, your template needs to include one or more <slot> elements, which act as placeholders for child nodes.
Finding slotted children. If your component needs information about its slotted children, see Accessing slotted children.
Use the slot element
To render an element's children, create a <slot> for them in the element's template. For example:
render(){
return html`
<div>
<slot></slot>
</div>
`;
}Children will now render in the <slot>:
<my-element>
<p>Render me</p>
</my-element>The children aren't moved in the DOM tree, but they're rendered as if they were children of the <slot>.
Arbitrarily many children can populate a single slot:
<my-element>
<p>Render me</p>
<p>Me too</p>
<p>Me three</p>
</my-element>{% include project.html folder="docs/templates/slots" openFile="my-element.js" %}
Use named slots
To assign a child to a specific slot, ensure that the child's slot attribute matches the slot's name attribute:
render(){
return html`
<div>
<slot name="one"></slot>
</div>
`;
}index.html
<my-element>
<p slot="one">Include me in slot "one".</p>
</my-element>Examples
my-element.js
{% include projects/docs/templates/namedslots/my-element.js %}index.html
{% include projects/docs/templates/namedslots/index.html %}{% include project.html folder="docs/templates/namedslots" openFile="my-element.js" %}
Use name, not id, to select slots.
Note that a slot's id attribute has no effect!
my-element.js
render(){
return html`
<div>
<slot id="one"></slot>
</div>
`;
}index.html
<my-element>
<p slot="one">nope.</p>
<p>ohai..</p>
</my-element>{% include project.html folder="docs/templates/slotid" openFile="my-element.js" %}
Compose a template from other templates
You can compose LitElement templates from other LitElement templates. In the following example, we compose a template for an element called <my-page> from smaller templates for the page's header, footer, and main content:
function headerTemplate(title) {
return html`<header>${title}</header>`;
}
function articleTemplate(text) {
return html`<article>${text}</article>`;
}
function footerTemplate() {
return html`<footer>Your footer here.</footer>`;
}
class MyPage extends LitElement {
...
render() {
return html`
${headerTemplate(this.article.title)}
${articleTemplate(this.article.text)}
${footerTemplate()}
`;
}
}{% include project.html folder="docs/templates/compose" openFile="my-page.js" %}
You can also compose templates by importing other elements and using them in your template:
import './my-header.js';
import './my-article.js';
import './my-footer.js';
class MyPage extends LitElement {
render() {
return html`
<my-header></my-header>
<my-article></my-article>
<my-footer></my-footer>
`;
}
}{% include project.html folder="docs/templates/composeimports" openFile="my-page.js" %}
Specify the render root {#renderroot}
The node into which your component's template will render is called its render root.
By default, LitElement creates an open shadowRoot and renders inside it, producing the following DOM structure:
<my-element>
#shadow-root
<p>child 1</p>
<p>child 2</p>To customize a component's render root, implement createRenderRoot and return the node you want the template to render into.
For example, to render the template into the main DOM tree as your element's children:
<my-element>
<p>child 1</p>
<p>child 2</p>Implement createRenderRoot and return this:
class LightDom extends LitElement {
render() {
return html`
<p>This template renders without shadow DOM.</p>
`;
}
createRenderRoot() {
/**
* Render template without shadow DOM. Note that shadow DOM features like
* encapsulated CSS and slots are unavailable.
*/
return this;
}
}{% include project.html folder="docs/templates/renderroot" openFile="my-element.js" %}
Template syntax cheat sheet
Render
render() { return html`<p>template</p>`; }Properties, loops, conditionals
// Property
html`<p>${this.myProp}</p>`;
// Loop
html`${this.myArray.map(i => html`<li>${i}</li>`)}`;
// Conditional
html`${this.myBool?html`<p>foo</p>`:html`<p>bar</p>`}`;Data bindings
// Attribute
html`<p id="${...}">`;
// Boolean attribute
html`<input type="text" ?disabled="${...}">`;
// Property
html`<input .value="${...}">`;
// Event handler
html`<button @click="${this.doStuff}"></button>`;Composition
// From multiple templates on same class
render() {
return html`
${this.headerTemplate}
<article>article</article>
`;
}
get headerTemplate() {
return html`<header>header</header>`;
}// By importing elements
import './my-header.js';
class MyPage extends LitElement{
render() {
return html`
<my-header></my-header>
<article>article</article>
`;
}
}Slots
render() { return html`<slot name="thing"></slot>`; }<my-element>
<p slot="thing">stuff</p>
</my-element>Using other lit-html features
Since LitElement uses the lit-html html tag function to define templates you can take advantage
of the entire lit-html feature set for writing your templates. This includes lit-html directives,
special functions that customize the way lit-html renders a binding.
To import features directly from lit-html, your project should add lit-html as a direct dependency.
We recommend using the widest practical version range for lit-html, to minimize the chance of npm
installing two different versions of lit-html:
npm i lit-element@^2.0.0
npm i lit-html@^1.0.0Import and use a lit-html directive
You can import and use a lit-html directive and use it as shown in the lit-html documentation.
import { LitElement, html } from 'lit-element';
import { until } from 'lit-html/directives/until.js';
const content = fetch('./content.txt').then(r => r.text());
html`${until(content, html`<span>Loading...</span>`)}`For a list of directives supplied with lit-html, see Built-in directives in the Template syntax reference.
Accessing nodes in the shadow DOM
The render() method result is usually rendered into shadow DOM, so the nodes are not direct children of the component. Use this.shadowRoot.querySelector() or this.shadowRoot.querySelectorAll() to find nodes in the
shadow DOM.
You can query the templated DOM after its initial render (for example, in firstUpdated), or use a getter pattern, like this:
get _closeButton() {
return this.shadowRoot.querySelector('#close-button');
}LitElement supplies a set of decorators that provide a shorthand way of defining getters like this.
More information:
@query, @queryAll, and @queryAsync decorators
The @query, @queryAll, and @queryAsync decorators all provide a convenient way to access nodes in the component's shadow root.
Using decorators. Decorators are a proposed JavaScript feature, so you’ll need to use a compiler like Babel or TypeScript to use decorators. See Using decorators for details.
The @query decorator modifies a class property, turning it into a getter that returns a node from the render root. The optional second argument is a cache flag which when true performs the DOM query only once and caches the result. This can be used as a performance optimization in cases when the node being queried is not expected to change.
import {LitElement, html} from 'lit-element';
import {query} from 'lit-element/lib/decorators.js';
class MyElement extends LitElement {
@query('#first')
_first;
render() {
return html`
<div id="first"></div>
<div id="second"></div>
`;
}
}This decorator is equivalent to:
get first() {
return this.renderRoot.querySelector('#first');
}shadowRoot and renderRoot. The renderRoot property identifies the container that the template is rendered into. By default, this is the component's shadowRoot. The decorators use renderRoot, so they should work correctly even if you override createRenderRoot as described in Specify the render root
The @queryAll decorator is identical to query except that it returns all matching nodes, instead of a single node. It's the equivalent of calling querySelectorAll.
import {LitElement, html} from 'lit-element';
import {queryAll} from 'lit-element/lib/decorators.js';
class MyElement extends LitElement {
@queryAll('div')
_divs;
render() {
return html`
<div id="first"></div>
<div id="second"></div>
`;
}
}Here, divs would return both `` elements in the template. For TypeScript, the typing of a @queryAll property is NodeListOf<HTMLElement>. If you know exactly what kind of nodes you'll retrieve, the typing can be more specific:
@queryAll('button')
_buttons!: NodeListOf<HTMLButtonElement>The exclamation point (!) after buttons is TypeScript's non-null assertion operator. It tells the compiler to treat buttons as always being defined, never null or undefined.
Finally, @queryAsync works like @query, except that instead of returning a node directly, it returns a Promise that resolves to that node. Code can use this instead of waiting for the updateComplete promise.
This is useful, for example, if the node returned by @queryAsync can change as a result of another property change.
Accessing slotted children
To access children assigned to slots in your shadow root, you can use the standard slot.assignedNodes method and the slotchange event.
For example, you can create a getter to access assigned nodes for a particular slot:
get _slottedChildren() {
const slot = this.shadowRoot.querySelector('slot');
const childNodes = slot.assignedNodes({flatten: true});
return Array.prototype.filter.call(childNodes, (node) => node.nodeType == Node.ELEMENT_NODE);
}You can also use the slotchange event to take action when the assigned nodes change. The following example extracts the text content of all of the slotted children.
handleSlotchange(e) {
const childNodes = e.target.assignedNodes({flatten: true});
// ... do something with childNodes ...
this.allText = Array.prototype.map.call(childNodes, (node) => {
return node.textContent ? node.textContent : ''
}).join('');
}
render() {
return html`<slot @slotchange=${this.handleSlotchange}></slot>`;
}More information:
@queryAssignedNodes decorator
The @queryAssignedNodes decorator converts a class property into a getter that returns all of the assigned nodes for a given slot in the component's shadow tree. The optional second boolean argument when true flattens the assigned nodes, meaning any assigned nodes that are slot elements are replaced with their assigned nodes. The optional third argument is a css selector which filters the results to matching elements.
Using decorators. Decorators are a proposed JavaScript feature, so you’ll need to use a compiler like Babel or TypeScript to use decorators. See Using decorators for details.
// First argument is the slot name
// Second argument is `true` to flatten the assigned nodes.
@queryAssignedNodes('header', true)
_headerNodes;
// If the first argument is absent or an empty string, list nodes for the default slot.
@queryAssignedNodes()
_defaultSlotNodes;The first example above is equivalent to the following code:
get headerNodes() {
const slot = this.shadowRoot.querySelector('slot[name=header]');
return slot.assignedNodes({flatten: true});
}For TypeScript, the typing of a queryAssignedNodes property is NodeListOf<HTMLElement>.
Resources
For more information on shadow DOM:
For more information on lit-html templates:
Guide Use (`docs/_guide/use.md`)
layout: guide title: Use a component slug: use lit_dev_path: /docs/
{::options toc_levels="1..3" /}
This page describes how to use a LitElement component in your application. It also describes how to make sure your deployed code is browser-ready by building it for production and loading the Web Components polyfills.
Use a LitElement component {#use}
This is a general guide to using third-party LitElement components. Refer to a component's README or other documentation for specific details.
To use a LitElement component in your code:
Develop {#develop}
Elements built with LitElement are published to npm as standard JavaScript modules, which all major browsers can now load.
However, LitElement and elements built with it import their dependencies using bare module specifiers (for example, import { ... } from 'module-name') instead of full paths (import {...} from '../path/to/module-name').
At the time of writing, browsers must still be provided with the full path to a standard JavaScript module in order to load it. To convert bare module specifiers to full paths, a light transform is required.
For a local server that does this automatically, try the Open Web Components ES dev server.
Build for production {#build}
To build for production, you can use a bundler such as WebPack or Rollup.
The following example configuration for Rollup resolves dependencies, converts bare module specifers to paths, and bundles the output.
rollup.config.js
import resolve from 'rollup-plugin-node-resolve';
export default {
// If using any exports from a symlinked project, uncomment the following:
// preserveSymlinks: true,
input: ['src/index.js'],
output: {
file: 'build/index.js',
format: 'es',
sourcemap: true
},
plugins: [
resolve()
]
};See a sample build configuration for LitElement with Babel and Rollup.
Load the WebComponents polyfills {#polyfills}
Elements built with LitElement use the Web Components set of standards, which are currently supported by all major browsers with the exception of Edge.
For compatibility with older browsers and Edge, load the Web Components polyfills.
To load the WebComponents polyfills:
Do not transpile the polyfills. Bundling them is okay.
See the Webcomponentsjs documentation for more information.
Try Create (`docs/_try/create.md`)
layout: try slug: create title: Create a component
In this step, you'll fill in the gaps in the starting code to create an element class with a basic HTML template.
Starting code
my-element.js
{% include projects/try/create/before/my-element.js %}Click Launch Code Editor to edit the starting code. When you're ready to see your code in action, click Preview.
{% include project.html folder="try/create/before" openFile="my-element.js" %}
Here's the completed code for this step:
my-element.js
{% include projects/try/create/after/my-element.js %}Your code sample should be working now. LitElement components are added to a page with simple HTML tags, like this:
<my-element></my-element>Try Events (`docs/_try/events.md`)
layout: try slug: events title: Events
In this step, you'll use lit-html's @event annotation to add an event listener to an element inside your template. You'll also add an event handler method to your class which will fire whenever the event occurs.
Starting code
my-element.js
{% include projects/try/events/before/my-element.js %}{% include project.html folder="try/events/before" openFile="my-element.js" %}
Here's the completed code for this step:
my-element.js
{% include projects/try/events/after/my-element.js %}Try Logic (`docs/_try/logic.md`)
layout: try slug: logic title: Logic in templates
In this step, you'll add a loop and a conditional to your LitElement template.
To repeat a part of your HTML template, you can use a JavaScript expression to iterate over an array property:
${this.myArray.map(item => html`<li>${item}</li>`)}Similarly, to conditionally render some part of your template, you can use a JavaScript expression to examine a boolean property:
${this.myBool ? html`<p>something</p>` : html`<p>something else</p>`}Starting code
my-element.js
{% include projects/try/logic/before/my-element.js %}{% include project.html folder="try/logic/before" openFile="my-element.js" %}
Here's the completed code for this step:
my-element.js
{% include projects/try/logic/after/my-element.js %}Try Properties (`docs/_try/properties.md`)
layout: try slug: properties title: Use properties
In this step, you'll declare a property for your component, initialize the property, and use the value in the component's template.
Starting code
my-element.js
{% include projects/try/properties/before/my-element.js %}{% include project.html folder="try/properties/before" openFile="my-element.js" %}
LitElement components update automatically when their properties change.
Here's the completed code for this step:
my-element.js
{% include projects/try/properties/after/my-element.js %}Try Style (`docs/_try/style.md`)
layout: try slug: style title: Styling
Style your component with CSS by defining a static styles getter in your class.
Starting code
my-element.js
{% include projects/try/style/before/my-element.js %}{% include project.html folder="try/style/before" openFile="my-element.js" %}
Here's the completed code for this step:
my-element.js
{% include projects/try/style/after/my-element.js %}Congratulations - you've made your first element with LitElement. Next, see the Getting Started guide and set up LitElement locally.
Includes Projects Index Typescript Manifest.Json (`docs/_includes/projects/index-typescript/manifest.json`)
{
"files": [
"simple-greeting.ts",
"index.html",
"index.ts"
],
"title": "LitElement Example",
"description": "LitElement Example",
"template": "typescript",
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
}
}
Includes Projects Index Manifest.Json (`docs/_includes/projects/index/manifest.json`)
{
"files": [
"simple-greeting.js",
"index.html",
"index.ts"
],
"title": "LitElement Example",
"description": "LitElement Example",
"template": "typescript",
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
}
}
Includes Projects Docs Create Manifest.Json (`docs/_includes/projects/docs/create/manifest.json`)
{
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"title": "lit-element code sample",
"description": "lit-element code sample",
"template": "typescript",
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
}
}
Includes Projects Docs Renderroot Manifest.Json (`docs/_includes/projects/docs/renderroot/manifest.json`)
{
"files": [
"default-root.js",
"light-dom.js",
"index.html",
"index.ts"
],
"title": "lit-element code sample",
"description": "lit-element code sample",
"template": "typescript",
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
}
}
Includes Projects Docs Typescript Manifest.Json (`docs/_includes/projects/docs/typescript/manifest.json`)
{
"files": [
"my-element.ts",
"index.html",
"index.ts"
],
"title": "lit-element code sample",
"description": "lit-element code sample",
"template": "typescript",
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
}
}
Includes Projects Events Child Manifest.Json (`docs/_includes/projects/events/child/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"dependencies": {
"@polymer/lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
},
"template": "typescript"
}
Includes Projects Events Childimperative Manifest.Json (`docs/_includes/projects/events/childimperative/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"dependencies": {
"@polymer/lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
},
"template": "typescript"
}
Includes Projects Events Events Manifest.Json (`docs/_includes/projects/events/events/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"dependencies": {
"@polymer/lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
},
"template": "typescript"
}
Includes Projects Events Host Manifest.Json (`docs/_includes/projects/events/host/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"dependencies": {
"@polymer/lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
},
"template": "typescript"
}
Includes Projects Lifecycle Customize Manifest.Json (`docs/_includes/projects/lifecycle/customize/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
},
"template": "typescript"
}
Includes Projects Lifecycle Firstupdated Manifest.Json (`docs/_includes/projects/lifecycle/firstupdated/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
},
"template": "typescript"
}
Includes Projects Lifecycle Haschanged Manifest.Json (`docs/_includes/projects/lifecycle/haschanged/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
},
"template": "typescript"
}
Includes Projects Lifecycle Microtasks Manifest.Json (`docs/_includes/projects/lifecycle/microtasks/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
},
"template": "typescript"
}
Includes Projects Lifecycle Performupdate Manifest.Json (`docs/_includes/projects/lifecycle/performupdate/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
},
"template": "typescript"
}
Includes Projects Lifecycle Requestupdate Manifest.Json (`docs/_includes/projects/lifecycle/requestupdate/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
},
"template": "typescript"
}
Includes Projects Lifecycle Shouldupdate Manifest.Json (`docs/_includes/projects/lifecycle/shouldupdate/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
},
"template": "typescript"
}
Includes Projects Lifecycle Subproperties Manifest.Json (`docs/_includes/projects/lifecycle/subproperties/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
},
"template": "typescript"
}
Includes Projects Lifecycle Update Manifest.Json (`docs/_includes/projects/lifecycle/update/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
},
"template": "typescript"
}
Includes Projects Lifecycle Updatecomplete Manifest.Json (`docs/_includes/projects/lifecycle/updatecomplete/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
},
"template": "typescript"
}
Includes Projects Lifecycle Updated Manifest.Json (`docs/_includes/projects/lifecycle/updated/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
},
"template": "typescript"
}
Includes Projects Properties Accessorssubclassing Manifest.Json (`docs/_includes/projects/properties/accessorssubclassing/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"super-element.js",
"sub-element.js",
"index.html",
"index.ts"
],
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
},
"template": "typescript"
}
Includes Projects Properties Attributeconverter Manifest.Json (`docs/_includes/projects/properties/attributeconverter/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
},
"template": "typescript"
}
Includes Projects Properties Attributeobserve Manifest.Json (`docs/_includes/projects/properties/attributeobserve/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
},
"template": "typescript"
}
Includes Projects Properties Attributereflect Manifest.Json (`docs/_includes/projects/properties/attributereflect/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
},
"template": "typescript"
}
Includes Projects Properties Customsetter Manifest.Json (`docs/_includes/projects/properties/customsetter/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
},
"template": "typescript"
}
Includes Projects Properties Declare Manifest.Json (`docs/_includes/projects/properties/declare/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
},
"template": "typescript"
}
Includes Projects Properties Declaretypescript Manifest.Json (`docs/_includes/projects/properties/declaretypescript/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.ts",
"index.html",
"index.ts"
],
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
},
"template": "typescript"
}
Includes Projects Properties Defaultconverter Manifest.Json (`docs/_includes/projects/properties/defaultconverter/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
},
"template": "typescript"
}
Includes Projects Properties Haschanged Manifest.Json (`docs/_includes/projects/properties/haschanged/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
},
"template": "typescript"
}
Includes Projects Properties Init Manifest.Json (`docs/_includes/projects/properties/init/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
},
"template": "typescript"
}
Includes Projects Properties Initmarkup Manifest.Json (`docs/_includes/projects/properties/initmarkup/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
},
"template": "typescript"
}
Includes Projects Properties Inittypescript Manifest.Json (`docs/_includes/projects/properties/inittypescript/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.ts",
"index.html",
"index.ts"
],
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
},
"template": "typescript"
}
Includes Projects Style Classmap Manifest.Json (`docs/_includes/projects/style/classmap/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html"
],
"dependencies": {
"lit-element": "latest",
"lit-html": "latest"
},
"template": "polymer"
}
Includes Projects Style Customproperties Manifest.Json (`docs/_includes/projects/style/customproperties/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html"
],
"dependencies": {
"lit-element": "latest"
},
"template": "polymer"
}
Includes Projects Style Host Manifest.Json (`docs/_includes/projects/style/host/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html"
],
"dependencies": {
"lit-element": "latest"
},
"template": "polymer"
}
Includes Projects Style Inherited Manifest.Json (`docs/_includes/projects/style/inherited/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html"
],
"dependencies": {
"lit-element": "latest"
},
"template": "polymer"
}
Includes Projects Style Inherited2 Manifest.Json (`docs/_includes/projects/style/inherited2/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html"
],
"dependencies": {
"lit-element": "latest"
},
"template": "polymer"
}
Includes Projects Style Inherited3 Manifest.Json (`docs/_includes/projects/style/inherited3/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html"
],
"dependencies": {
"lit-element": "latest"
},
"template": "polymer"
}
Includes Projects Style Introbutton Manifest.Json (`docs/_includes/projects/style/introbutton/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html"
],
"dependencies": {
"lit-element": "latest"
},
"template": "polymer"
}
Includes Projects Style Introcustomprops Manifest.Json (`docs/_includes/projects/style/introcustomprops/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html"
],
"dependencies": {
"lit-element": "latest"
},
"template": "polymer"
}
Includes Projects Style Intromargin Manifest.Json (`docs/_includes/projects/style/intromargin/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html"
],
"dependencies": {
"lit-element": "latest"
},
"template": "polymer"
}
Includes Projects Style Maps Manifest.Json (`docs/_includes/projects/style/maps/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html"
],
"dependencies": {
"lit-element": "latest",
"lit-html": "latest"
},
"template": "polymer"
}
Includes Projects Style Mapsextended Manifest.Json (`docs/_includes/projects/style/mapsextended/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-button.js",
"my-element.js",
"index.html"
],
"dependencies": {
"lit-element": "latest",
"lit-html": "latest"
},
"template": "polymer"
}
Includes Projects Style Nestedcss Manifest.Json (`docs/_includes/projects/style/nestedcss/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html"
],
"dependencies": {
"lit-element": "latest"
},
"template": "polymer"
}
Includes Projects Style Perinstanceexpressions Manifest.Json (`docs/_includes/projects/style/perinstanceexpressions/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html"
],
"dependencies": {
"lit-element": "latest"
},
"template": "polymer"
}
Includes Projects Style Slottedbase Manifest.Json (`docs/_includes/projects/style/slottedbase/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html"
],
"dependencies": {
"lit-element": "latest"
},
"template": "polymer"
}
Includes Projects Style Slottedselector Manifest.Json (`docs/_includes/projects/style/slottedselector/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html"
],
"dependencies": {
"lit-element": "latest"
},
"template": "polymer"
}
Includes Projects Style Specificity Manifest.Json (`docs/_includes/projects/style/specificity/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html"
],
"dependencies": {
"lit-element": "latest"
},
"template": "polymer"
}
Includes Projects Style Styleatemplate Manifest.Json (`docs/_includes/projects/style/styleatemplate/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html"
],
"dependencies": {
"lit-element": "latest"
},
"template": "polymer"
}
Includes Projects Style Stylecustomtag Manifest.Json (`docs/_includes/projects/style/stylecustomtag/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html"
],
"dependencies": {
"lit-element": "latest"
},
"template": "polymer"
}
Includes Projects Style Stylemap Manifest.Json (`docs/_includes/projects/style/stylemap/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-button.js",
"index.html"
],
"dependencies": {
"lit-element": "latest",
"lit-html": "latest"
},
"template": "polymer"
}
Includes Projects Style Stylemap2 Manifest.Json (`docs/_includes/projects/style/stylemap2/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"index.html",
"div-3.js"
],
"dependencies": {
"lit-html": "latest",
"lit-element": "latest"
},
"template": "polymer"
}
Includes Projects Style Superstyles Manifest.Json (`docs/_includes/projects/style/superstyles/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"super-element.js",
"my-element.js",
"index.html"
],
"dependencies": {
"lit-element": "latest"
},
"template": "polymer"
}
Includes Projects Style Theming Manifest.Json (`docs/_includes/projects/style/theming/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html"
],
"dependencies": {
"lit-element": "latest"
},
"template": "polymer"
}
Includes Projects Style Unsafecss Manifest.Json (`docs/_includes/projects/style/unsafecss/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html"
],
"dependencies": {
"lit-element": "latest"
},
"template": "polymer"
}
Includes Projects Style Unsafecss1 Manifest.Json (`docs/_includes/projects/style/unsafecss1/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html"
],
"dependencies": {
"lit-element": "latest"
},
"template": "polymer"
}
Includes Projects Style Where Manifest.Json (`docs/_includes/projects/style/where/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"app-styles.css",
"index.html"
],
"dependencies": {
"lit-element": "latest"
},
"template": "polymer"
}
Includes Projects Docs Templates Compose Manifest.Json (`docs/_includes/projects/docs/templates/compose/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-page.js",
"index.html",
"index.ts"
],
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
},
"template": "typescript"
}
Includes Projects Docs Templates Composeimports Manifest.Json (`docs/_includes/projects/docs/templates/composeimports/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-page.js",
"my-header.js",
"my-article.js",
"my-footer.js",
"index.html",
"index.ts"
],
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
},
"template": "typescript"
}
Includes Projects Docs Templates Databinding Manifest.Json (`docs/_includes/projects/docs/templates/databinding/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
},
"template": "typescript"
}
Includes Projects Docs Templates Define Manifest.Json (`docs/_includes/projects/docs/templates/define/manifest.json`)
{
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"title": "lit-element code sample",
"description": "lit-element code sample",
"template": "typescript",
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
}
}
Includes Projects Docs Templates Design Manifest.Json (`docs/_includes/projects/docs/templates/design/manifest.json`)
{
"files": [
"dom-manip.js",
"update-properties.js",
"index.html",
"index.ts"
],
"title": "lit-element code sample",
"description": "lit-element code sample",
"template": "typescript",
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
}
}
Includes Projects Docs Templates Expressions Manifest.Json (`docs/_includes/projects/docs/templates/expressions/manifest.json`)
{
"title": "lit-element code sample",
"description": "lit-element code sample",
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
},
"template": "typescript"
}
Includes Projects Docs Templates Namedslots Manifest.Json (`docs/_includes/projects/docs/templates/namedslots/manifest.json`)
{
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"title": "lit-element code sample",
"description": "lit-element code sample",
"template": "typescript",
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
}
}
Includes Projects Docs Templates Renderroot Manifest.Json (`docs/_includes/projects/docs/templates/renderroot/manifest.json`)
{
"files": [
"default-root.js",
"light-dom.js",
"index.html",
"index.ts"
],
"title": "lit-element code sample",
"description": "lit-element code sample",
"template": "typescript",
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
}
}
Includes Projects Docs Templates Slotid Manifest.Json (`docs/_includes/projects/docs/templates/slotid/manifest.json`)
{
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"title": "lit-element code sample",
"description": "lit-element code sample",
"template": "typescript",
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
}
}
Includes Projects Docs Templates Slots Manifest.Json (`docs/_includes/projects/docs/templates/slots/manifest.json`)
{
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"title": "lit-element code sample",
"description": "lit-element code sample",
"template": "typescript",
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
}
}
Includes Projects Try Create After Manifest.Json (`docs/_includes/projects/try/create/after/manifest.json`)
{
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"title": "LitElement Example",
"description": "LitElement Example",
"template": "typescript",
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
}
}
Includes Projects Try Create Before Manifest.Json (`docs/_includes/projects/try/create/before/manifest.json`)
{
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"title": "LitElement Example",
"description": "LitElement Example",
"template": "typescript",
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
}
}
Includes Projects Try Events After Manifest.Json (`docs/_includes/projects/try/events/after/manifest.json`)
{
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"title": "LitElement Example",
"description": "LitElement Example",
"template": "typescript",
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
}
}
Includes Projects Try Events Before Manifest.Json (`docs/_includes/projects/try/events/before/manifest.json`)
{
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"title": "LitElement Example",
"description": "LitElement Example",
"template": "typescript",
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
}
}
Includes Projects Try Logic After Manifest.Json (`docs/_includes/projects/try/logic/after/manifest.json`)
{
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"title": "LitElement Example",
"description": "LitElement Example",
"template": "typescript",
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
}
}
Includes Projects Try Logic Before Manifest.Json (`docs/_includes/projects/try/logic/before/manifest.json`)
{
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"title": "LitElement Example",
"description": "LitElement Example",
"template": "typescript",
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
}
}
Includes Projects Try Properties After Manifest.Json (`docs/_includes/projects/try/properties/after/manifest.json`)
{
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"title": "LitElement Example",
"description": "LitElement Example",
"template": "typescript",
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
}
}
Includes Projects Try Properties Before Manifest.Json (`docs/_includes/projects/try/properties/before/manifest.json`)
{
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"title": "LitElement Example",
"description": "LitElement Example",
"template": "typescript",
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
}
}
Includes Projects Try Style After Manifest.Json (`docs/_includes/projects/try/style/after/manifest.json`)
{
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"title": "LitElement Example",
"description": "LitElement Example",
"template": "typescript",
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
}
}
Includes Projects Try Style Before Manifest.Json (`docs/_includes/projects/try/style/before/manifest.json`)
{
"files": [
"my-element.js",
"index.html",
"index.ts"
],
"title": "LitElement Example",
"description": "LitElement Example",
"template": "typescript",
"dependencies": {
"lit-element": "latest",
"@webcomponents/webcomponentsjs": "latest"
}
}
--- METRICS ---