I have an app that renders components loaded dynamically from the database. I can render those components and everything works great, but I want to be able to apply specific styles to these components using withStyles.
Here's what I have tried, for sake of simplicity I'm modeling my database response with a JS file.
const dbResponse = {
__styles: {
myStyle: { background: 'blue' },
},
components: (styles) => ({
component: 'AppBar'
className: styles.myStyle,
}),
};
export default withStyles(dbResponse.__styles)(createComponentFromSchema(
dbResponse.components(dbResponse.__styles)
));
This will generate the AppBar component but not with the correct styles. The reason is obviously that what I need to pass to components is the Map that withStyles creates and passes to the children components. I could recursively clone the entire tree and replace the styles but I don't want to do that. I am using server side rendering and have access to all of the JSS specific pieces that are incidental to that scheme if it is helpful.
Related
I have React app (functional components) and I use Material-UI. I have custom theme also and I use theme provider. I have created custom component (e.g. MySuperContainer) and I need to connect it with particular styles (object) in my theme
export const myTheme = { …, components: { MySuperContainer: { padding: “10px“, … } … }
I don’t want to import it to component to sx via useTheme hook. I don’t want to use styled components nor anything similar. Finally I want to write <MySuperContainer /> and it should already find its own custom styles in myTheme.
How can I achieve this? Is there any example or tutorial how to do this? I cannot find anything.
I have a classic atoms/molecules/organisms monorepo with storybooks in order to create a custom components library. At the same time, such a library is based on MUI which has applied a custom theme. All of this is built with TypeScript.
I decided to base our library on MUI because it could be easier and faster for us to develop our custom components, but while I'm saving time on making them I'm putting the rest of my time trying to fix issues with the built :(
The theme
On atoms repo I have added our custom theme json which is imported by other repos (molecules and organisms) and it also gets exported as part of the atom's library and can be applied on an external project.
It uses a basic MUI as a base theme, which has been updated with some branding changes and also some custom colours (here is the problem). To make those custom colours available I have created a createPalette.d.ts file on the same Theme/ folder to declare the additions:
import '#mui/material/styles';
declare module '#mui/material/styles' {
interface Palette {
myColor: Palette['primary'];
}
interface PaletteOptions {
myColor?: PaletteOptions['primary'];
}
}
// Updated Button's props so it allows the color
declare module '#mui/material/Button' {
interface ButtonPropsColorOverrides {
myColor: true;
}
}
The them is then applied to each repo's .storybook/preview.js file like follows:
import { ThemeProvider as MUIThemeProvider, createTheme } from '#mui/material/styles';
import { ThemeProvider } from 'emotion-theming';
import theme from '../stories/Theme';
import { withThemes } from '#react-theming/storybook-addon';
const providerFn = ({ theme, children }) => {
// this fixes some issues between emotion and mui theming
const serialTheme = JSON.parse(JSON.stringify(theme));
const muiTheme = createTheme(serialTheme);
return (
<MUIThemeProvider theme={muiTheme}>
<ThemeProvider theme={muiTheme}>{children}</ThemeProvider>
</MUIThemeProvider>
);
};
const themingDecorator = withThemes(null, [theme], {
providerFn,
});
export const decorators = [themingDecorator];
And it all works just fine. I can make use of the MUI components and my theme easily.
Well, not exactly....
The problem
While I can access the theme from any other repo, it seems to fail to find the custom colours declaration and therefore every time I try to use something like theme.palette.myColor.main (as per our sample above) I get a
Property 'myColor' does not exist on type 'Palette'.ts(2339)
I have found out that, obviously, if I just replicate createPalette.d.ts on, let's say, molecules repo, it doesn't complain when I use it and also it autocomplete it with its main, dark, light... options. However, it does still through an error on 'build':
stories/ComponentFolder/index.tsx: error TS2339: Property 'myColor' does not exist on type 'Palette'.
My thoughts
What I think would be the most obvious solution is to import/extend createPalette.d.ts on any other repo but I don't seem to find a way of doing so, but also then there is the problem when building.
I'm not convinced this will work if I import the theme on any other project because maybe it would complain again about the property not existing on type 'Palette'.
I might as well just be doing this all wrong and the Theme should be stored and applied in a different way. What would be the best approach for something like this?
Notes
I'm also exporting the Theme as part of my library because I have also not been able to find a way of building and publishing my library with my theme applied. At the moment I am importing it: import theme from '#mylibrary/atoms/lib/Theme' and applying it with <ThemeProvider />. In some way, it's basically the same problem but instead of affecting other repos within my monorepo, it's affecting any other react app that tries to make use of the library.
I really, really hope someone can shed some light on this or share their way of creating something similar, please :D
I think that you are importing an interface and using it as a theme, pretty sure that you need to use a theme object like it is shown here in Using with Material-UI
const theme = {
palette: {
primary: {
main: '#556cd6',
},
secondary: {
main: '#19857b',
},
error: {
main: red.A400,
},
background: {
default: '#fff',
},
},
};
I'd like to define styles for my Datepicker component through the Chakra-ui styles.
import DatePicker from 'react-datepicker';
import 'react-datepicker/dist/react-datepicker.css';
import { chakra } from '#chakra-ui/react';
const StyledDatepicker = chakra(DatePicker);
I've used the chakra factory function to be able to give the StyledDatepicker some styles through props, as shown in the code above, but I'd like to define everything in a separate styles .ts file like I've done for the built-in chakra components.
I've made a Datepicker.ts file with some style definitions and added it to the overrides object which is exported here as styles:
const styles = extendTheme(overrides);
export { styles };
which is imported in App.tsx and assigned to the theme prop of my ChakraProvider.
I've made some custom components which was fine, though not exactly what I'm looking for, and I'm looking to change the styles of the header container, the element with classname react-datepicker__header - no examples of making a custom component for this on react-datepicker website. I know I can do it with CSS files, but I'd really like to do all of it in the styles file for consistency.
The answer turned out to be fairly straightforward, but imo, chakra could have made it a bit easier to learn about this possibility.
To target a css class, you just add the classname into your styles object like this:
const DatepickerStyles = {
baseStyle: {
color: 'black',
'.react-datepicker__header': {
background: 'white',
},
},
};
Then do something like this where you want to use the component (In my case, the datepicker, but this will work for any component):
function MyDatePickerComponent(props) {
const datepickerStyles = useStyleConfig('Datepicker');
return (
<Datepicker
__css={datepickerStyles}
//some required props omitted for simplicity
/>
);
}
You can use sx instead of __css, I use __css for readability.
For react-datepicker and my case of styling the header specifically, I had to use a custom calendarContainer component, where I also applied the style in a similar manner by using useMultiStyleConfig instead of useStyleConfig. Hope this can be of help to someone.
I have a react application and a custom component library built using material-ui. Components within the react application itself uses react-jss for styling and I'd like to avoid too many references to material-ui in the app itself just in case I wish to replace material-ui in the future.
From what I've read it seems that Material-UI uses a wrapped version of JSS so they should be compatible, but I'm unable to extract both my own styles as well as those from material-ui. I seem to be able to extract one or the other though...
I've followed the React-JSS Server Side Rendering guide and that works fine, but I'm wondering how I can most easily combine styles from both without having to resort to using #material-ui/core/styles everywhere.
Here is my bootstrap component that registers the JssProvider in my app:
export const Bootstrap: React.FC<IBootstrapProps> = ({
sheetsRegistry,
generateId
}) => {
return (
<JssProvider registry={sheetsRegistry} generateId={generateId}>
<App />
</JssProvider>
)
}
export default Bootstrap
The sheetsRegistry and generateId props comes from a render function called directly from an express handler:
const render = (props: ISSRRenderProps) => {
const sheetsRegistry = new SheetsRegistry()
const generateId = createGenerateId()
const appContent = renderToString(
<Bootstrap
sheetsRegistry={sheetsRegistry}
generateId={generateId}
/>
)
return {
appContent,
styles: sheetsRegistry.toString()
}
}
This gives me all my custom styles in the returned styles property which I in turn dump to the page without issues. The problem comes when I try to combine this with #material-ui. Simply including a component from the library does not give me it's styles server side though they do render on the client after a FOUC.
Using the server rendering guide from material-ui and modifying my Bootstrap component does give my the material-ui styles, but now my own styles are not collected. This also adds a single reference to material-ui in my react app, but if that is what it takes I can live with it as long as sub-components don't have to use material-uis style tools.
import { ServerStyleSheets } from "#material-ui/core/styles"
export const Bootstrap: React.FC<IBootstrapProps> = ({
sheetsRegistry,
generateId,
...rest
}) => {
const sheets = new ServerStyleSheets()
sheetsRegistry?.add(sheets as any)
return sheets.collect(
<JssProvider registry={sheetsRegistry} generateId={generateId}>
<App {...rest} />
</JssProvider>
)
}
Is there a way to get the best of both worlds? I'd love to use Material-UI components in my UI library and extract all styles globally for server side rendering.
I've been able to come up with a "workaround" by modifying my render function to collect material-ui styles separately and just concatenating them:
import { ServerStyleSheets } from "#material-ui/core/styles"
const render = (props: ISSRRenderProps) => {
const sheetsRegistry = new SheetsRegistry()
const generateId = createGenerateId()
const muiStyles = new ServerStyleSheets()
const appContent = renderToString(
muiStyles.collect(
<Bootstrap
sheetsRegistry={sheetsRegistry}
generateId={generateId}
/>
)
)
return {
appContent,
styles: muiStyles.toString() + sheetsRegistry.toString()
}
}
The reason believe this is a workaround and may not be the best solution is that I still have to separately handle my own styles vs material-ui styles. Seeing as they both use JSS behind the scenes I still think it should be possible to seamlessly collect both in the same sheetsRegistry, but this works for now.
New to material-ui here and trying to work out the best way of using the css in js. I can see from the demo code that inserting styles as classes is pretty easy using 'withStyles' but if I do the following I'd need to re-create the 'grow' and 'defaultPadding' styles in every component.
import React from 'react';
import { withStyles } from '#material-ui/core/styles';
import Paper from '#material-ui/core/Paper';
function Dashboard(props) {
const { classes } = props;
return (
<Paper className={classes.defaultPadding}>
Hello
</Paper>
);
}
const styles = theme => ({
grow: {
flexGrow: 1
},
defaultPadding: {
padding: theme.spacing.unit * 3 + 'px'
}
});
export default withStyles(styles)(Dashboard);
I could easily create a factory method in an external file that returns a massive object containing all my default styles but I'd need to be able to pass the existing theme to it if I want to use things like 'theme.spacing.unit'.
So, is there a way to create a bunch of re-usable styles of my own, using theme values, then import them into any component?
Bonus question: Is it possible to create nested objects so I can do things like this? ...
className={classes.layout.header}
I think creating an external factory method would work or you could also create a ThemeProvider to pass your theme to every component that needs the same styles.
In the Codesandbox I created two Dashboard components. One with styled-components just to show how a ThemeProvider works and the other component is using withStyles.
For withStyles factory I called it sharedStyles in the demo and with this you can create a new style object inside your components by calling it and spreading it to a new style (just if you need to add more styles).
Nesting inside styles seems not supported. You could create keys like layout.heading or flatten the object before using it (see code in the demo). Both not perfect and I wouldn't use it.
I'm personally prefering styled-components but you don't need to switch to it. You can look at the source code how they implemented it and use it in your code to pass the theme to your component.
The ThemeProvider is using React Context-Api.