webui-lit-migration

star 24.0k

Guide for migrating Chromium WebUI components from Polymer to Lit.

chromium By chromium schedule Updated 3/18/2026

name: webui-lit-migration description: >- Guide for migrating Chromium WebUI components from Polymer to Lit. Activate this skill if the user asks to migrate a file or component to Lit.

Chromium WebUI Lit Migration

Step 1: Run scripts/run_codemod_script.sh

IMPORTANT: Always do this first, as it automates trivial migration steps.

Run the script in this skill's scripts directory. Pass the path to the Polymer based element's TS class definition file as the parameter. Example: ./webui-lit-migration/scripts/run_codemod_script.sh
chrome/browser/resources/certificate_manager/certificate_entry.ts

Step 2: .ts file cleanup

  1. Replace PolymerElement import The script replaces this import except in cases where multiple things are imported from Polymer. If an import from polymer_bundled.min.js is still present after running the script, remove it and add: import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js';

  2. Update 'extends PolymerElement' The script will update this in the case that no mixins are used. If there is still a reference to PolymerElement: replace SomeMixin(PolymerElement) with SomeMixinLit(CrLitElement)

  3. Address any "TODO: Port this observer to Lit" comments left by the migration script as follows: a. Examine the observer (e.g. 'onFooChanged_') method. If it does not reference the DOM, add the following in a willUpdate callback. If it does reference the DOM (e.g., this.shadowRoot or this.$), add this in a updated() lifecycle callback instead.

if (changedProperties.has('foo')) {
  this.onFooChanged_();
}
b. If the property being observed is protected or private, the
   changedProperties lifecycle method parameter will require a cast:
const changedPrivateProperties = changedProperties as Map<PropertyKey, unknown>;
if (changedPrivateProperties.has('myProperty_')) { ... }
c. Remove the added TODO and the commented out observer line.
  1. Address any complex observers in a similar way. Look for an "observers: " line:
observers: ['onFooOrBarChanged_(foo, bar)']

Remove the line and for each observer listed, add to a lifecycle method as described for single property observers, but check changedProperties for any of the properties listed in the observer:

if (changedProperties.has('foo') || changedProperties.has('bar')) {
  this.onFooOrBarChanged_();
}
  1. Move value initialization out of 'properties' to the declaration.

Replace

  foo: {
    type: String,
    value: 'foo',
  },
  ...
  value: string;

with

  foo: {
    type: String,
  },
  ...
  accessor value: string = 'foo';

For uninitialized properties, if a TS compiler error is thrown on build, initialize to a dummy or default value rather than using a non-null assertion operator or changing the type definition.

Step 3: Check for any missing shared CSS styles.

If any imported style file does not exist, check if the Polymer version (same file path, but without _lit suffix) exists. If so, generate the Lit version as follows:

  1. Create Lit Version: Create [shared_style]_style_lit.css.
  2. Copy Content: Copy the CSS rules to the new *_lit.css file.
  3. Update Metadata: Use #type=style-lit in the metadata. Update any imported and included styles to the lit version (e.g. cr_shared_style.css.js import --> cr_shared_style_lit.css.js import, include="cr-shared-style" becomes include="cr-shared-style-lit").
  4. Empty Original: Clear the original *_style.css file, leaving only its] metadata header and a comment:
    /* Purposefully empty since this style is generated at build time from the
     * equivalent Lit version. */
    
  5. Import in Component: Component CSS should import the new *_lit.css.js and include it.
  6. BUILD.gn: Add the new *_lit.css to css_files.

Step 4: Update .html.ts template file

  1. Replace any