The Material 3 web components Google never shipped.
A complete Material 3 component library for the web — from buttons and inputs to data tables, date pickers, autocomplete, trees, and navigation.
Familiar to users. Native to AI. Your users already know the interface from Android. Your coding agent gets every tag, attribute, and event, making these components easy to use, compose, and extend.
One script tag · no bundler · no framework
claude plugin install material-web@viewflow
Claude Code · or npx skills add viewflow/material
|
|
Status | ||||
|---|---|---|---|---|---|
| PO-2026-0141 | Acme Corp | May 4 | 24,500.00 | Pending | |
| PO-2026-0140 | Globex Ltd | May 4 | 12,000.00 | Approved | |
| PO-2026-0139 | Initech | May 3 | 36,800.00 | Pending | |
| PO-2026-0138 | Hooli | May 3 | 8,400.00 | Received | |
| PO-2026-0137 | Stark Industries | May 2 | 92,400.00 | Approved | |
| PO-2026-0136 | Wayne Enterprises | May 2 | 3,200.00 | On hold | |
| PO-2026-0135 | Tyrell Corp | May 1 | 14,600.00 | Pending | |
| PO-2026-0134 | Aperture Sci. | Apr 30 | 47,100.00 | Received | |
| PO-2026-0133 | Cyberdyne | Apr 29 | 22,000.00 | Cancelled |
Sort a column or select a row — this is the real component, not a screenshot.
POST /purchasing/orders/ Content-Type: application/x-www-form-urlencoded — submit the form to see the payload —
Showcases
Composed screens you can open and click through, each built from the components below.
ERP · Purchase orders
Table-style enterprise list with KPI cards, status filters, sortable columns, and a tabbed detail dialog.
CRM pipeline
Kanban board of sales deals with a funnel widget, filter chips, per-card menus, and an add-deal form.
Email client
List-detail layout with search app bar, navigation rail, multi-select inbox, and compose dialog.
Field service · mobile
Mobile-first work-order list: bottom navigation, filters in bottom sheets, adaptive detail. Opens in a phone-sized window.
Frameworks
These are standard custom elements. Your framework needs one line of configuration to accept them. The elements are the API, and they ship with types for every editor and every framework's template checker.
Nothing to configure. The inputs are form-associated, so a swap posts real values and the server renders the next fragment.
<form hx-post="/purchasing/orders/" hx-target="#panel"> <material-autocomplete name="vendor" required>…</material-autocomplete> <material-date-range-field start-name="from" end-name="to"></material-date-range-field> <material-button type="submit" label="Save"></material-button> </form> <!-- Unpoly is the same markup with up-submit / up-target -->
React 19 sets properties on custom elements and forwards attributes, so the elements work unwrapped. Custom events come through a ref.
import 'advanced-material-web/components/material-select.js'; const ref = useRef(null); useEffect(() => { const el = ref.current; const onChange = (e) => setWarehouse(e.detail.value); el.addEventListener('valueChange', onChange); return () => el.removeEventListener('valueChange', onChange); }, []); <material-select ref={ref} label="Warehouse" />
One compiler option tells Vue to leave material-* tags alone.
After that :prop and @event bind natively.
// vite.config.ts vue({ template: { compilerOptions: { isCustomElement: (tag) => tag.startsWith('material-'), } } }) <!-- then, anywhere --> <material-select :label="label" @valueChange="onChange"> <material-option value="ams">Amsterdam</material-option> </material-select>
Svelte compiles to the DOM, so custom elements need no configuration at all —
props are attributes and on: listens to the real event.
import { defineCustomElements } from 'advanced-material-web/loader'; defineCustomElements(); <material-select label="Warehouse" on:valueChange={(e) => (warehouse = e.detail.value)}> <material-option value="ams">Amsterdam</material-option> </material-select>
Add the schema once per module and Angular stops treating unknown tags as an
error; [prop] and (event) then work as usual.
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; @Component({ schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <material-select [label]="label" (valueChange)="onChange($event)"> <material-option value="ams">Amsterdam</material-option> </material-select> ` })
What ships for the type checker
The package carries a manifest for every editor and template compiler, generated from the components rather than written by hand, so tag names, attributes and events autocomplete and typo-check wherever you write them.
Per-component imports come from
advanced-material-web/components, so a page that uses six elements
bundles six elements.
Components
This is the set a purchasing screen, an admin console or a field-service app runs on. Every name below opens a working page.
Text fields
Data & display
Navigation
Search & progress
Built in, everywhere
Theming
Colour and size come from --md-sys-color-* custom properties that
inherit into every shadow root. Change the properties, and every component
follows: no component code, no rebuild, no per-component override.
Both controls below act on the panel, not the page — custom properties resolve per element, so a theme class on any container re-themes that subtree and nothing else.
And the accent, set as custom properties straight onto the same panel — these win over whichever class it is wearing.
<html class="light"> <link rel="stylesheet" href="theme.css"> /* six classes: light · dark · ±medium · ±high contrast */ /* your own palette: export from Material Theme Builder */
Install
1 · As a skill, so your agent writes the markup
The agent gets every tag, attribute, event, and the page wiring, and it stops guessing. In Claude Code:
claude plugin marketplace add viewflow/material claude plugin install material-web@viewflow
In Cursor, Codex, OpenCode, or Gemini CLI, use the skills CLI:
npx skills add viewflow/material
2 · As a package, with your bundler
npm install advanced-material-web
import { defineCustomElements } from 'advanced-material-web/loader'; import 'advanced-material-web/theme.css'; defineCustomElements();
3 · From a CDN, with nothing installed
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/advanced-material-web@26/css/theme.css"> <script type="module" src="https://cdn.jsdelivr.net/npm/advanced-material-web@26/cdn/material.min.js"></script> <material-button variant="filled" label="It works"></material-button>
jsDelivr and unpkg both mirror npm, so the npm release is the CDN release.
Pin the major version in production, as above. For a page that uses a handful
of components, load
advanced-material-web/dist/material/material.esm.js instead: a small
entry that fetches chunks on demand.
Licence
You can use every component in a product you sell. There is no fee and no separate commercial licence.
- AGPL-3.0-or-later
- Viewflow Library Exception
- Unmodified use permitted
Modifying the library — or building on it with an LLM?
The exception covers shipping the package unmodified. If you modify the library itself, your changes are a derived work and remain subject to the AGPL.
The same applies when you put the library's source code into an LLM context to generate new components from it: we treat that as copying from the library, not as an independent implementation.
If you want to fork the library, modify it, or let an AI agent build on its source, use Viewflow Pro. It provides the same code under a permissive commercial licence, without copyleft.
The library is licensed under AGPL-3.0-or-later with the Viewflow Library Exception, following the same approach used by the GCC Runtime Library. Install the package, ship it unmodified inside your proprietary application, keep your own licence, and publish no source code. The AGPL applies to changes you make to the library itself — not to your application.
Integration is three lines: add the theme stylesheet, put a theme class on
<html>, and include the Material Symbols font for icon
ligatures. A coding agent can write those three lines without supervision.
Questions
Do I need a bundler or a framework?
No. One script tag registers every element, and each component carries its own CSS inside its shadow root. The only stylesheet you load is a theme file of custom properties. It works with React, Vue, Django templates, Rails, or a plain HTML file. This page is a plain HTML file.
How is this different from Google's material-web?
Google has rebuilt Material for the web four times since 2015: Material Design
Lite, MDC Web, the @material/mwc-* beta, and today's
@material/web. The beta stayed at version 0.x for four years and
never reached 1.0. The
roadmap
for @material/web gives its status as maintenance mode. It also
lists seventeen components that Google did not ship. These include the data
table, the date and time pickers, autocomplete and search. They also include the
navigation bar, drawer and rail, the top app bar, snackbar, tooltip, card and
segmented button. A business application needs these components. This library
has all of them.
This library also has no runtime dependency and no build step. Its components add
behavior to server-rendered markup, such as a real <table> or a
real <form> post. They do not replace that markup with a
client-side data model.
Can I ship it in a commercial product?
Yes. There is no fee and no separate commercial licence. The library is licensed under AGPL-3.0-or-later with the Viewflow Library Exception, following the same approach used by the GCC Runtime Library. Install the package, ship it unmodified inside your proprietary application, keep your own licence, and publish no source code. The AGPL applies to changes you make to the library itself — not to your application.
How large is it?
The single bundle is about 585 KB with all 72 elements eager. For a page that uses a handful of components, the lazy loader entry is small and fetches chunks on demand. There is no runtime stylesheet fetch either way, so there is no flash of unstyled content.