Building apps
Building apps with lit-material
The component packages (button, dialog, navigation, …) are enough to build individual UI, but a whole
application typically also needs a router, a place for state shared across the app, and a way to pass values
(like the current theme) down a component tree without prop drilling: the three things a React app
usually reaches for (router, context, Redux). This guide wires up the lit-material equivalents in
one minimal example, then extends it with data fetching, form validation, and i18n. See each package's
own README for full API detail:
@lit-material/router: routing for a single page app.@lit-material/store: a state store shaped like Redux.-
@lit-material/core'sthemeContext/localeContext: built on the standard@lit/contextprotocol. @lit-material/task: a reactive controller for async work (data fetching).@lit-material/form: a reactive controller that tracks a form's overall validity.
Running the docs site locally? pnpm --filter @lit-material/app-shell-demo dev to preview
this demo in isolation.
-
Install
-
2A shared store
-
3Router, theme context & the app shell
-
4Data fetching
-
5Form validation
-
6Internationalization (i18n)
-
7What this doesn't cover
Install
npm install @lit-material/router @lit-material/store @lit-material/core @lit-material/task @lit-material/form @lit/context lit
Add @lit/localize too if you're using the i18n step later on.
The entry point is tiny — main.ts loads the design tokens, then the app shell:
import "@lit-material/tokens/css/index.css";
import "./bootstrap-router.js";
import "./app-shell.js";
Deploying under a sub-path (a GitHub Pages project site, for instance, not a domain root) needs one
more line before anything reads the current URL — @lit-material/router's
setBasePath(). The demo above is served under exactly that kind of nested path, so it
needs this too:
import { setBasePath } from "@lit-material/router";
// Served nested at /lit-material/app-shell-demo/ in production (see vite.config.ts) — must run before
// app-shell.js (and the RouteController inside it) ever reads location.pathname. Static imports execute in
// the order listed in main.ts, so this only works because that file lists this import first (mirrors
// apps/docs/src/bootstrap-router.ts). Local dev serves at `/`, where BASE_URL is "/" and this is a no-op.
setBasePath(import.meta.env.BASE_URL);
Skip this file entirely if your app is deployed at a domain root.
The next steps walk through the rest of that demo's source, one file at a time.