Themvora + Material UI
@themvora/mui
Derive a Material UI theme from Themvora semantic tokens and rebuild it automatically whenever the active theme changes.
Pick your framework
The adapter derives a Material UI theme from Themvora semantic tokens and connects it to Material UI's own theme system. Pick the framework you are building in — every snippet below follows that choice.
Installation
Two installs. The first is Material UI's own — see its installation guide if you need more than this. The second is Themvora, and it names the runtime package for React.
pnpm add @mui/material @emotion/react @emotion/styledpnpm add @themvora/core @themvora/react @themvora/muiHow it works
Themvora builds Material UI's own theme object from its tokens and hands it to Material UI's provider. The library keeps doing exactly what it always did — it just gets its theme from Themvora instead of from you. That is why this adapter is a provider component: there is a real theme object to hand over.
Quick Start
Material UI wired up for React. Switch the picker above and everything below changes with it.
import type { ReactNode } from "react";
import { useThemeRuntime } from "@themvora/react";
import { MuiThemeProvider } from "@themvora/mui";
// Rebuilds the library's native theme on every change.
export function ThemeZone({ children }: { children: ReactNode }) {
const runtime = useThemeRuntime();
return <MuiThemeProvider runtime={runtime}>{children}</MuiThemeProvider>;
}Mount the provider
Themvora's provider owns the active theme. Drop this at your app root — everything below it can read and change the theme, and the bridge above picks up each change automatically.
import { ThemeProvider } from "@themvora/react";
import { Page } from "./Page";
import { ThemeZone } from "./components/ThemeZone";
export default function App() {
return (
<ThemeProvider defaultTheme="plum-light" initialMode="system">
<ThemeZone>
<Page />
</ThemeZone>
</ThemeProvider>
);
}Use Material UI components
import { Button, Card, CardContent, Typography } from "@mui/material";
export function Example() {
return (
<Card>
<CardContent>
<Typography variant="h6">Themvora + Material UI</Typography>
<Button variant="contained">Primary action</Button>
</CardContent>
</Card>
);
}These are ordinary Material UI components. No Themvora-specific props are required — the theme reaches them through Material UI's own mechanism.
Theme switching
The point of an adapter is that this is the only code that changes — the components stay exactly as they are.
import { useTheme } from "@themvora/react";
import { Button, Stack } from "@mui/material";
export function ThemeControls() {
const { setMode, toggleTheme } = useTheme();
return (
<Stack direction="row" spacing={1}>
<Button onClick={toggleTheme}>Toggle</Button>
<Button onClick={() => setMode("light")}>Light</Button>
<Button onClick={() => setMode("dark")}>Dark</Button>
</Stack>
);
}Before and after
You own the theme object. You choose the colours, keep them in step with your design tokens by hand, write the dark variant yourself, and rebuild the object whenever anything changes. Every new token has to be added twice — once to your design system and once to Material UI.
Themvora owns the theme. The adapter derives the Material UItheme from Themvora's tokens and rebuilds it whenever the active theme changes — switching family or mode, or a persisted preference resolving on load. There is no second place to update, because there is no second theme.
Your components stay Material UI components. Themvora becomes the source of the active theme.
A complete example
Everything together for React. Paste it in and it runs.
import { ThemeProvider, useThemeRuntime } from "@themvora/react";
import { MuiThemeProvider } from "@themvora/mui";
import { Example } from "./Example";
// The bridge rebuilds Material UI's theme whenever Themvora's active theme changes.
function ThemeZone() {
const runtime = useThemeRuntime();
return (
<MuiThemeProvider runtime={runtime}>
<Example />
</MuiThemeProvider>
);
}
export default function App() {
return (
<ThemeProvider defaultTheme="plum-light" initialMode="system">
<ThemeZone />
</ThemeProvider>
);
}What gets mapped
Themvora owns the meaning; the adapter owns the vocabulary.
| Themvora | Material UI |
|---|---|
| primary | palette.primary.main |
| primaryForeground | palette.primary.contrastText |
| secondary | palette.secondary.main |
| background | palette.background.default |
| card | palette.background.paper |
| foreground | palette.text.primary |
| mutedForeground | palette.text.secondary |
| destructive | palette.error.main |
| success | palette.success.main |
| border | palette.divider |
| radius | shape.borderRadius |
| typography.fontFamilies.sans | typography.fontFamily |
What's Available
@themvora/mui ships 8 exports in 3 categories. Click any export to reveal what it does and how to use it.
Provider
Create
Registry
Use Cases
The important features in practice — copy any of these straight into your app.
Build a static MUI theme
Generate a Theme at build time from concrete tokens.
import { createMuiTheme } from "@themvora/mui";
import { getBuiltInThemes, resolveTheme } from "@themvora/core";
// resolveTheme takes (themes, name) — not a { family, mode } object.
const themes = getBuiltInThemes();
const muiTheme = createMuiTheme(resolveTheme(themes, "plum-dark"));More Examples
Static themes generated from concrete tokens, and runtime subscriptions.
import { createMuiTheme } from "@themvora/mui";
import { getBuiltInThemes } from "@themvora/core";
// createMuiTheme accepts a runtime, a store, a theme definition, or raw
// tokens. A `{ family, mode }` object is none of those — the factory then
// falls back to MUI's stock palette instead of the theme you asked for.
const themes = getBuiltInThemes();
const theme = themes.find((t) => t.name === "mint-light")!;
const muiTheme = createMuiTheme(theme);API Reference
Provider
| Export | Description |
|---|---|
MuiThemeProvider | Wraps MUI's own ThemeProvider with a theme derived from Themvora semantic tokens (palette, shape, typography, shadows, breakpoints). |
useMuiTheme(runtime) | Subscribes to a Themvora runtime and returns a Theme rebuilt whenever the theme changes. |
Create
| Export | Description |
|---|---|
createMuiTheme(source) | Builds a MUI Theme from a theme definition, its tokens, or a runtime. |
buildMuiThemeOptions(theme) | Maps an AdapterResolvedTheme to MUI ThemeOptions — colors, spacing, shape, shadows, breakpoints. |
Registry
| Export | Description |
|---|---|
createMuiAdapter(options) | Framework-neutral ThemeAdapter you register via runtime.adapters.use() — alternative to the provider, with getSnapshot/subscribe for use outside React. |
MUI_ADAPTER_ID | The registry id ("mui") used to identify and replace the adapter. |
CreateMuiAdapterOptions | Options for the registry adapter — including strategy for how faithfully tokens map onto MUI's theme. |
MuiAdapterOptions | Base adapter options type (strategy + defaults) used by both the provider and the registry adapter. |