DEV Community

Cover image for 10 years of building Web Components: the story of <vaadin-combo-box>
Serhii Kulykov
Serhii Kulykov

Posted on

10 years of building Web Components: the story of <vaadin-combo-box>

Today marks the 10 year “birthday” of one of the most complex and sophisticated parts of Vaadin web components: the <vaadin-combo-box>. I had a chance to use this component and contribute to it before joining Vaadin. Today, I’d like to explore its story, and to illustrate how Vaadin web components evolved over time.

Building a combo-box component is quite a challenging task, but I’m not going to dig deep into technical details. There is a great blog post about this topic by Jan Miksovsky, creator of Elix library. I also recommend this article by the React Aria team about their ComboBox implementation and challenges they faced.

At Vaadin, we invested significant effort into making our combo-box both feature rich and accessible. Our component supports different ways of assigning data, lazy loading, internal and external filtering. As for accessibility, in our case it took some time to get things right — especially because of using Shadow DOM.

Over the years, our work on <vaadin-combo-box> followed the Web Components evolution. We created it using Polymer 1 at the time of the so-called “v0” proposals. Then we ported it to following Polymer versions 2 and 3. And this year, we finally migrated to Lit. But let’s take a step back to see where it all started.

First steps

The initial prototype of <vaadin-combo-box> was internally using... a single column data grid! More specifically, an early version of <vaadin-grid> — another of Vaadin components with an even more fascinating story. Both were parts of the Vaadin Elements set featured on the Polycasts episode by Rob Dodson.

During the next iteration, the combo-box was updated to use <iron-list> — a virtual scroller element by the Polymer team (which was even used by Chrome UI, especially history and downloads). Around the same time it was changed to use overlay teleportation, adopted by many other Vaadin components later.

Visually the component was designed to be aligned with a set of Paper Elements by the Polymer team. At the time, the future was looking bright: Web Components started to gain cross-browser support, the Polymer ecosystem was rapidly evolving — and Vaadin Elements team worked hard to catch up.

The first stable version of <vaadin-combo-box> was released in 2016 — that’s when I had a chance to use it in production in my previous company. In just a bit over a year, the component reached version 2.0 based on Polymer 2, with support for stable implementations of custom elements and shadow DOM.

And that's what is great about Web Components: while the implementation details change over time, ultimately it’s still standard HTML. Both <vaadin-combo-box> version 2.0 and this year's upcoming 25.0 release are based on the same standards, and provide mostly the same API: properties, attributes and events.

Early days

Starting from 2017, Vaadin Elements were undergoing a major internal overhaul related to styling. While custom elements created by the Polymer team originally were using “CSS mixins” and @apply, that proposal ended up being abandoned in favor of shadow parts, which only got implemented in browsers 2 years later.

That’s when the Vaadin Elements team came up with own future proof solution: ThemableMixin, used for style injection into shadow roots. While there was no native ::part() support yet, elements inside <vaadin-combo-box> were updated to provide part attributes (in fact, some of these parts are still there today).

Also, around this time usage of Paper Elements was replaced with new web components built by Vaadin, like <vaadin-text-field>, and provided new default styles. These changes marked version 3.0, and I had a chance to contribute to it after joining Vaadin in late 2017. But that was still only the start of the journey.

The next step of the styling update for Vaadin web components was related to theming. In version 4.0, which became part of the Vaadin platform 10, we updated <vaadin-combo-box> to use the new Lumo theme by default. Later we also added Material theme, which could be used by importing different HTML files.

Yes, back then Polymer 2 was relying on HTML imports — another proposal that didn’t reach cross-browser adoption. It was announced that the next version will move to ES modules. So in our team, we had to make adjustments to all Vaadin components in preparation for the long-awaited Polymer 3 stable release.

The road to Lit

By the end of 2018, Polymer 3.0 was released as stable… just to be effectively deprecated. Google recommended to use new libraries — lit-html and lit-element for creating Web Components. With our rapidly growing codebase, moving to new libraries would mean a significant amount of work, which we couldn't afford.

However, at this point the Polymer dependency became a technical debt, while the Lit team built new things that many developers, including myself, were eager to try. This is how Vaadin web components journey to Lit started. However, after some prototyping we were only able to ship a few incomplete alpha versions.

Then it became clear that we need to at least get the codebase to Polymer 3 first. While the main goal was to ship proper TypeScript definitions, while working on this project, I also migrated our tests to @web/test-runner, a great tool by the Modern Web team. We still use it for testing Vaadin web components today.

The next step towards Lit was moving Vaadin web components to the monorepo. Thanks to dropping bower support and significantly faster tests with the new test runner, we could finally consolidate our codebase. This also allowed us to ship internal improvements, like replacing <iron-list> with the new virtualizer.

Another milestone worth a separate blog post was improving accessibility of many components including <vaadin-combo-box> in Vaadin 22. In this version, we moved interactive elements like <input> and <label> to the light DOM. We also improved code sharing using a set of mixins and Lit reactive controllers.

Where we are today

Refactoring is hard. Especially, it's hard to justify having to spend months of work on something considered and implementation detail. So it took years for Vaadin web components to be migrated to Lit. And I’m grateful to all my colleagues from the Vaadin design system team for our joint effort to make it happen.

To make moving to Lit smoother, we created PolylitMixin, a small layer on top of LitElement bringing support for Polymer features like property observers. We also adapted our test setup to run the same set of test suites for both Lit and Polymer. Finally, for every web component we added an experimental Lit version.

This was especially tricky because of numerous cross-dependencies and certain components being tightly coupled with each other. In case of <vaadin-combo-box>, most of its logic was reused in our time-picker component. In Vaadin 23, we added a multi-select version of the combo-box, initially built as an add-on.

Finally, this year we completed the Lit migration project and removed Polymer dependency from the upcoming Vaadin 25. As for the combo-box logic, it was split it into smaller mixins used by time-picker and multi-select-combo-box components, instead of "internal" versions of combo-box in shadow DOM.

And one last important change that I’d like to highlight: after almost 10 years, we no longer use DOM teleportation for our overlays! Thanks to the native popover API landing in all modern browsers including Safari 17+, we can keep overlays in shadow DOM of their respective components and use slots for content.

What’s next

While Vaadin 25 is still in alpha and has some rough edges, you can already try new Lit based versions of Vaadin web components. I’m planning to share more updates about our progress in the upcoming months, as we approach beta and stable release. In the meantime, you can check out following resources:

  • Live demos of new base styles that make customizing Vaadin web components much easier and will serve as a foundation for the new Aura theme,
  • New API documentation built using 11ty static site generator, covering JS, CSS and TypeScript API provided by our web components.

And if you have never used Web Components yet, it’s definitely worth trying! Despite all the challenges, they eventually proved to be the right choice for Vaadin components. And by the way, the latest alpha of of the @vaadin/combo-box is almost 40% smaller than the Polymer 3 version from 5 years ago.

Happy 10 years anniversary to <vaadin-combo-box>! It’s been a great journey.

Top comments (0)