ThemeProvider does not apply theme to child components from material ui library - reactjs

I am using Typescript, React. Material-UI (now MUI) and Webpack.
I am trying to apply a theme using material-ui's ThemeProvider but it only seems to apply that theme to components that are not from the material-ui library.
import React from 'react'
import { ThemeProvider, CssBaseline } from '#material-ui/core'
import { createTheme } from '#material-ui/core/styles'
import Router from './components/Router'
import NavBar from './components/NavBar'
import Toolbar from '#mui/material/Toolbar'
import Container from '#mui/material/Container'
export function App(): JSX.Element {
const theme = createTheme({
palette: {
type: 'dark',
primary: {
main: '#ffeb3b',
},
secondary: {
main: '#795548',
},
},
})
return (
<ThemeProvider theme={theme}>
<NavBar />
<Toolbar />
<Container>
<Router />
</Container>
<CssBaseline />
</ThemeProvider>
)
}
The I can see darkmode being toggled so long as the backdrop for the app is not a MUI paper element. Also my AppBar contained within my NavBar component does not change color when I change the palette main colors.
Putting the theme into a different file does not help. And I have already run Yarn upgrade so everything should be up to date. What is going on?

Related

MUI - dark theme doesn't change anything

I am new to MUI, but all was pretty easy to understand beside changing to dark theme.
I opened documentation of MUI at page where there are examples with dark theme. I copied the first example and it didn't work to me. Why doesn't this simple code example change my MUI theme to 'dark' mode? from here I understood that I need to add more components, but I didn't really understand what does it mean. If I don't add DOM tree it doesn't work? Why the background doesn't change?!
import React from 'react';
import { ThemeProvider, createTheme } from '#mui/material/styles';
function App() {
const darkTheme = createTheme({
palette: {
mode: 'dark'
},
});
return (
<ThemeProvider theme={darkTheme}>
Hello World
</ThemeProvider>
);
}
export default App;
You shoul add <CssBaseline />
import React from 'react';
import { ThemeProvider, createTheme } from '#mui/material/styles';
function App() {
const darkTheme = createTheme({
palette: {
mode: 'dark'
},
});
return (
<ThemeProvider theme={darkTheme}>
<CssBaseline />
Hello World
</ThemeProvider>
);
}
export default App;

React Material UI Theme provider doesn't change colors version 5x

I'm building an app in NextJs that must support custom themes. By reading the Material UI documentation to change the primary color something like should work:
import { ThemeProvider, createTheme } from '#emotion/react';
import { Button } from '#mui/material';
const theme = createTheme({
pallete: {
primary: #fefefe,
}
});
export default (_) => (
<ThemeProvider theme={theme}>
<Button>Primary</Button>
</ThemeProvider>
);
However the color of the button never changes, I'm I doing anything wrong?
You have to use ThemeProvider from #mui package, not from #emotion.
use - import { createTheme, ThemeProvider } from '#mui/material'

React Mui Appbar theming

I tried theming MUI AppBar but I have no idea about theming.
Can I style an AppBar with theme without using styled component or other style api?
palette.js
import { createTheme } from '#mui/material/styles';
const theme = createTheme({
palette: {
primary: {
main: "#000F04"
}
}
});
App.js
import theme from "../../styles/palette";
import { ThemeProvider } from '#mui/material/styles';
const App = () => {
return (
<ThemeProvider theme={theme}>
<AppBar position="static" color="primary">
<Toolbar>
</Toolbar>
</AppBar>
</ThemeProvider>
)
}
Your codesandbox work.
Use the sx props if you need to add specific style. https://mui.com/system/the-sx-prop/
You can too overide style on the theme: https://mui.com/customization/theme-components/#heading-global-style-overrides

Can we keep different themes for different pages in react with styled components website? [duplicate]

I'm using styled-components in my React app and wanting to use a dynamic theme. Some areas it will use my dark theme, some will use the light. Because the styled components have to be declared outside of the component they are used in, how do we pass through a theme dynamically?
That's exactly what the ThemeProvider component is for!
Your styled components have access to a special theme prop when they interpolate a function:
const Button = styled.button`
background: ${props => props.theme.primary};
`
This <Button /> component will now respond dynamically to a theme defined by a ThemeProvider. How do you define a theme? Pass any object to the theme prop of the ThemeProvider:
const theme = {
primary: 'palevioletred',
};
<ThemeProvider theme={theme}>
<Button>I'm now palevioletred!</Button>
</ThemeProvider>
We provide the theme to your styled components via context, meaning no matter how many components or DOM nodes are in between the component and the ThemeProvider it'll still work exactly the same:
const theme = {
primary: 'palevioletred',
};
<ThemeProvider theme={theme}>
<div>
<SidebarContainer>
<Sidebar>
<Button>I'm still palevioletred!</Button>
</Sidebar>
</SidebarContainer>
</div>
</ThemeProvider>
This means you can wrap your entire app in a single ThemeProvider, and all of your styled components will get that theme. You can swap that one property out dynamically to change between a light and a dark theme!
You can have as few or as many ThemeProviders in your app as you want. Most apps will only need one to wrap the entire app, but to have a part of your app be light themed and some other part dark themed you would just wrap them in two ThemeProviders that have different themes:
const darkTheme = {
primary: 'black',
};
const lightTheme = {
primary: 'white',
};
<div>
<ThemeProvider theme={lightTheme}>
<Main />
</ThemeProvider>
<ThemeProvider theme={darkTheme}>
<Sidebar />
</ThemeProvider>
</div>
Any styled component anywhere inside Main will now be light themed, and any styled component anywhere inside Sidebar will be dark themed. They adapt depending on which area of the application they are rendered in, and you don't have to do anything to make it happen! 🎉
I encourage you to check out our docs about theming, as styled-components was very much built with that in mind.
One of the big pain points of styles in JS before styled-components existed was that the previous libraries did encapsulation and colocation of styles very well, but none of them had proper theming support. If you want to learn more about other pain points we had with existing libraries I'd encourage you to watch my talk at ReactNL where I released styled-components. (note: styled-components' first appearance is at ~25 minutes in, don't be surprised!)
While this question was originally for having multiple themes running at the same time, I personally wanted to dynamically switch in runtime one single theme for the whole app.
Here's how I achieved it: (I'll be using TypeScript and hooks in here. For plain JavaScript just remove the types, as, and interface):
I have also included all the imports at the top of each block code just in case.
We define our theme.ts file
//theme.ts
import baseStyled, { ThemedStyledInterface } from 'styled-components';
export const lightTheme = {
all: {
borderRadius: '0.5rem',
},
main: {
color: '#FAFAFA',
textColor: '#212121',
bodyColor: '#FFF',
},
secondary: {
color: '#757575',
},
};
// Force both themes to be consistent!
export const darkTheme: Theme = {
// Make properties the same on both!
all: { ...lightTheme.all },
main: {
color: '#212121',
textColor: '#FAFAFA',
bodyColor: '#424242',
},
secondary: {
color: '#616161',
},
};
export type Theme = typeof lightTheme;
export const styled = baseStyled as ThemedStyledInterface<Theme>;
Then in our main entry, in this case App.tsx we define the <ThemeProvider> before every component that's going to use the theme.
// app.tsx
import React, { memo, Suspense, lazy, useState } from 'react';
import { Router } from '#reach/router';
// The header component that switches the styles.
import Header from './components/header';
// Personal component
import { Loading } from './components';
import { ThemeProvider } from 'styled-components';
// Bring either the lightTheme, or darkTheme, whichever you want to make the default
import { lightTheme } from './components/styles/theme';
// Own code.
const Home = lazy(() => import('./views/home'));
const BestSeller = lazy(() => import('./views/best-seller'));
/**
* Where the React APP main layout resides:
*/
function App() {
// Here we set the default theme of the app. In this case,
// we are setting the lightTheme. If you want the dark, import the `darkTheme` object.
const [theme, setTheme] = useState(lightTheme);
return (
<Suspense fallback={<Loading />}>
<ThemeProvider theme={theme}>
<React.Fragment>
{/* We pass the setTheme function (lift state up) to the Header */}
<Header setTheme={setTheme} />
<Router>
<Home path="/" />
<BestSeller path="/:listNameEncoded" />
</Router>
</React.Fragment>
</ThemeProvider>
</Suspense>
);
}
export default memo(App);
And in header.tsx we pass the setTheme to the component (Lifting the state up):
// header.tsx
import React, { memo, useState } from 'react';
import styled, { ThemedStyledInterface } from 'styled-components';
import { Theme, lightTheme, darkTheme } from '../styles/theme';
// We have nice autocomplete functionality
const Nav = styled.nav`
background-color: ${props => props.theme.colors.primary};
`;
// We define the props that will receive the setTheme
type HeaderProps = {
setTheme: React.Dispatch<React.SetStateAction<Theme>>;
};
function Header(props:
function setLightTheme() {
props.setTheme(lightTheme);
}
function setDarkTheme() {
props.setTheme(darkTheme);
}
// We then set the light or dark theme according to what we want.
return (
<Nav>
<h1>Book App</h1>
<button onClick={setLightTheme}>Light </button>
<button onClick={setDarkTheme}> Dark </button>
</Nav>
);
}
export default memo(Header);
Here's something that did the job for me:
import * as React from 'react';
import { connect } from 'react-redux';
import { getStateField } from 'app/redux/reducers/recordings';
import { lightTheme, darkTheme, ThemeProvider as SCThemeProvider } from 'app/utils/theme';
import { GlobalStyle } from 'app/utils/globalStyles';
interface ThemeProviderProps {
children: JSX.Element;
isLightMode?: boolean;
}
const ThemeProvider = ({ children, isLightMode }: ThemeProviderProps) => {
return (
<SCThemeProvider theme={isLightMode ? lightTheme : darkTheme}>
<React.Fragment>
{children}
<GlobalStyle />
</React.Fragment>
</SCThemeProvider>
);
};
export const ConnectedThemeProvider = connect((state) => ({
isLightMode: getStateField('isLightMode', state)
}))(ThemeProvider);

How to import and use a custom font in a material-ui theme?

I'm trying to import and use the Yellowtail font (from Google Fonts) in my React app in a Material-UI theme.
As far as i know all google fonts are on npm, I've installed it, with the
npm install typeface-yellowtail --save
command.
I have imported it in App.js, put it in the font-family part of the theme, passed the theme to the MuiThemeProvider, but it does not work. What did I miss?
This is what i have inside of App.js (header contains an AppBar with some grids and body contains only an h1 text for testing)
import React, { Component, Fragment } from 'react';
import Header from './Components/Layouts/Header';
import AppBody from './Components/Layouts/AppBody';
import Footer from './Components/Layouts/Footer';
import { MuiThemeProvider, createMuiTheme } from '#material-ui/core';
import 'typeface-yellowtail';
const theme = createMuiTheme({
typography: {
fontFamily:
'"Yellowtail", cursive',
},
});
class App extends Component {
render() {
return (
<MuiThemeProvider theme={theme}>
<Header />
<AppBody />
<Footer />
</MuiThemeProvider>
);
}
}
export default App;
Instead of installing via npm, you can just first load CSS file.
#import url('https://fonts.googleapis.com/css?family=Yellowtail&display=swap');
Import this CSS file
import './assets/css/yellowtail.css';
Now you don't need to use any #font-face. This can be used with font families like normal.
You are missing three things:
Import the npm package as something
Use the CssBaseline component
Add an override to the object provided to createMuiTheme()
See example:
import React, { Component, Fragment } from 'react';
import Header from './Components/Layouts/Header';
import AppBody from './Components/Layouts/AppBody';
import Footer from './Components/Layouts/Footer';
import { MuiThemeProvider, createMuiTheme, CssBaseline } from '#material-ui/core';
import yellowtail from 'typeface-yellowtail';
const theme = createMuiTheme({
typography: {
fontFamily:
'"Yellowtail", cursive',
},
overrides: {
MuiCssBaseline: {
'#global': {
'#font-face': [yellowtail],
},
},
},
});
class App extends Component {
render() {
return (
<MuiThemeProvider theme={theme}>
<CssBaseline />
<Header />
<AppBody />
<Footer />
</MuiThemeProvider>
);
}
}
export default App;
Example CodeSandbox (MUI v3): https://codesandbox.io/s/late-pond-gqql4?file=/index.js
Example CodeSandbox (MUI v4): https://codesandbox.io/s/pensive-monad-eqwlx?file=/index.js
Notes
MuiThemeProvider changed to ThemeProvider in the transition from Material-UI v3 to v4. If you are on v4, this is the only change needed - this example code otherwise works on both versions.
You must wrap text in Material-UI's Typography component for the font to be used.
There is a much easier way of doing this that doesn't require a .css file or installing any extra packages.
If your font is available over a CDN like Google Fonts then just imported into the root HTML file of your app:
<link
href="https://fonts.googleapis.com/css2?family=Yellowtail&display=swap"
rel="stylesheet"
/>
Then add one line in the Material-UI theme:
const theme = createTheme({
typography: { fontFamily: ["Yellowtail", "cursive"].join(",") }
});
Working codesandbox example with Material-UI v5.
Update: Since #ekkis mentioned I'm adding further details on how to include the Google Font. The following answer assumes you're using Material UI v4.10.1
Install the gatsby-plugin-web-font-loader to install Google font.
Edit gatsby-config.js to include the plugin and the Google font.
module.exports = {
plugins: [
{
resolve: "gatsby-plugin-web-font-loader",
options: {
google: {
families: ["Domine", "Work Sans", "Happy Monkey", "Merriweather", "Open Sans", "Lato", "Montserrat"]
}
}
},
]
}
To use the font, create a file called theme.js in src/components
import {
createMuiTheme,
responsiveFontSizes
} from "#material-ui/core/styles"
let theme = createMuiTheme({
typography: {
fontFamily: [
"Work Sans",
"serif"
].join(","),
fontSize: 18,
}
})
// To use responsive font sizes, include the following line
theme = responsiveFontSizes(theme)
export default theme
Now that you've the theme export, you can use it in your components.
Import theme on to your components. Let's say <Layout /> is your main component.
// src/components/layout.js
/**
* External
*/
import React from "react"
import PropTypes from "prop-types"
import {
ThemeProvider
} from "#material-ui/styles"
import { Typography } from "#material-ui/core"
/**
* Internal
*/
import theme from "./theme"
const Layout = ({ children }) => {
return (
<>
<ThemeProvider theme={theme}>
<Typography variant="body1">Hello World!</Typography>
</ThemeProvider>
</>
)
}
To use Google font in other components, just use
<Typography ...>Some Text</Typography>
As long as these components use as their parent, these components will continue to use the Google Font. Hope this helps.
Old Answer:
Place your text in the Typography component to reflect the Google Font you installed via npm
import { Typography } from "#material-ui/core"
Assume you have some text in <AppBody>
<AppBody>
Hello World!
</AppBody>
The above text should now be changed to
<AppBody>
<Typography variant="body1">Hello World!</Typography>
</AppBody>
Refer Material UI docs for the different variants.
I have define my font in main CSS file(index.css)
#font-face {
font-family: 'mikhak';
src: url(./fonts/mikhak.ttf) format("truetype")
}
and then in the App.tsx I added theme
const theme = createTheme({
typography: {
fontFamily: 'mikhak, Raleway, Arial',
}
});
ReactDOM.render(
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>,
document.getElementById('root')
);
Note that it is for Typography components.
This worked for me:
Install your font as a package using #fontsource on npm, i.e:
npm i #fontsource/work-sans
Add to your _app.js your import, this makes your package (font) accessible globally
const workSans = require('#fontsource/work-sans');
In your theme modify your typography:
...
typography: {
fontFamily: 'Work Sans',
}
...
That's it!
I want to add to #georgeos answer that a small revision of the proposed solution worked for me:
export const theme = createTheme({
...
components: {
MuiCssBaseline: {
styleOverrides: {
'#global': {
'#font-face': [Inter, Nunito],
},
},
},
},
...
});
I also needed to add the following to the app.ts file (source):
function App(){
...
return (
<ThemeProvider theme={theme}>
<CssBaseline enableColorScheme /> <---
...
</ThemeProvider>);
...
}

Resources