Hook API Styles aren't loading? - reactjs

The project is being built with Next.js and material-ui. I've used the Hook API method to style my components but the styles aren't always loading. When I run next dev to test it, my styles don't load. If I edit anything in the page currently being rendered, it reloads, but this time with the styles. If I click reload, however, the styles fail to load again. I've been searching for help with this but I can't find anything that seems to solve the issue. I saw a few things about using material-ui/core with material-ui/styles but it's all in reference to v3 and it doesn't solve the issue I have.
Here's one of the pages for example:
import Nav from '../components/nav';
import Footer from '../components/footer';
import Product from '../components/product';
import { makeStyles, ThemeProvider } from '#material-ui/core/styles';
import theme from '../components/customTheme';
import {
Typography,
Button
} from '#material-ui/core';
const useStyles = makeStyles({
body: {
margin: '15vh 0',
backgroundColor: ' white',
textAlign: 'left'
},
earnWrapper: {
display: 'block',
textAlign: 'left',
backgroundColor: 'white',
width: '80%',
margin: '5% 2vh',
padding: '5%',
borderRadius: '25px',
transition: '0.3s',
boxShadow: '0px 5px 20px #dedede'
},
earnContent: {
textAlign: 'left',
display: 'inline-block'
},
earntCaption: {
color: 'grey',
},
earntAmount: {
margin: '1vh 0 2vh'
},
shareInfo: {
margin: '5% 2vh',
textAlign: 'left'
},
products: {
textAlign: 'center ',
width: '100%'
}
});
export default function Home(props) {
const styles = useStyles();
return (
<ThemeProvider theme={theme}>
<DefaultHead
title="Oorigin | Home"
/>
<Nav isLoggedIn={true} />
<div className={styles.body}>
<div className={styles.earnWrapper}>
<div className={styles.earnContent}>
<Typography className={styles.earntCaption} variant="caption">You've earned</Typography>
<Typography className={styles.earntAmount} variant="h4">S$18.50</Typography>
<Button disableElevation variant="contained" color="primary">Withdraw</Button>
</div>
</div>
<div className={styles.shareInfo}>
<Typography><b>Shop, Share, Earn</b></Typography>
<Typography><br/>Shop products you like, share products you love, and earn up to 10% commission on every qualifying sale you refer</Typography>
</div>
<div className={styles.products}>
<Product
imgURL="../TestItem1.svg"
imgAlt="Test Product"
title="Disinfecting Air Purifying Solution"
price={(22.80).toFixed(2)}
link="/products/staticProduct"
/>
<Product
imgURL="../TestItem2.svg"
imgAlt="Test Product"
title="Disinfecting Air Purifying Solution"
price={(22.80).toFixed(2)}
/>
<Product
imgURL="../TestItem3.svg"
imgAlt="Test Product"
title="Disinfecting Air Purifying Solution"
price={(22.80).toFixed(2)}
/>
<Product
imgURL="../TestItem4.svg"
imgAlt="Test Product"
title="Disinfecting Air Purifying Solution"
price={(22.80).toFixed(2)}
/>
</div>
</div>
<Footer/>
</ThemeProvider>
);
}

Related

Material UI implementation in TypeScript

I am new to typeScript and I am using this materialUI premium theme OnePirate and I am using the Footer component and it is working perfectly fine in the js but when I am moving the same Footer component to my project and rename the component to .tsx It throws errors saying "No Overload matches this call"
Here is the code:
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import Grid from "#material-ui/core/Grid";
import Link from "#material-ui/core/Link";
import Container from "#material-ui/core/Container";
import Typography from "./materialUi/Typography";
import TextField from "./materialUi/TextField";
import fbicon from "./static/themes/onepirate/appFooterFacebook.png";
import twicon from "./static/themes/onepirate/appFooterTwitter.png";
const useStyles = makeStyles((theme) => ({
root: {
display: "flex",
backgroundColor: theme.palette.secondary.light,
},
container: {
marginTop: theme.spacing(8),
marginBottom: theme.spacing(8),
display: "flex",
},
iconsWrapper: {
height: 120,
},
icons: {
display: "flex",
},
icon: {
width: 48,
height: 48,
display: "flex",
justifyContent: "center",
alignItems: "center",
backgroundColor: theme.palette.warning.main,
marginRight: theme.spacing(1),
"&:hover": {
backgroundColor: theme.palette.warning.dark,
},
},
list: {
margin: 0,
listStyle: "none",
paddingLeft: 0,
},
listItem: {
paddingTop: theme.spacing(0.5),
paddingBottom: theme.spacing(0.5),
},
language: {
marginTop: theme.spacing(1),
width: 150,
},
}));
const LANGUAGES = [
{
code: "en-US",
name: "English",
},
{
code: "fr-FR",
name: "Français",
},
];
function Footer() {
const classes = useStyles();
return (
<Typography component="footer" className={classes.root}>
<Container className={classes.container}>
<Grid container spacing={5}>
<Grid item xs={6} sm={4} md={3}>
<Grid
container
direction="column"
justify="flex-end"
className={classes.iconsWrapper}
spacing={2}
>
<Grid item className={classes.icons}>
<a href="https://material-ui.com/" className={classes.icon}>
<img src={fbicon} alt="Facebook" />
</a>
<a
href="https://twitter.com/MaterialUI"
className={classes.icon}
>
<img src={twicon} alt="Twitter" />
</a>
</Grid>
<Grid item>© 2019 Onepirate</Grid>
</Grid>
</Grid>
<Grid item xs={6} sm={4} md={2}>
<Typography variant="h6" marked="left" gutterBottom>
Legal
</Typography>
<ul className={classes.list}>
<li className={classes.listItem}>
<Link href="/premium-themes/onepirate/terms/">Terms</Link>
</li>
<li className={classes.listItem}>
<Link href="/premium-themes/onepirate/privacy/">Privacy</Link>
</li>
</ul>
</Grid>
<Grid item xs={6} sm={8} md={4}>
<Typography variant="h6" marked="left" gutterBottom>
Language
</Typography>
<TextField
select
SelectProps={{
native: true,
}}
className={classes.language}
>
{LANGUAGES.map((language) => (
<option value={language.code} key={language.code}>
{language.name}
</option>
))}
</TextField>
</Grid>
<Grid item>
<Typography variant="caption">
{"Icons made by "}
<Link
href="https://www.freepik.com"
rel="nofollow"
title="Freepik"
>
Freepik
</Link>
{" from "}
<Link
href="https://www.flaticon.com"
rel="nofollow"
title="Flaticon"
>
www.flaticon.com
</Link>
{" is licensed by "}
<Link
href="https://creativecommons.org/licenses/by/3.0/"
title="Creative Commons BY 3.0"
target="_blank"
rel="noopener noreferrer"
>
CC 3.0 BY
</Link>
</Typography>
</Grid>
</Grid>
</Container>
</Typography>
);
}
export default Footer;
This is the Typography code if I don't import from material UI
import React from "react";
import PropTypes from "prop-types";
import { withStyles } from "#material-ui/core/styles";
import { capitalize } from "#material-ui/core/utils";
import MuiTypography from "#material-ui/core/Typography";
const styles = (theme) => ({
markedH2Center: {
height: 4,
width: 73,
display: "block",
margin: `${theme.spacing(1)}px auto 0`,
backgroundColor: theme.palette.secondary.main,
},
markedH3Center: {
height: 4,
width: 55,
display: "block",
margin: `${theme.spacing(1)}px auto 0`,
backgroundColor: theme.palette.secondary.main,
},
markedH4Center: {
height: 4,
width: 55,
display: "block",
margin: `${theme.spacing(1)}px auto 0`,
backgroundColor: theme.palette.secondary.main,
},
markedH6Left: {
height: 2,
width: 28,
display: "block",
marginTop: theme.spacing(0.5),
background: "currentColor",
},
});
const variantMapping = {
h1: "h1",
h2: "h1",
h3: "h1",
h4: "h1",
h5: "h3",
h6: "h2",
subtitle1: "h3",
};
function Typography(props) {
const { children, classes, marked = false, variant, ...other } = props;
return (
<MuiTypography variantMapping={variantMapping} variant={variant} {...other}>
{children}
{marked ? (
<span
className={
classes[`marked${capitalize(variant) + capitalize(marked)}`]
}
/>
) : null}
</MuiTypography>
);
}
Typography.propTypes = {
children: PropTypes.node,
classes: PropTypes.object.isRequired,
marked: PropTypes.oneOf([false, "center", "left"]),
variant: PropTypes.string,
};
export default withStyles(styles)(Typography);
This is the error now:
TL;DR: I've open sourced my implementation of onepirate in Typescript so you can just use that: https://github.com/rothbart/onepirate-typescript
This is a great question. Onepirate is a great resource but as you've noticed it isn't really built in a way that's Typescript friendly.
Your problem here is that the Typography implementation in onepirate isn't doing a great job of specifying which properties it expects, and is relying on the fact that vanilla JS will mostly just pass everything through to the underlying MuiTypography component.
Here's a much cleaner Typography implementation that preserves the underline effect in onepirate works with Typescript:
import React from "react";
import PropTypes, { InferProps } from "prop-types";
import {
withStyles,
WithStyles,
createStyles,
Theme,
} from "#material-ui/core/styles";
import MuiTypography, { TypographyProps } from "#material-ui/core/Typography";
const styles = (theme: Theme) =>
createStyles({
markedH2Center: {
height: 4,
width: 73,
display: "block",
margin: `${theme.spacing(1)}px auto 0`,
backgroundColor: theme.palette.secondary.main,
},
markedH3Center: {
height: 4,
width: 55,
display: "block",
margin: `${theme.spacing(1)}px auto 0`,
backgroundColor: theme.palette.secondary.main,
},
markedH4Center: {
height: 4,
width: 55,
display: "block",
margin: `${theme.spacing(1)}px auto 0`,
backgroundColor: theme.palette.secondary.main,
},
markedH6Left: {
height: 2,
width: 28,
display: "block",
marginTop: theme.spacing(0.5),
background: "currentColor",
},
});
function getMarkedClassName(variant: string) {
switch (variant) {
case "h2":
return "markedH2Center";
case "h3":
return "markedH3Center";
case "h4":
return "markedH4Center";
}
return "markedH6Left";
}
const variantMapping = {
h1: "h1",
h2: "h1",
h3: "h1",
h4: "h1",
h5: "h3",
h6: "h2",
subtitle1: "h3",
};
function Typography<C extends React.ElementType>(
props: TypographyProps<C, { component?: C }> &
WithStyles<typeof styles> &
InferProps<typeof Typography.propTypes>
) {
const { children, variant, classes, marked, ...other } = props;
return (
<MuiTypography variantMapping={variantMapping} variant={variant} {...other}>
{children}
{marked ? (
<span className={classes[getMarkedClassName(variant as string)]} />
) : null}
</MuiTypography>
);
}
Typography.propTypes = {
marked: PropTypes.oneOf([false, "center", "left"]),
};
Typography.defaultProps = {
marked: false,
};
export default withStyles(styles)(Typography);
Just a heads up that will run into this same problem with other onepirate components as well (I just finished re-implementing most of them in Typescript so I could use them in my own web app). If you're trying to reverse engineer how I did this I'd pay particular attention to how I took advantage of MuiTypography props and then just added the one new property I needed (marked).
TypographyProps<C, { component?: C }> &
WithStyles<typeof styles> &
InferProps<typeof Typography.propTypes>
I'd also highly recommend reading over https://material-ui.com/guides/typescript/ if you haven't already, it's a super helpful guide for understanding some of the Typescript gotchas in Material-UI.

React Create a Horizontal Divider with Text In between

I need to create a React component that is a Horizontal Divider with a content like text In between. All the resources I have online is unable to help me get this done. I tried a material-ui divider by creating a Divider component and placing my text in-between like the example below:
<Divider>Or</Divider>
But I get the error:
hr is a void element tag and must neither have `children` nor use `dangerouslySetInnerHTML`.
I need to achieve this in the image below:
Any help will be appreciated.
These are my codes below:
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import List from '#material-ui/core/List';
import Divider from '#material-ui/core/Divider';
const useStyles = makeStyles((theme) => ({
root: {
width: '100%',
maxWidth: 360,
backgroundColor: theme.palette.background.paper,
},
}));
export default function ListDividers() {
const classes = useStyles();
return (
<List component="nav" className={classes.root} aria-label="mailbox
folders">
<Divider>Or</Divider>
</List>
);
}
Using Material UI.
import React from "react";
import { makeStyles } from "#material-ui/core";
const useStyles = makeStyles(theme => ({
container: {
display: "flex",
alignItems: "center"
},
border: {
borderBottom: "2px solid lightgray",
width: "100%"
},
content: {
paddingTop: theme.spacing(0.5),
paddingBottom: theme.spacing(0.5),
paddingRight: theme.spacing(2),
paddingLeft: theme.spacing(2),
fontWeight: 500,
fontSize: 22,
color: "lightgray"
}
}));
const DividerWithText = ({ children }) => {
const classes = useStyles();
return (
<div className={classes.container}>
<div className={classes.border} />
<span className={classes.content}>{children}</span>
<div className={classes.border} />
</div>
);
};
export default DividerWithText;
You can import and use it like the below
<DividerWithText>Or</DividerWithText>
Result
Here a custom example of what you could do : repro on stackblitz
import React, { Component } from "react";
import { render } from "react-dom";
import Hello from "./Hello";
import "./style.css";
const App = () => {
return <Divider>Or</Divider>;
};
const Divider = ({ children }) => {
return (
<div className="container">
<div className="border" />
<span className="content">
{children}
</span>
<div className="border" />
</div>
);
};
render(<App />, document.getElementById("root"));
And the CSS:
.container{
display: flex;
align-items: center;
}
.border{
border-bottom: 1px solid black;
width: 100%;
}
.content {
padding: 0 10px 0 10px;
}
Update 29/03/2022
That's now possible with Material UI 🔥
https://mui.com/components/dividers/#dividers-with-text
You may want different spacing sometime
<Divider spacing={1}>Hello World</Divider>
Or
<Divider spacing={2}>Hello World</Divider>
For a configurable spacing here a Github Gist
Or a playground in codesandbox if you prefer
The current answer causes any text with spaces in-between to wrap:
If that happens, changing width: 100% to flexGrow: 1 should solve it:
border: {
borderBottom: "2px solid lightgray",
flexGrow: 1,
}
Unfortunately for now, having Divider with text on it in MUI is only available in v5, which is still in alpha stage. If you would like to try, you can download the alpha package, but be warned that it is still highly unstable and a lot of changes might be needed to migrate to v5, which is not very worth it.
GitHub discussion: https://github.com/mui-org/material-ui/issues/24036

React Admin Layout Component From Scratch

I want to implement a customized Layout for react-admin. I've tried to use their documentation 'https://marmelab.com/react-admin/Theming.html#using-a-custom-layout'
But it didn't work well ( for example we didn't have theme in our component). There is a broken link in the documentation that says you can use it for customizing :(.
I've implemented something right now that works fine right now but I don't know would work completely without problem/side-effects till the end of the project.
import * as React from 'react'
import { MuiThemeProvider, withStyles, createStyles} from '#material-ui/core/styles'
import appTheme from '../config/Theme'
import AppBar from './AppBar'
import Navbar from './Navbar'
const styles = (theme: any) => createStyles({
appFrame: {
display: 'flex',
flexDirection: 'column',
overflowX: 'auto',
},
content: {
display: 'flex',
flexDirection: 'column',
flexGrow: 2,
marginTop: '4em',
padding: theme.spacing.unit * 3,
paddingLeft: 5,
},
contentNoSidebar: {
display: 'flex',
flexGrow: 1,
},
root: {
backgroundColor: theme.palette.background.default,
display: 'flex',
flexDirection: 'column',
minHeight: '100vh',
position: 'relative',
zIndex: 1,
},
})
class CustomLayout extends React.Component<any, any> {
public render () {
const {
children,
classes,
logout,
open,
title,
} = this.props
return (
<MuiThemeProvider theme={appTheme}>
<div className={classes.root}>
<div className={classes.appFrame}>
<AppBar title={title} open={open} logout={logout} />
<Navbar/>
<main className={classes.contentNoSidebar}>
<div className={classes.content}>
{children}
</div>
</main>
</div>
</div>
</MuiThemeProvider>
)
}
}
export default withStyles(styles)(CustomLayout)
I appreciate if you could help me in this case.
I see nothing wrong with this code sample. This is indeed the way to go to customize the Layout. Do you have any issue with it ?
Can you open an issue on the react-admin repository about the documentation broken link ?

Creating themeable react component with JSS

I'm having some trouble following the docs on how to get this to work
I have a component that has some styles created, for the most part, all of these styles will apply to wherever this component is rendered but sometimes depending on what page the app is routed to I will want to change the overall height of the component on those pages. I figured I could just do this by passing in some new height props of some sort to follow a more theme centric approach. How would I get this to work with my current setup?
The styles file
const styles = theme => ({
root: {
backgroundSize: 'cover',
padding: '25px 20px',
boxSizing: 'border-box',
backgroundPosition: '50% 0',
backgroundColor: 'rgba(40,70,94,.7)',
backgroundBlendMode: 'multiply',
...theme.root
}
});
export default styles;
The component file
import React from 'react';
import styles from './EventTop.styles';
import injectSheet, { ThemeProvider } from 'react-jss';
const EventTop = (props) => (
<ThemeProvider theme={props.theme}>
<aside className={props.classes.root} style={{ backgroundImage: `url(${props.event.event_logo})` }}>
<div className="wrapper">
<div className="event-info">
<span className="event-time">
7:00 PM
</span>
<span className="event-date">
27 Jun 2020
</span>
<span className="event-end-time">
Ends at 10:00 PM
</span>
<span className="event-title">
Bidr Gala
</span>
<span className="event-attire">
Cocktail Attire
</span>
</div>
</div>
</aside>
</ThemeProvider>
);
export default injectSheet(styles)(EventTop);
And then when I render it I pictured something like this
<EventTop event={this.props.events.currentEvent} theme={{ height: 'calc(100vh - 64px)' }}/>
But when I try to console log out the theme getting passed to the style generator I don't see the theme coming in.
I have Material UI installed and the whole app is wrapped in it's MuiThemeProvider
<Provider store={store}>
<MuiThemeProvider theme={theme}>
<App>
<Reboot />
<AppRouter />
</App>
</MuiThemeProvider>
</Provider>
And this is the theme I'm actually seeing the material theme and not my own merged anywhere in it.
Here is how I got this to work
First I moved the stateless component into a new file
EventTop.component.jsx
import React from 'react';
import Moment from 'react-moment';
import styles from './EventTop.styles';
import themableWrapper from '../Themable';
const EventTopComponent = (props) => (
<aside className={props.classes.root} style={{ backgroundImage: `url(${props.event.event_logo})` }}>
<div className="wrapper">
<div className={props.classes.eventInfo}>
<span className={props.classes.eventTime}>
<Moment parse="HH:mm:ss" format="h:mm A">{props.event.start_time}</Moment>
</span>
<span className={props.classes.eventDate}>
<Moment parse="YYYY-MM-DD" format="D MMM YYYY">{props.event.event_date}</Moment>
</span>
<span className={props.classes.eventEndTime}>
Ends at <Moment parse="HH:mm:ss" format="h:mm A">{props.event.end_time}</Moment>
</span>
<span className={props.classes.eventTitle}>
{props.event.auction_title}
</span>
<span className={props.classes.eventAttire}>
{props.event.attire}
</span>
</div>
</div>
</aside>
);
export default themableWrapper(styles)(EventTopComponent);
Then in the EventTop.jsx I import the stateless component and return another stateless component but this one is wrapped in a new themable wrapper
import React from 'react';
import EventTopComponent from './EventTop.component';
import { Themable } from '../Themable';
const EventTop = ({ theme, ...rest }) => (
<Themable theme={theme}>
<EventTopComponent {...rest} />
</Themable>
);
export default EventTop;
Inside the new themable file, I have some exports setup
import injectSheet, { createTheming } from 'react-jss';
export const theming = createTheming('__NEW_THEME_NAMESPACE__');
export const { ThemeProvider: Themable } = theming;
export default (styles) => injectSheet(styles, { theming });
And then in the style generator, I can use something like lodash to merge my incoming theme with the default styles setup
EventTop.styles.js
import { merge } from 'lodash';
const styles = theme => {
return merge({
root: {
backgroundSize: 'cover',
padding: '25px 20px',
boxSizing: 'border-box',
backgroundPosition: '50% 0',
backgroundColor: 'rgba(40,70,94,.7)',
backgroundBlendMode: 'multiply',
},
eventInfo: {
flexGrow: '1',
display: 'flex',
flexDirection: 'column',
justifyContent: 'flex-end',
textAlign: 'left',
paddingTop: '20px',
fontWeight: '700',
color: 'white'
},
eventTime: {
fontSize: '55px'
},
eventDate: {
fontSize: '43px'
},
eventEndTime: {
fontSize: '30px'
},
eventTitle: {
fontSize: '70px'
},
eventAttire: {
fontSize: '30px'
}
}, theme);
};
export default styles;
In order for this to work with MuiThemeProvider wrapping the whole app I have to set up a new namespace, and then use the createTheme to make a new theme provider instance. This then needs to wrap the actual component code which is why I opted to create essentially a "container" component and the actual component as separate pieces so I could keep the logic separate. Plus this still allows me to make one stateless and the other not if I so choose
You are confusing MuiThemeProvider and the one from react-jss.

How to style MUI Tooltip?

How can I style MUI Tooltip text? The default tooltip on hover comes out black with no text-wrap. Is it possible to change the background, color etc? Is this option even available?
The other popular answer (by André Junges) on this question is for the 0.x versions of Material-UI. Below I've copied in my answer from Material UI's Tooltip - Customization Style which addresses this for v3 and v4. Further down, I have added a version of the example using v5.
Below are examples of how to override all tooltips via the theme, or to just customize a single tooltip using withStyles (two different examples). The second approach could also be used to create a custom tooltip component that you could reuse without forcing it to be used globally.
import React from "react";
import ReactDOM from "react-dom";
import {
createMuiTheme,
MuiThemeProvider,
withStyles
} from "#material-ui/core/styles";
import Tooltip from "#material-ui/core/Tooltip";
const defaultTheme = createMuiTheme();
const theme = createMuiTheme({
overrides: {
MuiTooltip: {
tooltip: {
fontSize: "2em",
color: "yellow",
backgroundColor: "red"
}
}
}
});
const BlueOnGreenTooltip = withStyles({
tooltip: {
color: "lightblue",
backgroundColor: "green"
}
})(Tooltip);
const TextOnlyTooltip = withStyles({
tooltip: {
color: "black",
backgroundColor: "transparent"
}
})(Tooltip);
function App(props) {
return (
<MuiThemeProvider theme={defaultTheme}>
<div className="App">
<MuiThemeProvider theme={theme}>
<Tooltip title="This tooltip is customized via overrides in the theme">
<div style={{ marginBottom: "20px" }}>
Hover to see tooltip customized via theme
</div>
</Tooltip>
</MuiThemeProvider>
<BlueOnGreenTooltip title="This tooltip is customized via withStyles">
<div style={{ marginBottom: "20px" }}>
Hover to see blue-on-green tooltip customized via withStyles
</div>
</BlueOnGreenTooltip>
<TextOnlyTooltip title="This tooltip is customized via withStyles">
<div>Hover to see text-only tooltip customized via withStyles</div>
</TextOnlyTooltip>
</div>
</MuiThemeProvider>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Here is documentation on tooltip CSS classes available to control different aspects of tooltip behavior: https://material-ui.com/api/tooltip/#css
Here is documentation on overriding these classes in the theme: https://material-ui.com/customization/components/#global-theme-override
Here is a similar example, but updated to work with v5 of Material-UI (pay attention that it works in 5.0.3 and upper versions after some fixes). It includes customization via the theme, customization using styled, and customization using the sx prop. All of these customizations target the "tooltip slot" so that the CSS is applied to the element that controls the visual look of the tooltip.
import React from "react";
import ReactDOM from "react-dom";
import { createTheme, ThemeProvider, styled } from "#mui/material/styles";
import Tooltip from "#mui/material/Tooltip";
const defaultTheme = createTheme();
const theme = createTheme({
components: {
MuiTooltip: {
styleOverrides: {
tooltip: {
fontSize: "2em",
color: "yellow",
backgroundColor: "red"
}
}
}
}
});
const BlueOnGreenTooltip = styled(({ className, ...props }) => (
<Tooltip {...props} componentsProps={{ tooltip: { className: className } }} />
))(`
color: lightblue;
background-color: green;
font-size: 1.5em;
`);
const TextOnlyTooltip = styled(({ className, ...props }) => (
<Tooltip {...props} componentsProps={{ tooltip: { className: className } }} />
))(`
color: black;
background-color: transparent;
`);
function App(props) {
return (
<ThemeProvider theme={defaultTheme}>
<div className="App">
<ThemeProvider theme={theme}>
<Tooltip title="This tooltip is customized via overrides in the theme">
<div style={{ marginBottom: "20px" }}>
Hover to see tooltip customized via theme
</div>
</Tooltip>
</ThemeProvider>
<BlueOnGreenTooltip title="This tooltip is customized via styled">
<div style={{ marginBottom: "20px" }}>
Hover to see blue-on-green tooltip customized via styled
</div>
</BlueOnGreenTooltip>
<TextOnlyTooltip title="This tooltip is customized via styled">
<div style={{ marginBottom: "20px" }}>
Hover to see text-only tooltip customized via styled
</div>
</TextOnlyTooltip>
<Tooltip
title="This tooltip is customized via the sx prop"
componentsProps={{
tooltip: {
sx: {
color: "purple",
backgroundColor: "lightblue",
fontSize: "2em"
}
}
}}
>
<div>
Hover to see purple-on-blue tooltip customized via the sx prop
</div>
</Tooltip>
</div>
</ThemeProvider>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Documentation on changes to the theme structure between v4 and v5: https://mui.com/guides/migration-v4/#theme
Tooltip customization examples in the Material-UI documentation: https://mui.com/components/tooltips/#customization
MUI v5 Update
You can customize the Tooltip by overriding the styles in the tooltip slot. There are 3 ways to do that in v5. For reference, see the customization section of Tooltip. More examples of sx prop and createTheme can be seen here and here.
styled()
const ToBeStyledTooltip = ({ className, ...props }) => (
<Tooltip {...props} classes={{ tooltip: className }} />
);
const StyledTooltip = styled(ToBeStyledTooltip)(({ theme }) => ({
backgroundColor: '#f5f5f9',
color: 'rgba(0, 0, 0, 0.87)',
border: '1px solid #dadde9',
}));
sx prop
<Tooltip
title="Add"
arrow
componentsProps={{
tooltip: {
sx: {
bgcolor: 'common.black',
'& .MuiTooltip-arrow': {
color: 'common.black',
},
},
},
}}
>
<Button>SX</Button>
</Tooltip>
createTheme + ThemeProvider
const theme = createTheme({
components: {
MuiTooltip: {
styleOverrides: {
tooltip: {
backgroundColor: 'pink',
color: 'red',
border: '1px solid #dadde9',
},
},
},
},
});
If you want to change text color , font-size ... of Tooltip there is a simple way.
You can insert a Tag inside Title of Martial Ui Tooltip for example :
<Tooltip title={<span>YourText</span>}>
<Button>Grow</Button>
</Tooltip>
then you can style your tag anyhow you want.
check below Example :
This answer is out of date. This answer was written in 2016 for the 0.x versions of Material-UI. Please see this answer for an approach that works with versions 3 and 4.
well, you can change the text color and the element background customizing the mui theme.
color - is the text color
rippleBackgroundColor - is the tooltip bbackground
Example: Using IconButton - but you could you the Tooltip directly..
import React from 'react';
import IconButton from 'material-ui/IconButton';
import MuiThemeProvider from 'material-ui/lib/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/lib/styles/getMuiTheme';
const muiTheme = getMuiTheme({
tooltip: {
color: '#f1f1f1',
rippleBackgroundColor: 'blue'
},
});
const Example = () => (
<div>
<MuiThemeProvider muiTheme={muiTheme}>
<IconButton iconClassName="muidocs-icon-custom-github" tooltip="test" />
</MuiThemeProvider>
</div>
);
You can also pass a style object for the Tooltip (in IconButton it's tooltipStyles) - but these styles will only be applied for the root element.
It's not possible yet to change the label style to make it wrap in multiple lines.
I ran into this issue as well, and want for anyone seeking to simply change the color of their tooltip to see this solution that worked for me:
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Tooltip from '#material-ui/core/Tooltip';
import Button from '#material-ui/core/Button';
import DeleteIcon from '#material-ui/icons/Delete';
const useStyles = makeStyles(theme => ({
customTooltip: {
// I used the rgba color for the standard "secondary" color
backgroundColor: 'rgba(220, 0, 78, 0.8)',
},
customArrow: {
color: 'rgba(220, 0, 78, 0.8)',
},
}));
export default TooltipExample = () => {
const classes = useStyles();
return (
<>
<Tooltip
classes={{
tooltip: classes.customTooltip,
arrow: classes.customArrow
}}
title="Delete"
arrow
>
<Button color="secondary"><DeleteIcon /></Button>
</Tooltip>
</>
);
};
MUI v5 custom component
Building on NearHuscarl's answer using sx, the simplest approach for me was to create a custom component to include the styling plus any other properties you want repeated on each tooltip.
For example, the component could display the tooltips on the bottom with an arrow and a larger font size:
const StyledTooltip = ({ title, children, ...props }) => (
<Tooltip
{...props}
title={title}
placement="bottom"
arrow
componentsProps={{
tooltip: {
sx: {
fontSize: '1.125rem',
},
},
}}
>
{children}
</Tooltip>
);
const Buttons = () => (
<div>
<StyledTooltip title="This is one">
<Button>One</Button>
</StyledTooltip>
<StyledTooltip title="This is two">
<Button>Two</Button>
</StyledTooltip>
</div>
);
Another solution with HtmlTooltip
I Use HtmlTooltip and add arrow: {color: '#f5f5f9',}, for the arrow tooltip style.
And much more to the tooltip style itself.
So I use ValueLabelComponent to control the label and put there a Tooltip from MaterialUI.
Hopes it give another way to edit MaterialUI Tooltip :)
const HtmlTooltip = withStyles((theme) => ({
tooltip: {
backgroundColor: 'var(--blue)',
color: 'white',
maxWidth: 220,
fontSize: theme.typography.pxToRem(12),
border: '1px solid #dadde9',
},
arrow: {
color: '#f5f5f9',
},
}))(Tooltip);
function ValueLabelComponent({ children, open, value }) {
return (
<HtmlTooltip arrow open={open} enterTouchDelay={0} placement="top" title={value}>
{children}
</HtmlTooltip>
);
}
...
...
return (
<div className={classes.root}>
<Slider
value={value}
onChange={handleChange}
onChangeCommitted={handleChangeCommitted}
scale={(x) => convertRangeValueToOriginalValue(x, minMaxObj)}
valueLabelDisplay="auto"
valueLabelFormat={(x) => '$' + x}
ValueLabelComponent={ValueLabelComponent}
aria-labelledby="range-slider"
/>
</div>
);
I used makeStyles() and ended with that:
import React from 'react';
import Grid from '#mui/material/Grid';
import Typography from '#mui/material/Typography';
import Tooltip from '#mui/material/Tooltip';
import InfoOutlinedIcon from '#mui/icons-material/InfoOutlined';
import { makeStyles } from '#material-ui/core/styles';
const styles = makeStyles({
tooltip: {
backgroundColor: '#FFFFFF',
color: '#000000',
border: '.5px solid #999999',
fontSize: '.85rem',
fontWeight: '400'
}
});
const HeaderTooltip = ({ header, tooltip }) =>
<Grid container direction="row" alignItems="center" spacing={1}>
<Grid item>
<Typography variant='h5'>{header}</Typography>
</Grid>
<Grid item>
<Tooltip title={tooltip} classes={{ tooltip: styles().tooltip }}>
<InfoOutlinedIcon />
</Tooltip>
</Grid>
</Grid>
export default HeaderTooltip;
With styledComponent and MUI V5
import styled from 'styled-components';
....
....
<StyledTooltip title={tooltip}>
<IconTextStyle>
{icon}
<Label>{label}</Label>
</IconTextStyle>
</StyledTooltip>
const StyledTooltip = styled((props) => (
<Tooltip classes={{ popper: props.className }} {...props} />
))`
& .MuiTooltip-tooltip {
display: flex;
background-color: #191c28;
border-radius: 4px;
box-shadow: 0px 0px 24px #00000034;
}
`;
I'm created custom Tooltip in the following way
import React from 'react'
import Tooltip from '#material-ui/core/Tooltip'
import ErrorOutlineOutlinedIcon from '#material-ui/icons/ErrorOutlineOutlined'
import {
makeStyles,
createStyles,
withStyles,
} from '#material-ui/core/styles'
import Typography from '#material-ui/core/Typography'
import { Divider, Link, Paper } from '#material-ui/core'
const HtmlTooltip = withStyles(theme => ({
arrow: {
'&::before': {
color: 'white'
}
},
tooltip: {
backgroundColor: '#f5f5f9',
boxShadow: theme.shadows[8],
color: 'rgba(0, 0, 0, 0.87)',
fontSize: 14,
maxWidth: 800,
padding: 0,
},
tooltipPlacementTop: {
margin: '4px 0',
},
}))(Tooltip)
const imageStyles = { root: { color: 'deeppink', height: 20, marginBottom: 0, width: 20 } }
const Image = withStyles(imageStyles)(({ classes }) => (
<ErrorOutlineOutlinedIcon classes={classes} />
))
const useStyles = makeStyles(theme =>
createStyles({
content: {
border: `1px solid ${theme.palette.grey[300]}`,
margin: 0,
minWidth: 600,
padding: 0,
zIndex: 1,
},
contentInner: {
padding: theme.spacing(1)
},
header: {
backgroundColor: 'deeppink',
fontWeight: 'bold',
padding: theme.spacing(1),
}
})
)
export default function CustomTooltip(params) {
const classes = useStyles()
const labelDisplay = params.content
const textDispaly = params.text
return (
<>
{labelDisplay && labelDisplay.length > 20 ? (<HtmlTooltip arrow interactive title={
<Paper className={classes.content}>
<div className={classes.header}>
<Typography color='inherit' variant='body1' style={{color: 'white', fontSize: '20px'}}>
{params.title}
</Typography>
</div>
<Divider />
<div className={classes.contentInner}>
{textDispaly}
</div>
</Paper>}
placement='top'
>
<div style={{ alignItems: 'center', display: 'flex', fontSize: '12px', justifyContent: 'space-between' }}>
{labelDisplay}<Image/>
</div>
</HtmlTooltip>) : (labelDisplay)}
</>
)
}

Resources