Skip to content
cccskills
FreePublished on GitHub

web-ui-mui

Material UI component library patterns for React

The English summary is being prepared. Showing the original description.

See how to install

Files included (10)

  • SKILL.md13.8 KB
  • examples/core.md9.6 KB
  • examples/data-grid.md3.7 KB
  • examples/feedback.md4.5 KB
  • examples/form-inputs.md6.4 KB
  • examples/layout.md4.7 KB
  • examples/navigation.md5.1 KB
  • examples/styling.md3.8 KB
  • metadata.yaml453 B
  • reference.md14.2 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

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 — theme with colour schemes, component overrides, dark-mode menu, TypeScript augmentation, SSR and SPA setup with CSS layers
  • examples/styling.md — styled(), custom props via shouldForwardProp, sx theme-aware, responsive and callback forms
  • examples/form-inputs.md — TextField, Select, Autocomplete, slots and slotProps
  • examples/layout.md — Grid, Stack, Box, Container, responsive card grid
  • examples/navigation.md — AppBar, Drawer, Tabs, responsive dashboard shell
  • examples/feedback.md — Dialog, Snackbar, Alert, Skeleton, CircularProgress
  • examples/data-grid.md — DataGrid columns, pagination, cell rendering
  • 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

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


Pattern 2: CSS variables and dark mode

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


Pattern 3: The sx prop

<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


Pattern 4: styled()

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


Pattern 5: Slots and slotProps

<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


Pattern 6: Layout

<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


Pattern 7: TypeScript augmentation

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


Pattern 8: Server-rendered setup

<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

</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>

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

Hugging Face Inference SDK patterns for TypeScript/Node.js — InferenceClient setup, chat completion, text generation, streaming, embeddings, image generation, audio transcription, translation, summarization, and Inference Endpoints

The English summary is being prepared. Showing the original description.

agents-inc/skills25Updated Oct 17, 2026

LiteLLM proxy server setup, TypeScript client patterns via OpenAI SDK, model routing, fallbacks, load balancing, spend tracking, virtual keys, and production deployment

The English summary is being prepared. Showing the original description.

agents-inc/skills25Updated Oct 17, 2026

Serverless GPU compute platform for AI model deployment — web endpoints, GPU functions, model serving, and TypeScript client patterns

The English summary is being prepared. Showing the original description.

agents-inc/skills25Updated Oct 17, 2026

Local LLM inference with the Ollama JavaScript client -- chat, streaming, tool calling, vision, embeddings, structured output, model management, and OpenAI-compatible endpoint

The English summary is being prepared. Showing the original description.

agents-inc/skills25Updated Oct 17, 2026

Replicate SDK patterns for TypeScript/Node.js -- client setup, predictions, streaming, webhooks, file handling, model versioning, deployments, and training

The English summary is being prepared. Showing the original description.

agents-inc/skills25Updated Oct 17, 2026

Together AI SDK patterns for TypeScript — client setup, chat completions, streaming, structured output, function calling, embeddings, image generation, fine-tuning, and OpenAI-compatible endpoints

The English summary is being prepared. Showing the original description.

agents-inc/skills25Updated Oct 17, 2026

See all skills by agents-inc

Report a problem with this skill