Library / SDK
Polymer/polymer avatar
Polymer/polymer

Polymer 3 runs through a browser specifier transform, and its README points at Lit

Our original Web Component library.

22,005 stars1,953 forksHTMLBSD-3-Clause

At a glance

What is it?
Polymer is the original Web Component library, published on npm as @polymer/polymer at version 3.5.2, where main and module resolve to the same file and serving to a browser needs a rewrite step. The README steers new projects to LitElement and leaves Polymer as the target for code that is already on 3.0.
Who is it for?
Pick Polymer when you are maintaining elements that already run on it and you want the smallest change, and pick LitElement for anything new, which is what the README itself advises.
Can I use it commercially?
Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Activity is slowing. The repository last received commits 7 months ago.
What is it written in?
Mainly HTML, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • Polymer/polymer22,005 ★ · -13
Star history
Repository2026-09-212026-11-14
Polymer/polymer22k22k

-13 Stars in the last 54 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

The README opens by steering new projects to LitElement

The first note in the file is not a feature list. It says this is the current stable version of the Polymer library, that at Google I/O 2018 a new Web Component base class, LitElement, was announced as a successor to the PolymerElement base class in this library, and that if you are starting a new project you should consider LitElement instead. For an existing project it points the other way: migrate to 3.0 for best compatibility with the JavaScript ecosystem.

The reason both recommendations fit on one page is interoperability. Because elements are interoperable Web Components, elements built with Polymer 3.0 and elements built with LitElement can be mixed and matched in the same app, so the migration path is one element at a time rather than all at once. That works as an argument for existing code and against new code, which is exactly how the note is written. The naming also has a cost worth naming: the word Polymer in a search box mostly returns chemistry, polymer clay, and biology, so reach for the package name @polymer/polymer or the webcomponents.org entry instead.

npm specifiers have to be rewritten before a browser can load them

Polymer 3.0 is published on npm using JavaScript Modules, which lets it use the native module loader in current major browsers. The catch is spelled out in the same note: because Polymer uses npm conventions to reference dependencies by name, a light transform to rewrite specifiers to URLs is required to run in the browser. The polymer-cli development server performs that transform, and so does `polymer build` for an optimized deployment build. Tools like webpack and Rollup can serve or bundle Polymer elements instead.

That single sentence is the main operational difference from a library that ships a bundle. Opening an HTML file straight from disk does not resolve a bare specifier such as a package name, so local development needs either the CLI server or a bundler in the loop. Both routes are in the README, and both assume a module-compatible toolchain, which is why the getting started path installs the CLI globally with `npm i -g polymer-cli` before serving.

main and module resolve to the same file, and legacy is opt-in by path

The package manifest is short enough to read in full. The name is @polymer/polymer, the version is 3.5.2, and both the main and the module fields point at the identical entry file, polymer-element.js. Alongside it at the repository root sit polymer-legacy.js and polymer-element.js with their TypeScript declaration files, polymer-legacy.d.ts and polymer-element.d.ts, plus interfaces.d.ts.

The consequence is that whatever resolution rule a tool applies, it lands on the same module, so there is no separate modern versus legacy entry to choose between by field. The legacy build is only reachable by naming its path, which means a bundler will not select it for you and a migration away from it has to be explicit. For anyone comparing packages, that duplicated entry is also the fastest way to tell this manifest apart from one that splits a browser and a module build across two files.

Types come from Closure externs generated during install

The manifest wires code generation into the install itself. The scripts section defines generate-types as `gulp generate-externs`, and prepare as `npm run generate-types`, so npm runs that step whenever the package is installed from a directory or a git checkout. The tooling behind it is visible in devDependencies: @polymer/gen-closure-declarations, google-closure-compiler, gulp, and gulp-vulcanize, with an externs directory at the repository root.

The consequence is that installing this library from source is a build, not a copy. The prepare hook needs a working node_modules and a functioning gulp and Closure toolchain before anything else happens, so an install in a stripped environment, an offline machine, or a frozen dependency cache can fail at install time rather than at build time. If you vendor this package, copy the published files instead of installing the repository, or run the generation once and commit what it produced.

Tests and the dev server have to agree on module resolution

Three scripts share one idea. Serve is `polymer serve --npm --module-resolution=node`, test is `npm run lint && polymer test --npm --module-resolution=node`, and lint on its own is `gulp lint`. The same two flags appear in the test command and the development server, which is deliberate: a test run that resolves imports differently from the server you develop against produces failures that no change to your element will fix.

Continuous integration is configured through .travis.yml with a web-component-tester setup in wct.conf.json and a devDependency on wct-browser-legacy. One more script deserves a note because it is destructive by design: regen-package-lock runs `rm -rf node_modules package-lock.json; npm install`. It is there to rebuild the lockfile from scratch, and it will happily delete a node_modules you did not mean to lose.

Non-native browsers get a loader script, not a graceful default

The support claim has a shape worth reading closely. Web Components are implemented natively on Safari and Chrome, which the README puts at about 70 percent of installed browsers, and run well on Firefox, Edge, and IE11 using polyfills. The minimal example makes that concrete: the page loads `@webcomponents/webcomponents-loader.js` from node_modules before importing anything, and the devDependency is @webcomponents/webcomponentsjs at version 2.2.10 or newer.

So there are two supported paths, and which one you are on decides how your page starts. Native browsers get the import and nothing else. Everything else needs the loader script first, which means a fallback branch in your HTML and a polyfill in your dependency graph, and a browser matrix that includes IE11 is a maintenance cost the README does not quantify. Note that the 70 percent figure describes install base at the time the page was written, not a guarantee about your own audience, so check your analytics before you copy that number into a decision.

A properties getter is also your public attribute API

The minimal example is three moving parts. A class extends PolymerElement, a static properties getter describes the element's public property and attribute API, and a static template getter returns an HTMLTemplateElement built with the html tag that includes encapsulated styling and property bindings. Registration is customElements.define, and using the element afterwards is plain markup:

html
<my-element mood="happy"></my-element>

The design consequence sits in that first getter. Declared properties automatically become observed attributes, so the list in the getter is the element's external contract, not an internal detail. Rename one and you have changed an attribute name that other code and other pages may already be setting. The template getter is the other half: styles written inside it are scoped to the element's shadow tree, so a global stylesheet cannot restyle the inside of the element without reaching for the custom element's own template.

Two-way binding means the element can write back into your model

Polymer describes data binding as optional, and lists the two directions separately. When properties in the model for an element get updated, the element can update itself in response. When the element is updated internally, those changes can be propagated back to the model. The example template shows the first direction with a `[[mood]]` placeholder inside a span.

The consequence is ownership of the value. In a one-way binding, your model is the only writer and the element is a projection of it. Turn the second direction on and an interaction inside the element, a checkbox or a field, writes back into the model, so a form built this way has two write paths and one truth to reconcile. Decide per property which side owns the value, because a binding that propagates both ways will quietly resolve conflicts for you and leave you with a value nobody set deliberately.

Editorial conclusion

Pick Polymer when you are maintaining elements that already run on it and you want the smallest change, and pick LitElement for anything new, which is what the README itself advises. Before you commit, check three things: that your bundler or the polymer-cli development server is doing the specifier rewrite, that install is not tripping over the generate-types step in prepare, and that the browser support you need is native rather than polyfilled, because the loader script path is the one most likely to surprise you.

Frequently asked questions

what is polymer

The word covers several unrelated things, including the chemistry term and polymer clay. This repository is the Web Components library: you extend PolymerElement, describe the element's public properties in a static properties getter, return a template, and register the class with customElements.define.

is polymer just plastic

Nothing in this repository concerns plastic or materials. It is a front-end library for building custom HTML elements on top of the Web Components APIs, published on npm as @polymer/polymer and currently at version 3.5.2, and the polymer clay and chemistry senses of the word come from elsewhere.

How do I run Polymer in a project?

Install the package with `npm i @polymer/polymer` and the command line tools with `npm i -g polymer-cli`, then start the development server with `polymer serve --npm`. Running in a browser requires a module-compatible toolchain because the specifier rewrite is not native, and webpack or Rollup can do the same job.

Can Polymer 3 elements live next to LitElement elements?

Yes. Thanks to Web Components interoperability, elements built with Polymer 3.0 and elements built with LitElement can be mixed and matched in the same app, so a project already on Polymer 3.0 can migrate to LitElement one element at a time. LitElement was announced at Google I/O 2018 as the successor to PolymerElement.

Official sources

  1. License: BSD-3-Clause
  2. Polymer/polymer on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/polymer-polymer.svg)](https://hysenlabs.com/projects/polymer-polymer)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/polymer-polymer.svg)](https://hysenlabs.com/projects/polymer-polymer)

Was this useful?