ecosystem-integrations
MUI Ecosystem Integrations
Third-party libraries, schema-driven form builders, styling interop, and animation integrations that extend MUI's capabilities.
Schema-Driven Form Builders
FormEngine with MUI
JSON config renders live MUI forms with validation and event wiring.
npm install @react-form-builder/core @react-form-builder/components-material-ui
import { FormViewer } from '@react-form-builder/core';
import { view as muiView } from '@react-form-builder/components-material-ui';
const contactForm = {
tooltipType: 'MuiTooltip',
errorType: 'MuiErrorWrapper',
form: {
key: 'Screen',
type: 'Screen',
children: [
{
key: 'name',
type: 'MuiTextField',
props: { label: { value: 'Full Name' } },
schema: { validations: [{ key: 'required' }] },
},
{
key: 'email',
type: 'MuiTextField',
props: { label: { value: 'Email' } },
schema: { validations: [{ key: 'required' }, { key: 'email' }] },
},
{
key: 'role',
type: 'MuiSelect',
props: {
label: { value: 'Role' },
options: { value: [
{ value: 'admin', label: 'Admin' },
{ value: 'user', label: 'User' },
]},
},
},
{
key: 'submit',
type: 'MuiButton',
props: {
children: { value: 'Submit' },
variant: { value: 'contained' },
},
events: {
onClick: [
{ name: 'validate', type: 'common', args: { failOnError: true } },
{ name: 'onSubmit', type: 'custom' },
],
},
},
],
},
};
function DynamicForm() {
return (
<FormViewer
view={muiView}
getForm={() => JSON.stringify(contactForm)}
actions={{
onSubmit: (e) => console.log('Form data:', e.data),
}}
/>
);
}
MUI Components Pack maps JSON types to real MUI components:
MuiTextField, MuiSelect, MuiCheckbox, MuiSwitch, MuiButton,
MuiDialog, MuiCard, MuiAutocomplete, MuiDatePicker, etc.
Conditional Rendering with renderWhen:
{
"key": "discountCode",
"type": "MuiTextField",
"props": { "label": { "value": "Discount Code" } },
"renderWhen": { "value": "form.data.hasDiscount === true" }
}
react-jsonschema-form with MUI
JSON Schema → MUI form rendering.
npm install @rjsf/core @rjsf/mui @rjsf/validator-ajv8
import Form from '@rjsf/mui';
import validator from '@rjsf/validator-ajv8';
const schema = {
type: 'object',
required: ['name', 'email'],
properties: {
name: { type: 'string', title: 'Full Name' },
email: { type: 'string', title: 'Email', format: 'email' },
age: { type: 'integer', title: 'Age', minimum: 18 },
role: {
type: 'string',
title: 'Role',
enum: ['admin', 'editor', 'viewer'],
enumNames: ['Administrator', 'Editor', 'Viewer'],
},
bio: { type: 'string', title: 'Biography' },
},
};
const uiSchema = {
bio: { 'ui:widget': 'textarea', 'ui:options': { rows: 4 } },
role: { 'ui:widget': 'select' },
'ui:order': ['name', 'email', 'age', 'role', 'bio'],
};
function JsonSchemaForm() {
return (
<Form
schema={schema}
uiSchema={uiSchema}
validator={validator}
onSubmit={({ formData }) => console.log(formData)}
/>
);
}
Custom Widgets and Templates:
const widgets = {
DateWidget: (props) => (
<DatePicker value={dayjs(props.value)} onChange={(d) => props.onChange(d?.toISOString())} />
),
};
const templates = {
ObjectFieldTemplate: (props) => (
<Grid container spacing={2}>
{props.properties.map((prop) => (
<Grid key={prop.name} size={{ xs: 12, md: 6 }}>
{prop.content}
</Grid>
))}
</Grid>
),
};
<Form schema={schema} widgets={widgets} templates={templates} validator={validator} />
Uniforms with MUI Bridge
Multi-schema form engine with pluggable styling bridges.
npm install uniforms uniforms-bridge-json-schema uniforms-mui
import { AutoForm, AutoField, SubmitField } from 'uniforms-mui';
import { JSONSchemaBridge } from 'uniforms-bridge-json-schema';
import Ajv from 'ajv';
const ajv = new Ajv({ allErrors: true });
const schema = {
type: 'object',
properties: {
name: { type: 'string' },
email: { type: 'string', format: 'email' },
},
required: ['name', 'email'],
};
const bridge = new JSONSchemaBridge({ schema, validator: ajv.compile(schema) });
<AutoForm schema={bridge} onSubmit={(data) => save(data)}>
<AutoField name="name" />
<AutoField name="email" />
<SubmitField />
</AutoForm>
When to Use What
| Tool | Best For |
|---|---|
| FormEngine + MUI | Admin builders, dynamic forms from metadata, runtime JSON config |
| react-jsonschema-form + MUI | JSON Schema-driven forms, API-defined schemas |
| Uniforms + MUI | Multi-schema support, rapid prototyping |
| React Hook Form + MUI | Hand-coded forms with type-safe validation (Zod) |
| Formik + MUI | Legacy projects already using Formik |
Tailwind CSS + MUI Interop
Using MUI with Tailwind
MUI and Tailwind can coexist. Key: Tailwind's preflight conflicts with MUI's CssBaseline.
Setup (tailwind.config.ts):
export default {
// Important: disable preflight to avoid conflicts with CssBaseline
corePlugins: {
preflight: false,
},
// Scope Tailwind to avoid class conflicts
important: '#root', // or use selector strategy
content: ['./src/**/*.{ts,tsx}'],
};
Emotion + Tailwind ordering — ensure Emotion styles take precedence:
import createCache from '@emotion/cache';
import { CacheProvider } from '@emotion/react';
const cache = createCache({
key: 'css',
prepend: true, // Emotion styles inserted BEFORE Tailwind → Tailwind can override
});
<CacheProvider value={cache}>
<ThemeProvider theme={theme}>
<CssBaseline />
<App />
</ThemeProvider>
</CacheProvider>
Using Tailwind classes on MUI components:
// Tailwind classes work alongside sx prop
<Button className="rounded-full shadow-lg" sx={{ px: 4 }}>
Mixed styling
</Button>
// Use Tailwind for layout, MUI for component styles
<Box className="flex items-center gap-4 p-6">
<TextField label="Name" fullWidth />
<Button variant="contained">Save</Button>
</Box>
Base UI + Tailwind (Headless + Utility)
The cleanest integration: Base UI hooks for logic/a11y, Tailwind for all visuals.
import { useButton } from '@mui/base/useButton';
import clsx from 'clsx';
function TailwindButton({ children, variant = 'primary', ...props }) {
const { getRootProps, active, disabled, focusVisible } = useButton(props);
return (
<button
{...getRootProps()}
className={clsx(
'rounded-lg px-4 py-2 font-medium transition-all duration-150',
variant === 'primary' && 'bg-blue-600 text-white hover:bg-blue-700 active:bg-blue-800',
variant === 'secondary' && 'bg-slate-200 text-slate-900 hover:bg-slate-300',
disabled && 'opacity-50 cursor-not-allowed',
focusVisible && 'ring-2 ring-blue-400 ring-offset-2',
active && 'scale-95',
)}
>
{children}
</button>
);
}
Framer Motion + MUI
AnimatePresence with MUI Dialog
import { AnimatePresence, motion } from 'framer-motion';
import Dialog from '@mui/material/Dialog';
function AnimatedDialog({ open, onClose, children }) {
return (
<AnimatePresence>
{open && (
<Dialog
open={open}
onClose={onClose}
PaperComponent={motion.div}
PaperProps={{
initial: { opacity: 0, scale: 0.9, y: 20 },
animate: { opacity: 1, scale: 1, y: 0 },
exit: { opacity: 0, scale: 0.95, y: -10 },
transition: { type: 'spring', stiffness: 300, damping: 25 },
}}
>
{children}
</Dialog>
)}
</AnimatePresence>
);
}
Base UI + Tailwind + Framer Motion (Hardware Switch)
import { useSwitch } from '@mui/base/useSwitch';
import { motion } from 'framer-motion';
function HardwareSwitch({ label, ...props }) {
const { checked, disabled, focusVisible, getInputProps } = useSwitch(props);
return (
<label className="inline-flex items-center gap-3 cursor-pointer select-none">
{label && <span className="text-sm font-medium text-slate-200">{label}</span>}
<div className="relative">
<input {...getInputProps()} className="sr-only" />
{/* Track */}
<motion.div
className={`
relative flex h-9 w-16 items-center rounded-full px-1
border border-slate-700/80 bg-slate-900/90
shadow-[0_0_0_1px_rgba(15,23,42,0.9),0_8px_18px_rgba(0,0,0,0.85)]
${disabled ? 'cursor-not-allowed opacity-60' : 'cursor-pointer'}
`}
animate={{
boxShadow: checked
? '0 0 0 1px rgba(56,189,248,0.75), 0 16px 35px rgba(8,47,73,0.9)'
: '0 0 0 1px rgba(15,23,42,0.95), 0 10px 25px rgba(0,0,0,0.9)',
}}
whileTap={{ scale: 0.97 }}
transition={{ type: 'spring', stiffness: 500, damping: 32 }}
>
{/* LED indicator */}
<motion.div
className="pointer-events-none absolute left-1 top-1/2 h-1.5 w-1.5 -translate-y-1/2 rounded-full"
animate={{
backgroundColor: checked ? 'rgba(56,189,248,1)' : 'rgba(148,163,184,0.5)',
boxShadow: checked ? '0 0 10px rgba(56,189,248,0.9)' : '0 0 4px rgba(148,163,184,0.6)',
}}
/>
{/* Thumb */}
<motion.div
className="relative z-10 h-7 w-7 rounded-full bg-gradient-to-br from-slate-100 to-slate-400 shadow-lg flex items-center justify-center"
layout
animate={{ x: checked ? 26 : 0 }}
transition={{ type: 'spring', stiffness: 550, damping: 30, mass: 0.4 }}
>
<motion.div
className="h-4 w-4 rounded-full bg-slate-900/90 shadow-[inset_0_0_4px_rgba(0,0,0,0.75)]"
animate={{
boxShadow: checked
? 'inset 0 0 6px rgba(56,189,248,0.9)'
: 'inset 0 0 3px rgba(0,0,0,0.8)',
}}
/>
</motion.div>
{/* Focus ring */}
{focusVisible && (
<span className="pointer-events-none absolute -inset-1 rounded-full ring-2 ring-sky-400/80 ring-offset-2 ring-offset-slate-900" />
)}
</motion.div>
</div>
</label>
);
}
Animated Lists with MUI
import { AnimatePresence, motion } from 'framer-motion';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemText from '@mui/material/ListItemText';
function AnimatedList({ items }) {
return (
<List>
<AnimatePresence initial={false}>
{items.map((item) => (
<motion.div
key={item.id}
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
transition={{ type: 'spring', stiffness: 300, damping: 25 }}
>
<ListItem>
<ListItemText primary={item.name} secondary={item.email} />
</ListItem>
</motion.div>
))}
</AnimatePresence>
</List>
);
}
Custom Theme-Registered Components
Register your own component in the MUI theme so it responds to theme changes like a built-in.
// 1. Define the component
import { styled } from '@mui/material/styles';
import Box from '@mui/material/Box';
interface StatProps {
label: string;
value: string | number;
trend?: 'up' | 'down' | 'flat';
}
const StatRoot = styled(Box, {
name: 'MuiStat', // Register in theme as MuiStat
slot: 'Root',
})(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
gap: theme.spacing(0.5),
padding: theme.spacing(2, 3),
borderRadius: theme.shape.borderRadius * 2,
border: `1px solid ${theme.palette.divider}`,
}));
export function Stat({ label, value, trend }: StatProps) {
return (
<StatRoot>
<Typography variant="caption" color="text.secondary">{label}</Typography>
<Typography variant="h4">{value}</Typography>
{trend && <TrendIndicator trend={trend} />}
</StatRoot>
);
}
// 2. Register in theme
const theme = createTheme({
components: {
MuiStat: {
defaultProps: {},
styleOverrides: {
root: ({ theme }) => ({
backgroundColor: theme.palette.background.paper,
'&:hover': { borderColor: theme.palette.primary.main },
}),
},
variants: [
{
props: { variant: 'highlighted' },
style: ({ theme }) => ({
borderColor: theme.palette.primary.main,
backgroundColor: theme.palette.primary.light + '10',
}),
},
],
},
},
});
// 3. TypeScript augmentation
declare module '@mui/material/styles' {
interface Components {
MuiStat?: {
defaultProps?: Partial<StatProps>;
styleOverrides?: { root?: any };
variants?: any[];
};
}
}
Theme as Rules Engine
Encode design decisions in the theme and enforce at compile time:
// Restrict allowed Button variants in your design system
declare module '@mui/material/Button' {
interface ButtonPropsVariantOverrides {
contained: true;
outlined: true;
text: false; // ← disable 'text' variant at compile time
dashed: true; // ← add custom variant
gradient: true; // ← add custom variant
}
interface ButtonPropsColorOverrides {
inherit: false; // ← disable inherit color
brand: true; // ← add custom brand color
neutral: true; // ← add custom neutral color
}
interface ButtonPropsSizeOverrides {
extraLarge: true; // ← add custom size
}
}
Now <Button variant="text"> is a TypeScript error, enforcing design rules at compile time.
More from lobbi-docs/claude
vision-multimodal
Vision and multimodal capabilities for Claude including image analysis, PDF processing, and document understanding. Activate for image input, base64 encoding, multiple images, and visual analysis.
242design-system
Apply and manage the AI-powered design system with 50+ curated styles
126complex-reasoning
Multi-step reasoning patterns and frameworks for systematic problem solving. Activate for Chain-of-Thought, Tree-of-Thought, hypothesis-driven debugging, and structured analytical approaches that leverage extended thinking.
105gcp
Google Cloud Platform services including GKE, Cloud Run, Cloud Storage, BigQuery, and Pub/Sub. Activate for GCP infrastructure, Google Cloud deployment, and GCP integration.
73kanban
Kanban methodology including boards, WIP limits, flow metrics, and continuous delivery. Activate for Kanban boards, workflow visualization, and lean project management.
62debugging
Debugging techniques for Python, JavaScript, and distributed systems. Activate for troubleshooting, error analysis, log investigation, and performance debugging. Includes extended thinking integration for complex debugging scenarios.
59