Skip to content

AWC UI vs @material/web

Both libraries implement Material Design 3 as framework-agnostic web components, and both register md-* custom element tags. That overlap makes the comparison unusually direct — and makes the two mutually exclusive on a single page (see the tag-collision FAQ below).

Criterion@material/webAWC UI
Component count~15 component families (buttons, checkbox, chips, dialog, fields, icon buttons, lists, menus, progress, radio, select, slider, switch, tabs, …)56 components across forms, navigation, containment, data display, pickers, and charts
SSR / declarative shadow DOMClient-side rendering; built on Lit, where SSR support lives in the experimental @lit-labs/ssr packageFirst-party hydrate module (@awc-ui/core/hydrate) renders declarative shadow DOM on the server; components hydrate in place
ChartsNonemd-line-chart, md-bar-chart, md-area-chart, md-pie-chart, md-sparkline — see the charts gallery
Data tableNoneFull md-table family: sorting, pagination, row expansion, selection, toolbar
PickersNone shipped (a date picker was planned but never released)md-date-picker, md-time-picker, md-color-picker
Accessibility in CIComponents built to Material and ARIA guidance by the team that wrote themThe same ARIA discipline, plus an axe-core audit of every Storybook story runs in CI on every pull request
Maintenance statusIn maintenance mode since June 2024, per the project README: existing components are maintained, new component work is pausedActively developed
ProvenanceGoogle’s Material Design team — the canonical M3 web implementationIndependent implementation of the public M3 spec
Install baseLarge, with years of production use across Google properties and the wider communityYounger and smaller
Framework storyFramework-agnostic custom elementsFramework-agnostic custom elements, plus published React, Angular, Vue, and Svelte wrapper packages

An honest comparison concedes the other side’s strengths, and @material/web has real ones:

  • Provenance. It is written by the same organization that writes the Material Design spec. When the spec is ambiguous, their reading is the authoritative one.
  • Maturity. Its core components have shipped in high-traffic production surfaces for years. That kind of hardening only comes from install base.
  • Stability as a feature. Maintenance mode also means a frozen API surface — if your product only needs the components it already has, “no new components” can read as “no churn.”

The clearest difference is the catalog. Every component below is one @material/web never shipped — each demo is live.

Live preview
Show code for each technology
<!-- index.html — register the AWC UI elements once -->
<script type="module">
  import '@awc-ui/core/define';
</script>

<md-date-picker variant="docked" label="Due date" clearable style="max-width: 280px;"></md-date-picker>
Live preview — archive, then undo
Archive message
Show code for each technology
<!-- index.html — register the AWC UI elements once -->
<script type="module">
  import '@awc-ui/core/define';
</script>

<md-button variant="tonal" data-archive>Archive message</md-button>
<md-snackbar message="Message archived" action="Undo"></md-snackbar>

<script type="module">
  const snackbar = document.querySelector('md-snackbar');
  document.querySelector('[data-archive]').addEventListener('click', () => snackbar.show());
  snackbar.addEventListener('mdAction', () => snackbar.hide('action'));
</script>
Live preview — hover or focus the button
Show code for each technology
<!-- index.html <head> — the icon font the components draw from -->
<link rel="stylesheet"
  href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&display=swap">

<!-- index.html — register the AWC UI elements once -->
<script type="module">
  import '@awc-ui/core/define';
</script>

<md-tooltip text="Download report">
  <md-icon-button icon="download" aria-label="Download report"></md-icon-button>
</md-tooltip>
Live preview
Show code for each technology
<!-- index.html <head> — the icon font the components draw from -->
<link rel="stylesheet"
  href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&display=swap">

<!-- index.html — register the AWC UI elements once -->
<script type="module">
  import '@awc-ui/core/define';
</script>

<md-icon-button icon="mail" variant="standard" aria-label="Inbox, 5 unread">
  <md-badge value="5"></md-badge>
</md-icon-button>
<md-icon-button icon="notifications" variant="standard" aria-label="Notifications, new activity">
  <md-badge variant="small"></md-badge>
</md-icon-button>
Live preview
Show code for each technology
<!-- index.html — register the AWC UI elements once -->
<script type="module">
  import '@awc-ui/core/define';
</script>

<md-rating value="3.5" precision="0.5" show-value-label rating-label="Product rating"></md-rating>
Live preview — try pasting a six-digit code
Show code for each technology
<!-- index.html — register the AWC UI elements once -->
<script type="module">
  import '@awc-ui/core/define';
</script>

<md-otp-field length="6" group-size="3" label="Verification code" style="max-width: 320px;"></md-otp-field>

To see these composed into full screens — checkout flows, dashboards, data consoles — browse the recipes.

Can I use AWC UI and @material/web on the same page?

No. Both libraries define the same md-* tag names (md-button, md-checkbox, md-dialog, …), and the custom elements registry allows each tag to be defined exactly once per page — the second library to load throws a DOMException on customElements.define. There is no scoping trick that makes them coexist in one document.

So how do I migrate?

Treat it as a per-page (or per-app) decision, not a per-component one:

  • Whole page at a time. Swap the loader import, keep the markup, and reconcile attribute differences. Because the tag names match, most templates port without renaming elements — the diff is in attributes and events, not structure.
  • Separate documents can differ. A multi-page site or micro-frontend setup with genuinely separate documents (including iframes) can run one library per document during a transition.
  • What doesn’t work: trialing AWC UI inside an existing @material/web page. That is a real constraint of the shared tag prefix, and it cuts both ways.

Is the shared prefix intentional?

The md-* prefix is the natural one for a Material Design library, and keeping it means a migration reads as an attribute-level diff rather than a rewrite. The cost is the mutual exclusion above — we would rather state that plainly than have you discover it in a console error.


Material Design is a trademark of Google LLC. AWC UI is not affiliated with or endorsed by Google.