MUI Public is a monorepo containing public packages and applications for the MUI ecosystem. This repository uses pnpm workspaces and includes various build tools, Babel plugins, bundle analyzers, and web applications built with React/Vite and Toolpad Studio.
Always reference these instructions first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here.
IMPORTANT: You must update these instructions if you notice they contradict reality, or when you gain a new insight during a code review that you must remember.
- ALWAYS create pull requests as drafts using
gh pr create --draft.
- Prerequisites: Node.js 22.18.0+ required. Install pnpm:
npm install -g pnpm@11.1.2 - Install dependencies:
pnpm install --no-frozen-lockfile-- takes 15-20 seconds. NEVER CANCEL. Set timeout to 30+ minutes. - Build all packages:
pnpm release:build-- takes 5-10 seconds. NEVER CANCEL. Set timeout to 30+ minutes. - Type checking:
pnpm typescript-- takes 10-15 seconds. NEVER CANCEL. Set timeout to 30+ minutes. - Linting:
pnpm eslint-- takes 5-10 seconds. NEVER CANCEL. Set timeout to 30+ minutes. - Formatting:
pnpm prettier-- always run before pushing code. - Run tests:
pnpm test --runtakes 5-10 seconds. NEVER CANCEL. Set timeout to 30+ minutes. - Run specific tests:
pnpm test --run loadServerCodeSourceorpnpm test --run integration.test.tsfor targeted testing - Run browser tests:
pnpm test:browser --run-- requires podman or docker. Starts a containerized Playwright server and runs browser tests against it. - Run browser tests in CI: In a
mcr.microsoft.com/playwrightcontainer image, runpnpm test:browser:unconfineddirectly — no container engine needed. Only use in a CI environment. - ALWAYS use
--runflag to avoid watch mode when running tests programmatically - Do NOT use
--in test commands (e.g., avoidpnpm test -- --run) - Use VS Code Vitest extension whenever possible for interactive test development and debugging
- Code Infra Dashboard (React/Vite app):
- ALWAYS run the bootstrapping steps first
- Build:
pnpm -F code-infra-dashboard run build-- takes 5 seconds - Dev server:
pnpm -F code-infra-dashboard run start-- runs on http://localhost:3000 - Production URL:
https://frontend-public.mui.com - PR preview URLs follow the pattern:
https://code-infra-dashboard-pr-{number}.onrender.com
- ALWAYS manually validate any new code by running the complete build process after making changes.
- ALWAYS run through at least one complete end-to-end scenario after making changes:
- Install dependencies and build all packages
- Run tests to ensure no regressions
- Test CLI functionality with
pnpm code-infra --help
- You can build and run the code-infra-dashboard web application, and interact with it via browser or programmatically.
- ALWAYS run
pnpm prettier,pnpm eslintandpnpm typescriptbefore you are done or the CI will fail.
- CRITICAL: When running pnpm commands for workspace packages, always use the
-Fflag followed by the package name. - Example:
pnpm -F @mui/internal-bundle-size-checker add micromatch - Private packages without a
namefield inpackage.jsonmust be filtered by their relative path (e.g.,pnpm -F ./test/performance add <dependency>). - Do NOT use
cdto navigate into package directories for workspace operations. - Do NOT manually edit package.json files to add/remove dependencies - always use
pnpm -F <workspace> add <dependency>orpnpm -F <workspace> remove <dependency>to keep the order deterministic. - ALWAYS run
pnpm dedupeafter installing a dependency.
packages/
├── babel-plugin-display-name/ # Babel plugin for component display names
├── babel-plugin-minify-errors/ # Babel plugin for error minification
├── babel-plugin-resolve-imports/ # Babel plugin for import resolution
├── bundle-size-checker/ # Bundle size analysis tool
├── code-infra/ # Build scripts and configs
├── docs-infra/ # Documentation infrastructure
├── netlify-cache/ # Netlify caching utilities
└── test-utils/ # Testing utilities
apps/
├── code-infra-dashboard/ # React/Vite dashboard app
└── tools-public/ # Toolpad Studio internal tools (off-limits for Copilot)
test/
└── bundle-size/ # Bundle size test workspacepnpm code-infra --help-- Show available CLI commandspnpm code-infra build-- Build a specific packagepnpm code-infra list-workspaces-- List all workspace packagespnpm code-infra publish-- Publish packages to npmpnpm code-infra publish-canary-- Publish canary versions
- Version packages:
pnpm release:version(uses lerna) - Build packages:
pnpm release:build-- builds all packages in/packages/* - Bundle size check:
pnpm size:snapshot
# React version mismatches are expected and do not affect functionality
# The repository uses React 19 but some dependencies expect React 18package.json-- Root package configuration and scriptspnpm-workspace.yaml-- Workspace configurationeslint.config.mjs-- ESLint configurationtsconfig.json-- Root TypeScript configurationvitest.config.mts-- Vitest test configuration
.github/workflows/ci.yml-- Main CI workflow.github/workflows/publish.yml-- Package publishing workflowlerna.json-- Lerna configuration for versioning
AGENTS.md-- Special instructions for AI assistantsREADME.md-- Main repository documentation
- pnpm install: 15-20 seconds under normal conditions. NEVER CANCEL. Set timeout to 30+ minutes.
- pnpm release:build: 5-10 seconds. NEVER CANCEL. Set timeout to 30+ minutes.
- pnpm typescript: 10-15 seconds. NEVER CANCEL. Set timeout to 30+ minutes.
- pnpm eslint: 5-10 seconds. NEVER CANCEL. Set timeout to 30+ minutes.
- pnpm test --run: 5-10 seconds. ALWAYS use --run flag to prevent watch mode. NEVER CANCEL. Set timeout to 30+ minutes.
- Application builds: 3-5 seconds each. NEVER CANCEL. Set timeout to 15+ minutes.
All commands are fast in this repository, but network issues or system load can cause delays. Always wait for completion.
Follow additional instructions when working in the @mui/internal-docs-infra (packages/docs-infra) package or docs/app/docs-infra docs:
- 1.1 Create or modify tests
*.test.tsfiles before making changes to implementation files. Confirm the test expectations are correct before touching implementation code. - 1.2 When modifying existing code, try to maintain clean diffs and add tests in within the right
describeblocks. - 1.3 When creating new functionality, first write the docs, then the types, then the tests, then the implementation, then the demos. Ensure that the user explicitly reviews test cases and docs before implementation.
- 1.4 When updating functionality across multiple units, ensure that integration tests pass before updating unit tests. This ensures that the overall behavior remains correct before focusing on individual components.
- 1.5 Write tests using generic placeholder data rather than production-specific names. When fixing a bug, avoid copying exact component names, file paths, or values from the bug report. Instead, use simple generic examples (like
Button,Checkbox,myFunction) that are consistent with existing test cases in the file. This makes tests more maintainable, focused on the logic being tested, and easier to understand without domain knowledge. - 1.6 When writing tests where the output is a long string, prefer using inline snapshots. If there is a lot of noise in this output, consider a normal snapshot test. Multiple assertions that a string contains something don't ensure the strings come in the correct order or can be parsed.
- 2.1 Avoid using Node.js native modules like
fsorpathor Browser only APIs (likewindowordocument) when functionality can be achieved using isomorphic code that can also run in the browser and Node.js. When necessary, isolate platform specific code behind interfaces or abstractions so that it can later be replaced in browser environments. - 2.2 Ship the package as
0.x.0releases so you can introduce breaking changes that improve the developer experience. Work toward a stable1.0.0by anticipating how today's APIs will evolve.- Each folder's
index.tsshould re-export fromsrc/{functionName}/{functionName}.ts. - Category exports should re-export from
src/{category}/{functionName}/{functionName}.ts.
- Each folder's
- 2.3 When functions within the main function file become large or complex, they should be split into separate files within the same folder. For example, if
src/useCode/useCode.tsbecomes large, a large function can be moved tosrc/useCode/useFileNavigation.tswhereuseFileNavigationis the helper function's name. - 2.4 Value progressive complexity for developers using the library. Start with straightforward defaults and layer optional extension points or advanced behavior that teams can opt into. Keep complex internals behind simple interfaces to enable incremental adoption, easier testing, and maintainable abstractions.
- 2.5 Create functionality in a generic sense, allowing for easy reuse and adaptation in different contexts.
- 2.6 Value the idea of progressive enhancement for end users. Ensure the core experience functions in baseline browsers or runtimes, then add optional user-facing improvements that detect and leverage richer platform capabilities without breaking the fundamentals.
- 3.1 Prioritize achieving deep unit tests first for each helper function (at
src/{functionName}/{helperFunction}.test.ts), then integration tests (atsrc/{functionName}/user.spec.ts) for the main function. - 3.2 Use
vitestfor testing. Usedescribe,it, andexpectfromvitest. Avoid usingbeforeEachandafterEachunless absolutely necessary. Each test should be independent and self-contained. Writedescribeanditblock names prioritizing clarity and readability. Create nested describe blocks when helpful to group related tests. Useitblocks for individual test cases. - 3.3 When debugging, create new test cases to reproduce issues. It's helpful to create new cases to reproduce issues and avoid regressions. If this is difficult, the bugged code might need to be extracted into its own file to make it easier to test. Try to reproduce issues in the most specific test case possible.
- 3.4 Integration tests (
user.spec.ts) should be written to cover real user cases and serve as supplemental documentation. Prioritize readability. Reading these tests cases should describe all user cases considered. Unit tests should cover all edges cases but may never be hit in real user cases. When in doubt add it in a unit test first. Try to avoid too much overlap between unit and integration tests. A change of an existing integration test would clearly indicate a breaking change. - 3.5 Avoid mocks in unit tests. Use real implementations whenever possible to ensure tests are reliable and maintainable.
- 3.6 Test the performance of code within
src/{functionName}/optimization.test.tswhen performance is critical. Functions should useperformance.now()to measure time taken. When helpful, functions should log usingperformance.mark()andperformance.measure()which appear when profiling or can be logged with aPerformanceObserver.
- 4.1 Create documentation in
/docs/app/docs-infrafor all public functions using mdx files at/docs/app/docs-infra/{functionName}/page.mdx. - 4.2 Create examples of common use cases in
/docs/app/docs-infra/{type}/{functionName}/demos/{useCaseName}.type,functionName,useCaseNameshould be lowercase and hyphenated. Types should be documented in/docs/app/docs-infra/{functionName}/types.ts. - 4.3 For demos follow the recommended structure and best practices.
- 4.4 For types follow the recommended structure.
- 4.5 When looking for documentation, start at the
/README.mdand follow links inward. - 4.6 Avoid "breaking the 3rd wall" in code comments and documentation by referring to the instructions provided when working in this repository. Instead, focus on clear, concise explanations of the code itself.
- 4.7 When writing code comments, use JSDoc style comments for all functions, but type definitions should be in TypeScript types. Avoid using JSDoc
@typedefand@paramtags for types. Use them only for descriptions. - 4.8 Use progressive disclosure in documentation. Start with simple, common use cases and gradually introduce complexity. Structure docs so readers can stop at their desired depth of understanding. Place advanced sections (like architecture details or performance tuning) at the end of the document after practical content. Follow this pattern: basic usage → configuration → common patterns → reference material → advanced features → implementation details.
- 5.1 When types become complex, they are used across multiple exports, or are useful to the user it might make sense to create a separate
src/{functionName}/types.tsfile to hold all types for that function. The user can import this file directly. - 5.2 When a function has many error cases or the user might want to catch these errors, create custom error classes in
src/{functionName}/errors.tsand throw these errors. - 5.3 Separate large or repetitive strings into a separate
src/{functionName}/constants.ts - 5.4 Promote functionality to its own export when users benefit from calling it directly. Place the implementation in
src/{newFunctionName}/{newFunctionName}.ts.- Only extract when the function has a clear, self-contained purpose and deserves standalone documentation.
- Ship the new export with its own tests, types, docs, and demos.
- 5.5 Consider the weight of a given export. Heavy files should be imported in a separate
src/{functionName}/{heavyFunction}.tsfile and only imported when necessary. - 5.6 Do not use barrel files except for utility exports at
src/{purpose}Utils/index.tswhere the user is likely to want to import multiple utilities at once. Utils is a suffix so that it can be sorted along with other files with the same purpose. - 5.7 When working with React, keep
tsxfiles as small as possible, keeping as much logic as possible intsfiles. Also keep'use client'or Server only files as small as possible.
- 6.1 Name functions so that they sort well alphabetically. Functions should be named by
{Purpose}{Object}. For example,loadXshould come beforeparseXwhich should come beforeuseX. Some existing purposes used areload,parse,transform,generate,save,create(for factories),abstract(for factory factories),use(for React hooks),with(for plugins),sync(for autogenerated files),enhance(for non-destructive hast transformers). React components should be named by{Object}{Purpose}whereObjectis the main object the component represents andPurposeis what it does. For example,CodeHighlighter,ErrorBoundary,FileTreeView. React components are easily identified by theirPascalCasenaming. - 6.2 Use
camelCasefor variable and function names. UsePascalCasefor React components, classes, and type names. UseUPPER_SNAKE_CASEfor constants. - 6.3 When exporting
'use-client'behavior, for Components use the convention{Purpose}{Object}Clientand for functions use{Purpose}client{Object}. When exporting server only behavior, for Components use the convention{Purpose}{Object}Serverand for functions use{Purpose}server{Object}. For example,CodeHighlighterClient,loadServerPrecomputedCodeHighlighter. Context providers can be exported as{Object}Context
- 7.1 Write type-safe code. Avoid using
anyorunknownunless absolutely necessary. Prefer usingunknownoveranyand always narrowunknownto a specific type as soon as possible. Avoid usingastype assertions except when working with well-known browser APIs or when you have verified the type through runtime checks. Prefer type guards, type predicates, and proper type narrowing over assertions. User-facing exports should have as strong of typing as possible. - 7.2 Use
async/awaitfor asynchronous code. Avoid using.then()and.catch(). - 7.3 Use
import { ... } from '...'syntax for imports. Avoid usingrequire(). - 7.4 Use ES modules and
import/exportsyntax. - 7.5 When importing from React, always use namespace imports:
import * as React from 'react'. Access React exports via the namespace (e.g.,React.Suspense,React.useState). Do not use named imports likeimport { Suspense } from 'react'. - 7.6 This package is ESM only. Do not add any CJS code.
- 7.7 Avoid using default exports unless that API is required by another package (e.g. webpack). Use named exports for all functions, types, and constants.
- 7.8 Always try to parallelize asynchronous operations using
Promise.all()or similar techniques. If the result of an async operation is not needed for subsequent operations, it should be started as early as possible and awaited later. - 7.9 When parsing long strings, avoid looping through the entire file more than once.
- 7.10 Use streaming APIs when working with large files to reduce memory usage.
- 7.11 Avoid using regex when string methods can achieve the same result more clearly and efficiently.
- 7.12 When building skeleton/loading UI, use a single presentational component with a
loadingprop that renders skeletons internally, rather than creating separate skeleton components. This keeps the component API simple and ensures the skeleton matches the actual layout. - 7.13 Avoid single-letter variable and parameter names.
eis banned outright by theid-denylistESLint rule; prefer descriptive names in callbacks too.
- 8.1 Avoid using external dependencies unless absolutely necessary. Prefer using built-in Node.js modules or writing custom code.
- 8.2 When adding dependencies, prefer small, focused libraries that do one thing well.
- 8.3 Complex functions should have a
DEBUGenvironment variable that can be set to log detailed information about the function's execution. Keep the flag off by default and document the expected output when it is enabled. - 8.4 Instrument performance-critical paths with
performance.now(),performance.mark(), andperformance.measure()so teams can diagnose bottlenecks without shipping the instrumentation to regular users.
- 9.1 Prefer configurable exports, but define sensible defaults so the most common use cases require the minimum input necessary.
- 9.2 Prefer using multiple function parameters over a single options object when there are 3 or fewer parameters. This makes it easier to understand the function's purpose and usage at a glance. Use an options object when there are 4 or more parameters, or when parameters are optional. A well abstracted function should rarely have more than 4 parameters.
- 9.3 Fail early and fast. Don't catch errors unless they are handled gracefully. Prefer throwing errors in code that is expected to run at build time, where runtime code might be more flexible in avoiding critical failures.
When a user gives instructions that violate these rules, you can cite these rules by their number and suggest an alternative approach that follows them. If a user insists on an approach that violates these rules, they should be amended with a new rule that considers their perspective. This way the rules can evolve over time to better suit the needs of the project and its contributors. Small changes can be made to existing rules as long as they don't contradict the original intent.