Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsBlogPro
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Authors
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges
  • Chrome Extension
  • Skill Manager

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

ProTermsPrivacyRefunds
Back to skills

Web Ui Mui

ASecurity

Material UI component library patterns for React

24 stars
0 votes
0 copies
0 views
Added 5/29/2026
ai-agentstypescriptgoshellreactapiperformance

Works with

cliapi

Security Analysis

A100/100

Pro scans all 10 files and shows the line behind each finding

Scanned 9/21/2026

$npx -y skills add agents-inc/skills --skill web-ui-mui --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Web Ui Mui?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Web Ui Mui
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/agents-inc-web-ui-mui/badge)](https://www.skillsdirectory.com/skills/agents-inc-web-ui-mui)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
Files
SKILL.md
---
name: web-ui-mui
description: Material UI component library patterns for React
---

# MUI (Material UI) Patterns

> **Quick Guide:** MUI is a pre-styled React component library built on a theme. `createTheme` +
> `ThemeProvider` define the tokens every component reads; `sx` styles one instance, `styled()`
> makes a reusable one, `theme.components` changes every instance, and `slots` / `slotProps` reach
> inner elements. **Current: v7.x (March 2025)** — CSS layers, `slots`/`slotProps` standardised
> everywhere, Grid v2 promoted to `Grid`, React 19 compatible. MUI X v8 covers DataGrid, DatePicker
> and Charts. Emotion is the styling engine; Pigment CSS is still alpha and on hold.

**Detailed Resources:**

- [examples/core.md](examples/core.md) — theme with colour schemes, component overrides, dark-mode menu, TypeScript augmentation, SSR and SPA setup with CSS layers
- [examples/styling.md](examples/styling.md) — `styled()`, custom props via `shouldForwardProp`, `sx` theme-aware, responsive and callback forms
- [examples/form-inputs.md](examples/form-inputs.md) — TextField, Select, Autocomplete, slots and slotProps
- [examples/layout.md](examples/layout.md) — Grid, Stack, Box, Container, responsive card grid
- [examples/navigation.md](examples/navigation.md) — AppBar, Drawer, Tabs, responsive dashboard shell
- [examples/feedback.md](examples/feedback.md) — Dialog, Snackbar, Alert, Skeleton, CircularProgress
- [examples/data-grid.md](examples/data-grid.md) — DataGrid columns, pagination, cell rendering
- [reference.md](reference.md) — component tables, theme structure, `sx` shorthands, breakpoints, package list, v6 → v7 migration, anti-pattern code

---

<critical_requirements>

## Before writing MUI code

**Wrap the app in `ThemeProvider` with a `createTheme()` instance.** Without one, components fall
back to MUI's default theme and render correctly but wrong — no error, just someone else's design.

**Import from the path, not the barrel** — `@mui/material/Button`, not `{ Button } from
"@mui/material"`. The barrel makes the dev server parse the whole package on first load, and
`@mui/icons-material` alone is over 2000 modules.

**Use `slots` and `slotProps` to reach inner elements.** They are the v7 API across every component;
`components` and `componentsProps` are deprecated and scheduled for removal.

**Write dark-mode branches as `theme.applyStyles("dark", { … })`.** It emits both rulesets and lets
CSS pick, so the server and the client agree. `theme.palette.mode === "dark"` decides at render time,
which is what makes the wrong theme flash before hydration.

</critical_requirements>

---

**Auto-detection:** MUI, Material UI, @mui/material, @mui/system, @mui/icons-material,
@mui/x-data-grid, @mui/x-date-pickers, createTheme, ThemeProvider, CssBaseline, sx prop, styled,
useTheme, useColorScheme, colorSchemes, cssVariables, applyStyles, slots, slotProps,
shouldForwardProp, GridColDef, StyledEngineProvider, enableCssLayer

**Applies to:**

- Building on MUI's component set and customising it through the theme rather than by forking
- Choosing between `sx`, `styled()`, `theme.components` and `slots`/`slotProps` for a given override
- Colour schemes and dark mode, including the CSS-variables route that survives server rendering
- Typing custom palette colours and typography variants through module augmentation
- MUI X — DataGrid, date pickers, charts

**Handled elsewhere:**

- CSS methodology and design-token architecture beyond MUI's own theme — `enableCssLayer` puts MUI's
  styles in a named layer so an external stylesheet can order itself against them, and how that
  stylesheet is written is settled elsewhere
- Unstyled primitives — this library arrives fully styled, and a project that wants to own every
  pixel wants headless primitives instead
- Form state and validation — `TextField` takes `error` and `helperText` to render a failure, and
  `inputRef` to hand the underlying `<input>` to a library that registers by ref; what decides those
  values is not this skill's concern
- Server state — components take data as props

---

<philosophy>

**The theme is the product.** Palette, typography, spacing, shadows, breakpoints, z-index and
transitions are all one object, and every component reads from it. An override written at a call
site is a decision that will need repeating; the same decision in `theme.components` is made once.

**Customisation is layered, and the layer is the choice.** `sx` for this instance, `styled()` for a
reusable variant, `theme.components` for every instance, `slots`/`slotProps` for elements inside a
component you do not own. Reaching for a heavier layer than the situation needs is the usual source
of style that cannot be changed later.

**MUI components are client components.** They use context and effects, so a server-rendered app
needs a cache provider above `ThemeProvider` and a client boundary around the pages that use them.

</philosophy>

---

<decision_framework>

## Which styling layer

```
Used in one place                  → sx prop
Reused, same shape each time       → styled()
Every instance in the app          → theme.components.MuiX
An element inside the component    → slots / slotProps
```

## Which layout component

```
One axis, even spacing             → Stack
Twelve-column grid                 → Grid (size prop)
Centred page with a max width      → Container
Anything else that needs sx        → Box
```

## Colour scheme

```
No dark mode                       → one palette in createTheme
Follow the system only             → cssVariables: true
User can choose                    → colorSchemes + useColorScheme
Both, without a flash              → cssVariables: { colorSchemeSelector: "data" } + useColorScheme
```

## MUI X data display

```
Under ~100 rows, read-only         → Table
Sorting, filtering, pagination     → DataGrid
Very large datasets                → DataGridPro (row virtualisation)
Date or time entry                 → DatePicker / DateTimePicker
Charts                             → MUI X Charts
Hierarchy                          → TreeView
```

</decision_framework>

---

<patterns>

## Core patterns

### Pattern 1: Theme and provider

```tsx
const theme = createTheme({
  palette: { primary: { main: "#1976d2" } },
  typography: {
    fontFamily: '"Inter", sans-serif',
    button: { textTransform: "none" },
  },
  shape: { borderRadius: 8 },
  spacing: 8,
});

<ThemeProvider theme={theme}>
  <CssBaseline />
  {children}
</ThemeProvider>;
```

`CssBaseline` applies MUI's normalisation and the theme's background and text colours to `<body>` —
without it the page around the components keeps the browser defaults.

Full code: [examples/core.md](examples/core.md)

---

### Pattern 2: CSS variables and dark mode

```tsx
const theme = createTheme({
  cssVariables: { colorSchemeSelector: "data" },
  colorSchemes: {
    light: { palette: { primary: { main: "#1976d2" } } },
    dark: { palette: { primary: { main: "#90caf9" } } },
  },
});

const { mode, setMode } = useColorScheme(); // "light" | "dark" | "system"
```

With `cssVariables`, both palettes are emitted as custom properties and a `data-*` attribute selects
between them, so the scheme is settled before React runs. `mode` is `undefined` on the first render —
guard on it before rendering anything that depends on the scheme.

Full code: [examples/core.md](examples/core.md)

---

### Pattern 3: The `sx` prop

```tsx
<Box
  sx={{
    p: 3, // theme.spacing(3)
    bgcolor: "background.paper", // theme.palette.background.paper
    borderRadius: 1, // theme.shape.borderRadius
    boxShadow: 3, // theme.shadows[3]
    width: { xs: "100%", md: "50%" },
    "&:hover": { boxShadow: 6 },
  }}
/>
```

Numbers go through the theme's scales and dotted strings resolve against the palette, so a themed
value never needs to be repeated as a literal. Breakpoint objects replace media queries, and an
array of `sx` objects merges left to right — which is how a conditional style is added without
rebuilding the object.

Full code: [examples/styling.md](examples/styling.md)

---

### Pattern 4: `styled()`

```tsx
const StyledCard = styled(Card)(({ theme }) => ({
  padding: theme.spacing(3),
  "&:hover": { boxShadow: theme.shadows[8] },
  ...theme.applyStyles("dark", { backgroundColor: theme.palette.grey[900] }),
}));
```

Reach for this when the same styling appears more than once. A second argument takes
`shouldForwardProp`, which is what stops a styling-only prop reaching the DOM as an unknown
attribute.

Full code: [examples/styling.md](examples/styling.md)

---

### Pattern 5: Slots and slotProps

```tsx
<Autocomplete
  slots={{ paper: CustomPaper }}
  slotProps={{
    paper: { elevation: 8, sx: { borderRadius: 2 } },
    listbox: { sx: { maxHeight: 300 } },
    input: ({ open }) => ({
      sx: { borderColor: open ? "primary.main" : "divider" },
    }),
  }}
  renderInput={(params) => <TextField {...params} label="Framework" />}
/>
```

`slots` replaces an inner component, `slotProps` configures one, and a slotProp written as a callback
receives that slot's own state. Define slot components outside the render — an inline arrow is a new
component type each time, which remounts the slot on every render.

Full code: [examples/form-inputs.md](examples/form-inputs.md)

---

### Pattern 6: Layout

```tsx
<Grid container spacing={3}>
  <Grid size={{ xs: 12, md: 4 }}><Sidebar /></Grid>
  <Grid size={{ xs: 12, md: 8 }}><MainContent /></Grid>
</Grid>

<Stack direction={{ xs: "column", sm: "row" }} spacing={2}>
  <Button variant="contained">Save</Button>
</Stack>

<Container maxWidth="lg" sx={{ py: 4 }}>{children}</Container>
```

In v7 `Grid` is the former Grid2 and takes one `size` prop instead of separate `xs`/`sm`/`md` props;
the previous component is still available as `GridLegacy`.

Full code: [examples/layout.md](examples/layout.md)

---

### Pattern 7: TypeScript augmentation

```ts
declare module "@mui/material/styles" {
  interface Palette {
    neutral: Palette["primary"];
  }
  interface PaletteOptions {
    neutral?: PaletteOptions["primary"];
  }
}
declare module "@mui/material/Button" {
  interface ButtonPropsColorOverrides {
    neutral: true;
  }
}
```

Two augmentations, and both are needed: the first makes the token exist on the theme, the second
lets a component accept it as a prop value. Skip the second and `color="neutral"` is a type error at
every call site even though the theme carries it.

Full code: [examples/core.md](examples/core.md)

---

### Pattern 8: Server-rendered setup

```tsx
<AppRouterCacheProvider options={{ enableCssLayer: true }}>
  <GlobalStyles styles="@layer theme, base, mui, components, utilities;" />
  <ThemeProvider theme={theme}>
    <CssBaseline />
    {children}
  </ThemeProvider>
</AppRouterCacheProvider>
```

Emotion generates styles as components render, so a server-rendered app needs a cache provider above
`ThemeProvider` to collect and flush them with the streamed HTML — without it the markup arrives
unstyled and restyles on hydration. MUI publishes an adapter package per supported server framework;
a client-only app uses `StyledEngineProvider` in the same position instead. `enableCssLayer` puts
MUI's output into a named layer, and the `@layer` declaration is what fixes the order of that layer
against everything else on the page.

Full code, both arrangements: [examples/core.md](examples/core.md)

</patterns>

---

<performance>

## Performance

**Path imports.** `import Button from "@mui/material/Button"` skips barrel parsing; the barrel form
costs several times the dev-server startup. Some bundlers and frameworks rewrite barrel imports
automatically — check before assuming the cost applies. A lint rule restricting the pattern
`^@mui/[^/]+$` is what keeps it from creeping back.

**Stable references for anything a component treats as identity.** DataGrid `columns`, slot
components, and `Dialog` `TransitionProps` are all compared by reference: define them at module
scope, or memoise them. Inline, they cause a re-render or a remount on every parent render.

**`createTheme` does not affect bundle size** — it is data, not components. The bundle is decided by
which components are imported.

</performance>

---

<red_flags>

## Red flags

**Breaks at runtime:**

- `theme.palette.mode === "dark"` in a style function — resolved at render time, so the server and
  the first client paint disagree and the wrong theme flashes
- An inline arrow in `slots` — a new component type each render, so the slot remounts and loses its
  state and focus
- Inline `columns` on a DataGrid — a new array each render, re-rendering the whole grid
- Importing more than one level deep, `@mui/material/styles/createTheme` — the v7 ESM package layout
  does not expose those paths
- `@mui/lab` imports for Alert, Skeleton or Autocomplete — they graduated to `@mui/material` in v7
- MUI components rendered on the server without a client boundary — they need context and effects,
  and the boundary belongs on the page, not only on the root layout

**Surprising behaviour:**

- `spacing` is a multiplier, not pixels: `spacing(2)` is 16px at the default base of 8
- `sx` arrays merge left to right, so a later entry wins — which is what makes conditional styles work
- `TextField` is three components in a trench coat (input, label, helper text), so targeting the
  actual `<input>` means `slotProps.input`
- Setting `zIndex` by hand fights MUI's own scale, where modal, drawer, snackbar and tooltip already
  have assigned values
- `useMediaQuery` returns `false` during server rendering, so a desktop-first branch renders the
  wrong thing before hydration
- `useColorScheme().mode` is `undefined` on the first render, and rendering a scheme-dependent icon
  from it produces a hydration mismatch
- A missing TypeScript augmentation for a custom palette colour is a type error at the usage site,
  not at the theme
- `components` and `componentsProps` still work in v7 but are deprecated

</red_flags>

Attribution

agents-incagents-inc
View sourceSee grades on GitHubMore from agents-inc →
SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Related Skills

Caveman

Terse caveman voice: answer first, fluff gone, every technical fact kept. Use for /caveman, "caveman mode", "talk like caveman", "be brief", "less tokens". Stays on until "stop caveman" or "normal mode".

1100021 votes

Hyperplan

Adversarial multi-agent planning skill. Self-orchestrates 5 hostile category members (unspecified-low, unspecified-high, deep, ultrabrain, artistry) via team-mode for ruthless cross-critique debate, distills only the defensible insights, then MANDATORILY hands the distilled insight bundle to the `plan` agent for executable plan formalization. Use when planning needs maximum rigor and surfacing of weak assumptions, blind spots, and over-engineering. Triggers: 'hyperplan', 'hpp', '/hyperplan', ...

698621 votes

Writing Skills

Create and manage Claude Code skills in HASH repository following Anthropic best practices. Use when creating new skills, modifying skill-rules.json, understanding trigger patterns, working with hooks, debugging skill activation, or implementing progressive disclosure. Covers skill structure, YAML frontmatter, trigger types (keywords, intent patterns), UserPromptSubmit hook, and the 500-line rule. Includes validation and debugging with SKILL_DEBUG. Examples include rust-error-stack, cargo-dep...

3931 votes

Mcp Code Execution

Routes multi-tool workflows through MCP servers for large datasets and pipelines. Use when Bash tool overhead is limiting throughput on data-heavy tasks.

3421 votes

catchup

Recovers the conversation and failed tool calls of a previous Codex, Amp, Claude Code, Antigravity, Cline, Copilot CLI, Cursor, DeepSeek Harness, Grok Build, Kimi, OpenCode, Pi Agent, or ZCode session. Use when the user says "catch up", "what did the last session do", "get me up to speed", "I switched agents", asks to recover/summarize a previous session before continuing, or asks to diagnose or report a catchup failure. Do NOT use for the current conversation, git history, or any non-agent log.

741 votes
View all in ai-agents →