Skip to main content

At most one component library family

ab-000222 · ai-slop-code-drift.ui-stack-drift.dual-component-library
Severity: mediumactive

Why it matters

Running MUI alongside Chakra ships two theme providers, two sets of primitives, two typography scales, and often two CSS-in-JS runtimes to every page. Bundle size roughly doubles for the styled-component portion of your app, Core Web Vitals degrade on mobile, and designers cannot produce a coherent visual system because every component comes from a different opinionated foundation. Dark mode toggles typically work in one library and break in the other.

Severity rationale

Medium because the impact is bundle bloat and visual drift rather than correctness or data loss, though both affect production quality.

Remediation

Audit usage with grep -rl "from '@mui/material'" src/ | wc -l and the equivalent for the other library, then port the smaller side. Remove the loser's peer dependencies in one step: npm uninstall @chakra-ui/react @emotion/react @emotion/styled. Delete the provider wrapper from src/app/layout.tsx or src/providers/index.tsx and verify theme tokens now flow from a single source. Run a production build and confirm the removed library is no longer in the bundle analyzer output.

Detection

  • ID: ai-slop-code-drift.ui-stack-drift.dual-component-library

  • Severity: medium

  • What to look for: Apply the three-condition rule against this exact component library allowlist: @mui/material, @chakra-ui/react, @chakra-ui/core, @mantine/core, antd, react-bootstrap, @blueprintjs/core, grommet, evergreen-ui, @nextui-org/react, @nextui-org/system. EXCLUDE shadcn-ui (it is headless + Radix primitives, not a competing component library — its presence is signaled by components.json in the project root and individual Radix imports). EXCLUDE Radix UI primitives (@radix-ui/*) — they coexist with shadcn-ui by design. Count all package names found.

  • Detector snippet (shell-capable tools only): If the tool has shell access, list every source file importing one of the allowlisted component library families. Group by library. If rg is not installed or exits >=2, fall back to prose reasoning above.

    rg -l -e "from ['\"]@mui/" -e "from ['\"]@chakra" -e "from ['\"]@mantine/" -e "from ['\"]antd" -e "from ['\"]@ant-design/" src/
    
  • Pass criteria: 0 or 1 styled component libraries from the allowlist actively used. Report even on pass: "Canonical component library: [name] ([N] importing files) — or shadcn-ui (Radix-based, headless)."

  • Fail criteria: 2 or more styled component libraries from the allowlist meet all three conditions (at least 1 non-escape-hatch importing file).

  • Skip (N/A) when: 0 styled component libraries from the allowlist appear in RUNTIME_DEPS.

  • Detail on fail: "2 active component libraries: '@mui/material' (15 files) AND '@chakra-ui/react' (9 files). Mixing component libraries creates inconsistent visual language and bundle bloat."

  • Remediation: Two component libraries means double the bundle, double the theme system, double the design language, and zero visual consistency. Pick one and migrate:

    # Inventory the smaller side
    grep -rl "from '@mui/material'" src/ | wc -l
    grep -rl "from '@chakra-ui/react'" src/ | wc -l
    
    # Migrate the smaller side to the larger one, then:
    npm uninstall @chakra-ui/react @emotion/react @emotion/styled
    

Taxons

History