Tring to create a custom button component with mui button using theme provider.
Its working with react app and story book when using it directly.
in my case, I need to publish custom button component as npm package and I'm using tsdx for developing and publishing it. once package has published I was was trying to consume it my another react application button getting rendered but custom themes not getting applied which we provided during package development.
button package
import Button from '#mui/material/Button';
// import { deepmerge } from '#mui/utils';
import {btnProperties} from './properties'
import { createTheme, ThemeProvider, StyledEngineProvider } from '#mui/material/styles';
export interface Props {
/** Name of the button will be displayed */
label : string;
/** optional needs to be string --test*/
variant? : string;
/** Custom styles object */
theme? : object
}
const themeProperties = (inputTokenz : any) =>{
let combinedProps = { ...btnProperties , ...inputTokenz}
console.log("combinedProps",combinedProps)
return createTheme({
components: {
MuiButton: {
styleOverrides: {
root: {
fontSize: combinedProps.btnFontSize,
color: combinedProps.btnColor,
backgroundColor: combinedProps.btnBackgroundColor,
fontWeight: combinedProps.btnfontWeight,
textTransform : 'none',
'&:hover' : {
backgroundColor: combinedProps.btnHover,
}
},
},
},
},
});
}
/**
* A custom MUI button component.
*/
export const EnbdButton: FC<Props> = ({ label,theme }) => {
return <div>
{/* <Button variant='contained'>{label}</Button> */}
<StyledEngineProvider injectFirst>
{/* <ThemeProvider theme={deepmerge(baseTheme, getDeepMergeObj(theme))}> */}
<ThemeProvider theme={themeProperties(theme)}>
<Button variant="contained">{label}</Button>
</ThemeProvider>
</StyledEngineProvider>
</div>;
};```
I am recently getting started with Next JS. for styling, I am using Material UI. one issue I am facing is with the fonts. I couldn't able to change the font family to a different font. as per the below example (Github link), I created a _document.js page inside my pages folder
https://github.com/mui-org/material-ui/tree/master/examples/nextjs/pages
_document.js
in the below code I tried changing Roboto with Quicksand
import React from "react";
import Document, { Html, Head, Main, NextScript } from "next/document";
import { ServerStyleSheets } from "#material-ui/core/styles";
// import theme from "../components/Theme.js";
export default class MyDocument extends Document {
render() {
return (
<Html lang="en">
<Head>
{/* PWA primary color */}
{/* <meta name="theme-color" content={theme.palette.primary.main} /> */}
<link
rel="stylesheet"
// href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
href="https://fonts.googleapis.com/css?family=Quicksand:300,400,500,700&display=swap"
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
// `getInitialProps` belongs to `_document` (instead of `_app`),
// it's compatible with server-side generation (SSG).
MyDocument.getInitialProps = async (ctx) => {
const sheets = new ServerStyleSheets();
const originalRenderPage = ctx.renderPage;
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) => sheets.collect(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
// Styles fragment is rendered after the app and page rendering finish.
styles: [
...React.Children.toArray(initialProps.styles),
sheets.getStyleElement(),
],
};
};
Also, I customized my MUI theme object as below
const theme = createMuiTheme({
palette: {
type: "dark",
background: {
default: "#212121",
},
typography: {
fontFamily: "Quicksand",
},
},
});
Layout.js
import { createMuiTheme } from "#material-ui/core/styles";
import { ThemeProvider } from "#material-ui/styles";
import { makeStyles } from "#material-ui/core/styles";
import CssBaseline from "#material-ui/core/CssBaseline";
import Container from "#material-ui/core/Container";
import Divider from "#material-ui/core/Divider";
import NavBar from "./NavBar.js";
import Footer from "./Footer.js";
const useStyles = makeStyles((theme) => ({
root: {
display: "flex",
flexDirection: "column",
minHeight: "100vh",
},
main: {
marginTop: theme.spacing(0),
marginBottom: theme.spacing(10),
},
}));
const theme = createMuiTheme({
palette: {
type: "dark",
background: {
default: "#212121",
},
typography: {
fontFamily: "Quicksand",
},
},
});
function Layout({ children }) {
const classes = useStyles();
return (
<div className={classes.root}>
<ThemeProvider theme={theme}>
<CssBaseline />
<Container component="main" className={classes.main} maxWidth="md">
<NavBar />
<Divider />
{children}
<Divider />
<Footer />
</Container>
</ThemeProvider>
</div>
);
}
export default Layout;
But no luck. can anyone please advise?
Thanks In Advance
Venk
You can wrap your in _app.js with a ThemeProvider.
Example _app.js:
import '../styles/globals.scss'
import { motion, AnimatePresence } from 'framer-motion'
import { useRouter } from 'next/router'
import Header from '../components/Header'
import { AuthProvider } from '../contexts/AuthContext'
import { CartProvider } from '../contexts/CartContext'
import { ThemeProvider } from '#material-ui/core'
import theme from '../styles/theme'
export default function App({ Component, pageProps }) {
const router = useRouter()
return(
<AnimatePresence exitBeforeEnter>
<CartProvider>
<AuthProvider>
<ThemeProvider theme={theme}>
<Header />
<motion.div key={router.pathname} className="main">
<Component { ...pageProps } />
</motion.div>
</ThemeProvider>
</AuthProvider>
</CartProvider>
</AnimatePresence>
)
}
Example theme file:
import { createTheme, responsiveFontSizes } from "#material-ui/core"
let theme = createTheme({
palette: {
primary: {
main: '#0277bd',
},
secondary: {
main: '#b2ff59',
},
darkGray: {
main: '#333333'
},
},
})
theme = responsiveFontSizes(theme)
export default theme
I'm not sure how you understand the Mui theme concept. Let's just talk about NextJs first.
NextJs is no different from React. To set the global font-family you just have to put font-family: your desired font into the html, body {} block inside the global.css which is already imported at the top level of _app.js. Every component will use that global font unless it has custom styles. Example:
html, body {
padding: 0;
margin: 0;
font-family: Quicksand, cursive;
}
Now, with Mui, components use a designed style system which conclude default font option as well. To inject your custom style into every single Mui's component you need to wrap all of them inside a top/root level <ThemeProvider theme={yourCutomTheme}> component to override the defaults, reference: Custom Theming from Mui official doc.
Things are not that complicated tho.
In Nextjs V10+ with material-ui
_document.js
Don't forget to add this link tag before your fonts for improving the performance
<link rel="preconnect" href="https://fonts.gstatic.com" />
_app.js
add the override object to make your font a global font in your app instead of Roboto the default
const theme = responsiveFontSizes(
createMuiTheme({
// your custom UI Config ,
typography: {
fontFamily: "Quicksand",
},
overrides: {
MuiCssBaseline: {
"#global": {
"#font-face": [
{
fontFamily: "Quicksand",
fontStyle: "normal",
fontDisplay: "swap",
fontWeight: // your font weight,
},
],
},
},
},
})
)
Here is what worked for me with Google Fonts, MUI v5, NextJs v13 (TS)
https://nextjs.org/docs/api-reference/next/font
https://mui.com/material-ui/customization/typography/#adding-amp-disabling-variants
Add primary and secondary variant to theme
import { Barlow, Cormorant } from "#next/font/google";
export const barlow = Barlow({
weight: ["200", "400", "500", "600", "700"],
display: "swap",
fallback: ["Helvetica", "Arial", "sans-serif"],
});
export const cormorant = Cormorant({
weight: ["300", "400", "500", "600", "700"],
display: "swap",
fallback: ["Times New Roman", "Times", "serif"],
});
declare module "#mui/material/styles" {
interface TypographyVariants {
primary: React.CSSProperties;
secondary: React.CSSProperties;
}
interface TypographyVariantsOptions {
primary?: React.CSSProperties;
secondary?: React.CSSProperties;
}
}
declare module "#mui/material/Typography" {
interface TypographyPropsVariantOverrides {
primary: true;
secondary: true;
}
}
let theme = createTheme({
typography: {
fontFamily: barlow.style.fontFamily,
body1: {
fontFamily: barlow.style.fontFamily,
},
primary: {
fontFamily: barlow.style.fontFamily,
},
secondary: {
fontFamily: cormorant.style.fontFamily,
},
},
});
in component as sx prop
<Typography
sx={{
fontFamily: theme.typography.secondary.fontFamily,
}}
>
More About Us:
</Typography>
cormorant overides the default
or props
<Typography
variant="secondary"
>
More About Us:
</Typography>
I currently have lots of dialogs and want to change DialogTitle to have Typography h5 heading. Currently this works:
<DialogTitle>
<Typography variant="h5">Create Exercise</Typography>
</DialogTitle>
But I want this to be applied to all dialogs without having to add use the typography component. I also have tried the following with createTheme, but this does not change the fontSize.
const theme = createTheme({
components: {
MuiDialogTitle: {
styleOverrides: {
root: {
// Set this to h5
fontSize: "1.5rem",
},
},
},
},
});
Modify the DialogTitle component as mention below 👇
import MuiDialogTitle from "#material-ui/core/DialogTitle";
import Typography from "#material-ui/core/Typography";
import { withStyles } from "#material-ui/core/styles";
const styles = (theme) => ({
root: {
margin: 0,
padding: theme.spacing(2),
},
});
const DialogTitle = withStyles(styles)((props) => {
const { children, classes, ...other } = props;
return (
<MuiDialogTitle disableTypography className={classes.root} {...other}>
<Typography variant="h5">{children}</Typography>
</MuiDialogTitle>
);
});
export default DialogTitle;
use this component as DialogTitle, even you can modify the style and also able to add extra component into DialogTitle, like closing icon or some secondary title.
I have various resource components that sit inside the react-admin Admin component. The admin component has a custom layout. Each of the resource screens has a different primary colour though, to represent the section that you are in and the content type. How is it possible to pass a specific theme object variable of primary colour to each resource that is in then used inside my custom Layout?
<Admin
authProvider={AuthProvider}
dashboard={Dashboard}
dataProvider={graphQLProvider}
history={history}
layout={Layout}
title="Home"
<Resource name="User" options={{label: 'Administrators'}} list={UserList} show={UserShow} edit={UserEdit}
create={UserCreate} icon={UserIcon}/>
Should I be using the className prop which is available on 'every react-admin component' or is there a better way?
React-admin doesn't support this feature, but you can achieve it using a custom layout (see Replacing the appbar) and a listener on the history location to get the resource from the URL. Something like:
import React, { useEffect, useState } from 'react';
import { useHistory } from 'react-router-dom';
import { AppBar } from 'react-admin';
import { makeStyles } from '#material-ui/core/styles';
const useStyles = makeStyles({
posts: {
backgroundColor: 'red',
},
comments: {
backgroundColor: 'blue',
}
});
const MyAppBar = ({ children }) => {
const [resource, setResource] = useState();
const history = useHistory();
const classes = useStyles();
useEffect(() => {
const newResource = extractResourceFromPathname(history.location.pathname); // left as an exercise to the reader
if (newResource !== resource) {
setResource(newResource);
}
}, [history.location.pathname]);
return <AppBar className={classes[resource]} {...props} />
}
I have a header and want to style all of it's buttons differently than my global theme. I have tried using a child theme like:
<ThemeProvider
theme={(outerTheme) =>
_.merge(outerTheme, {
overrides: {
MuiButton: {
label: {
color: "#fff",
},
},
},
})
}
>
However, while I had expected this to override only MuiButton's in the child theme, it overrode them in them globally.
I know I can use makeStyles but, then, as far as I know, I have to reference it in all the child components which want to use the style. I'd like to wrap a higher level component and have all child components pick up the style. How is this done?
You can do this:
import Button from '#material-ui/core/Button';
import { purple } from '#material-ui/core/colors';
import { makeStyles } from '#material-ui/core/styles';
const useStyles = makeStyles((theme) => ({
header: {
"& button": {
marginRight: theme.spacing(1),
color: theme.palette.getContrastText(purple[500]),
backgroundColor: purple[500],
"&:hover": {
backgroundColor: purple[700],
},
},
},
});
function CustomHeader(props) {
const classes = useStyles();
return (
<div className={classes.header}>
<Button>Button 1</Button>
<Button>Button 2</Button>
<Button disabled>Button 3</Button>
</div>
)
};