Skip to content

Material UI

June Zhao edited this page Aug 22, 2026 · 2 revisions

Material UI

Material UI (MUI) is the primary UI component library for the Food Oasis client. We use it for layout, forms, buttons, dialogs, tables, date pickers, and theming.

This page is for new developers: what we use, where it lives in the repo, how to write UI with it, and how to upgrade safely.

What we use (current versions)

Package Role
@mui/material ^7 Core components (Button, Grid, TextField, Dialog, …)
@mui/icons-material ^7 Icon components
@mui/x-data-grid ^7 Admin verification / data-entry tables
@mui/x-date-pickers ^7 Time pickers (organization hours)
@emotion/react / @emotion/styled CSS-in-JS engine MUI depends on

Defined in client/package.json.

Not used anymore

  • @mui/lab — removed in the v7 migration. Prefer core components (e.g. Button with the loading prop instead of LoadingButton).
  • @mui/material/Unstable_Grid2 — Grid2 was folded into Grid in v7. Import Grid from @mui/material.

Related override

  • react-is is pinned to 18.3.1 via npm overrides because MUI v7 declares react-is ^19 while the app is still on React 18. Revisit this when upgrading to React 19.

Where things live

Path Purpose
client/src/theme/ Theme: palette, typography, breakpoints, component overrides
client/src/theme/index.tsx ThemeProvider wrapper used by the app
client/src/App.tsx Wraps the app in our ThemeProvider
client/src/components/MuiDemo/ Internal visual showcase of theme + components
client/src/components/Admin/ui/ Shared admin UI wrappers (Textarea, Label, dialogs, …)

Theme preview

  1. From client/: npm start
  2. Open http://localhost:3000/admin/muidemo

Useful for checking buttons, chips, typography, and palette changes without hunting through the app.

How to use MUI in this codebase

Imports

Prefer named imports from the package roots:

import { Box, Button, Grid, TextField, Typography } from "@mui/material";
import CheckIcon from "@mui/icons-material/Check";

Icons: import each icon from its own path (@mui/icons-material/Check) rather than barrel-importing the whole package.

Styling

Prefer MUI’s sx prop and theme tokens over one-off CSS files:

<Box
  sx={(theme) => ({
    color: theme.palette.headingText.main,
    p: 2,
  })}
>
  …
</Box>

Custom palette keys (e.g. headingText, foodPantry, confirmed) are declared in client/src/theme/palette.tsx with TypeScript module augmentation so they type-check.

Custom button variants such as gray and recenter are also declared there and styled in theme overrides.

Layout with Grid (important in v7)

In MUI v7, the old item / xs / sm API is gone. Use the size prop:

// ✅ v7
<Grid container spacing={2}>
  <Grid size={{ xs: 12, md: 6 }}>…</Grid>
  <Grid size={12}>…</Grid>
</Grid>

// ❌ v5 (do not use)
<Grid container>
  <Grid item xs={12} md={6}>…</Grid>
</Grid>

Gotcha: v5 Grid containers were implicitly width: 100%. v7 containers are only display: flex + minWidth: 0. A nested <Grid container> inside a flex parent can shrink to content width. If a nested row collapses on desktop, add sx={{ width: "100%" }} (or size={12}) to that container.

Do not use Grid as a generic flex wrapper when there is no grid layout. Prefer Box or Stack (see map filter chips in Advanced Filters).

Forms

  • Formik + Yup drive most forms.
  • Pass boolean errors to MUI: error={touched.field && Boolean(errors.field)}.
  • Shared multiline field: Admin/ui/Textarea.tsx (error?: boolean).

Data Grid and date pickers

  • Admin tables: @mui/x-data-grid (DataGrid, GridColDef, GridRowSelectionModel, …). See VerificationAdminGridMui.tsx.
  • Hours UI: @mui/x-date-pickers with LocalizationProvider + dayjs adapter. See OpenTimeForm.tsx.

Slots API

Prefer slotProps over deprecated componentsProps (still works in v7, but marked deprecated):

<Tooltip slotProps={{ tooltip: { sx: { fontSize: "0.95rem" } } }}>
  …
</Tooltip>

DOM nesting

Typography with variant="body1" defaults to a <p>. Do not put block elements (Alert, Box, Paper) inside it — React will warn about invalid nesting. Use component="div" or wrap the block in a sibling Box.

Common recipes

Responsive row

<Stack direction={{ xs: "column", sm: "row" }} spacing={2} alignItems="center">
  …
</Stack>

Theme-aware styles

sx={(theme) => ({
  borderColor: theme.palette.filterButtons.border.selected,
})}

Loading button (no @mui/lab)

<Button variant="contained" loading={isSubmitting} type="submit">
  Save
</Button>

Updating / upgrading MUI

  1. Read the migration guide for the target major (e.g. v6, v7).
  2. Bump related packages together in client/package.json:
    • @mui/material, @mui/icons-material
    • @mui/x-data-grid, @mui/x-date-pickers (X packages often have their own majors)
    • Keep @emotion/* compatible
  3. Run official codemods when available (Grid size props, etc.), then fix leftovers by hand.
  4. Reinstall and verify from client/:
npm install
npm run typecheck
npm run build
npm run format   # prettier — avoid noisy follow-up PRs after codemods
  1. Visual QA
    • Food seeker: /organizations (desktop + mobile list/map)
    • Admin: organization edit tabs, verification Data Grid, account forms
    • Theme demo: /admin/muidemo
  2. Watch for regressions
    • Nested Grid container width (see above)
    • Data Grid selection / valueGetter signature changes
    • Date picker error props must be booleans
    • react-draggable + mobile drawer: keep nodeRef on the draggable child; pin react-draggable carefully if upgrading

Prefer a dedicated upgrade PR (not mixed with feature work). After merge, update this wiki page’s version table if majors change.

Learning resources

Related wiki pages

Clone this wiki locally