Sharing styles between ReactJS and Material-UI - best practices

I am currently facing an issue where I need to share styles across my entire web application.

Here is the Problem: I have been using makeStyles in a repetitive manner as shown below:

In component_A.js

const useStyles = makeStyles({
    specific_style: {
        padding: '24px',
        // etc.
    }
})

function foo() {

    const classes = useStyles();

    return (
        <div className={classes.specific_style}>This is a styled div</div>
    )
}

In component_B.js

const useStyles = makeStyles({
    specific_style: {
        padding: '24px',
        // etc.
    }
})

function bar() {

    const classes = useStyles();

    return (
        <div className={classes.specific_style}>This is another styled div</div>
    )
}

This has resulted in a lot of duplicated code in my project and I believe there must be a more efficient way to handle this. I have considered utilizing a theme to store these classes and accessing them like {theme.specific_style}. However, my attempts thus far have not been successful.

Can someone provide me with a concrete example or solution for achieving this? :)

Possible solutions that I have explored:

  • While native CSS can achieve this, it doesn't align with my preferred approach.
  • Another option could be to separate the style classes into their own .js file and import them when needed. However, considering that I already have access to the theme, it seems logical for these styles to reside within the theme.

Answer №1

Summary: Your preference is to define styles before passing them to the theme provider for better organization:

const useStyles = makeStyles({specific_style: {spacing: 8}, /*more */});
function UseTheme() {
  const classes = useStyles();
  return (<ThemeProvider theme={classes}><DeepChild /></ThemeProvider>);
}
function DeepChild() {
  const theme = useTheme();
  return <span className={theme.specific_style}>specific_style spacing</span>;

If you don’t follow this approach, you may end up passing a raw object instead of a CSS class name. Test your styles in this sandbox.

Note: In terms of sharing styles, using the theme provider as a context provider may limit component reusability.

There are four levels of styling scopes:

  1. Global style override: Utilize '@global' prefix when creating styles for common CSS files or components that don't expose their classnames directly.
const useStyles = makeStyles(
 {'@global': {'.aGlobalStyleClass': {spacing:8} /*more */}
);
function UseTheme() {
  const classes = useStyles();
  return (<div className="aGlobalStyleClass">);
  1. Global Style component override: Customize the global theme to apply changes to all MUI components uniformly.
//from MUI's page
const theme = createMuiTheme({
  components: {
    // Component name ⚛️
    MuiButton: {
      styleOverrides: {
        // Rule name
        textPrimary: {
          // Custom CSS
          color: 'white',
        },
      },
    },
  },
});
  1. Contextual Style component override: Modify specific component styles by consuming the theme provider. However, consider keeping useStyles() at a higher parent level to avoid unnecessary overrides. The main example illustrates this concept.

  2. Local Style component override: Assign className={classes.style} or style={} to individual components. Deploy useStyles() strategically to prevent style duplication and improve hierarchy efficiency.

const useStyles = makeStyles({specific_style: {spacing: 8}, /*more */});
function Parent() {
  const classes = useStyles();
  return (<><Child1 classes={classes}/><Child2 classes={classes}/></>);
}
function Child1({classes}) {
  return <span className={classes.specific_style}>specific_style spacing</span>;
function Child2({classes}) {
  return <div className={classes.specific_style}>specific_style spacing</div>;
}

Considering contextual and local style overrides, strive for harmony between both while eliminating duplicate styles by organizing them based on composition hierarchy. A theme provider update will then affect its entire subtree consistently.

Answer №2

Utilizing the makeStyles() function within a theme is a great way to easily share styles across your entire project structure. With makeStyles(), you have the flexibility to pass in a function rather than just an object:

const useStyles = makeStyles((theme) => {
    root: {
        minWidth: '100%',
        color: theme.textColor,
    }
}

This approach allows you to incorporate higher-level theme components directly into your style objects. To implement this, simply create a central theme file where you can store all of your shared styles, and then import that file just like you would import any other component.

import { lightTheme } from './theme';

Another option is utilizing a Theme provider: https://material-ui.com/styles/advanced/

You could also consider passing the theme as a prop for more dynamic styling options.

Answer №3

If you are looking to create custom utility classes for widespread use, consider adding them to the theme override and accessing them in your components by referencing their class names.

How to Add Overrides to MUI Theme

 let theme = createMuiTheme({
      overrides: {
        MuiAppBar: {
          root: {
            transform: 'translateZ(0)'
          }
        }
      },
      props: {
        MuiIconButton: {
          disableRipple: true
        }
      }
    });

    theme = responsiveFontSizes(theme);

    theme.overrides.MuiCssBaseline = {
      '@global': {
        '.testMe': {
          color: 'red'
        },
        '.container-std': {
          [theme.breakpoints.up('lg')]: {
            maxWidth: '1200px',
            marginLeft: 'auto',
            marginRight: 'auto'
          }
        },
        '.container-wide': {
          margin: theme.spacing(2, 2)
        }
      }
    };

Implementing the Theme into Your Layout

import { theme } from './styles';
import Footer from '../footer/Footer';

export default function Layout(props) {
  const { children } = props;

  return (
    <ThemeProvider theme={theme}>
      <CssBaseline />
      <Grid container direction="column">
        <Grid item style={{ height: '70px' }}>
          <Header />
        </Grid>
        <Grid item>
          <div>
            {children}
          </div>
        </Grid>
        <Grid item>
          <Footer />
        </Grid>
      </Grid>
    </ThemeProvider>
  );
}

Utilizing Custom Classes in Your Components

export default function HomePage() {
  const classes = useStyles();

  return (
    <Box className={`${classes.hero} container-wide`}>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the reason for Jest attempting to resolve all components in my index.ts file?

Having a bit of trouble while using Jest (with Enzyme) to test my Typescript-React project due to an issue with an alias module. The module is being found correctly, but I believe the problem may lie in the structure of one of my files. In my jest.config ...

The AJAX response containing jQuery is failing to produce any visible changes

On Page 1 of my website, there is a form that, upon submission, loads Page 2 using jQuery. This process involves calling a PHP page and displaying the output on Page 1 without actually reloading the entire page. To maintain security, I have set up session ...

Placing an interactive overlay on top of a button component within Material UI

Currently, I am attempting to place a button on top of another button in Material UI: Click here for the demo I want to ensure that clicking the X button only prints B, instead of both A and B. Although I am aware that nesting buttons is not the proper w ...

Tips for transitioning your functions into class-based components on React

Looking for guidance on how to convert my function-based components to class-based components. Here's the original function-based component: import React, { useEffect, useState } from "react" import ReactMapGL, { Marker, Popup } from "r ...

Should we integrate a MongoDB database calculation app with a POST Controller in sails.js?

The primary function of the application interface is to provide variables that have been initially posted by the client, as well as any subsequent database calculations carried out in real time by a specialized engine. Is it possible to integrate this eng ...

Avoiding prop drilling when using server-side rendering in Next.js

How can I prevent prop drilling with SSR in this specific scenario? Layout.tsx: export default function RootLayout({ children, }: { children: React.ReactNode; }) { const user = await getUser(cookies().get("accessToken")?.value); return ...

There was a problem with the WebSocket handshake: the response header value for 'Sec-WebSocket-Protocol' did not match any of the values sent

I've encountered an issue with my React project that involves streaming live video through a WebSocket. Whenever the camera firmware is updated, I face an error in establishing the WebSocket connection. Here's how I initiate the WebSocket: wsRe ...

What is the best way to create dynamic .env files that can easily adapt to different environments instead of

Having multiple .env files (one for Vue and one for Laravel) with 'localhost' hard coded in them is causing accessibility issues from other computers on my network. It would be beneficial to have this set up dynamically, except for production. F ...

Extract the content from the division and set it as the image source

Looking for a way to retrieve the content from a div and insert that into the 'src' parameter of an image. Working on a project where JSON is used to load translation files, preventing me from loading images directly, but I want to at least load ...

When you make a POST request to an express API, the properties are not clearly defined

Just getting into Vue.JS and experimenting with creating a basic MEVN to-do list app for practice. I keep encountering an issue when trying to send a POST request to my express server, receiving the error message: TypeError: Cannot read properties of unde ...

Guide on sending a request to an API and displaying the retrieved information within the same Express application

I recently developed a basic express app with API and JWT authentication. I am now attempting to enhance the app by incorporating page rendering through my existing /api/.. routes. However, I am facing challenges in this process. app.use('/', en ...

Having trouble adding Font Awesome to your Next.js 13 project? Seeing an error message that says "Module not

Looking to incorporate fontawesome into my nextjs 13 project, I've been experimenting with various solutions. Tried implementing solution #3 from an old post as well as following the guidelines provided in the fontawesome documentation. Starting out w ...

Error message: "Do not place <Link> element without having a parent <Router>"

As I attempt to navigate between screens using BottomNavigationAction from Material UI, I encounter the error message You should not use <Link> outside a <Router> Here is my Tab.js: import React from 'react'; import { Link } from &a ...

Error in useState() function - state value does changed from initial value

One of my components utilizes useState() to manage the state of its floating label. Here's an example: const FloatingLabelInput = props => { const { value = '' } = props const [floatingLabel, toggleFloatingLabel] = useState(va ...

What is the best way to utilize the outcome of the initial API request when making a second API call in AngularJS

Looking to utilize the response of a first API call in a second API call. The situation is such that I need to fetch data from the first API and use it in the second API call. I believe a synchronous API call would be necessary. While attempting to imple ...

Guide to creating the onclick feature for a dynamic button in Meteor

<template name="actionTemplate"> {{#each action}} <button class="myButton" id={{_id}}>btn</button> {{> action}} {{/each}} </template> <template name="action"> <div class="sct" id={{_id}}> ...

I encountered an issue where useRouter is returning null and router.isready is not functioning properly with nextjs version 13

After experimenting with Next.js version 13, I encountered an error in my code that I am struggling to resolve. Here is the code snippet: import { useRouter } from 'next/navigation'; async function getCheckoutInfo() { const router = useRoute ...

Leveraging webpack for CSS bundling

I'm currently working on bundling some NPM modules using webpack instead of utilizing a CDN. While I've successfully managed to integrate the .js files, I'm facing challenges with including the .css files for these modules. One example is ...

Trying to set headers in Node/Express after they have already been sent is causing an error

I am attempting to send form data from the client side using jQuery to a POST route that will then pass the data to an API in Node/Express. I am encountering an issue where I receive the error message "Can't set headers after they are sent" and I am ...

I am unsure about implementing the life cycle in Svelte.js

Unknown Territory I am not well-versed in front-end development. Desire to Achieve I aim to utilize the lifecycle method with SvelteJS. Error Alert An error has occurred and the lifecycle method is inaccessible: ERROR in ./node_modules/svelte/index.m ...