Exploring the differences between conditional styles in Material-UI using styled components and JSS

I've made the decision to transition from using makeStyles to utilizing styled in Material-UI v5 for styling, as it's considered the "preferred" method. While makeStyles is still functional, I want to embrace the newer styling solution.

In my navigation list of items, I aim to highlight the currently selected item. Previously, I achieved this using makeStyles:

interface ListItemLinkProps {
    label: string;
    to: string;
}

const useStyles = makeStyles<Theme>(theme => ({
    selected: {
        color: () => theme.palette.primary.main,
    },
}));

const ListItemLink = ({ to, label, children }: PropsWithChildren<ListItemLinkProps>) => {
    const styles = useStyles();

    const match = useRouteMatch(to);
    const className = clsx({ [styles.selected]: !!match });

    return (
        <ListItem button component={Link} to={to} className={className}>
            <ListItemIcon>{children}</ListItemIcon>
            <ListItemText primary={label} />
        </ListItem>
    );
};

(I'm utilizing clsx to determine when to apply the selected style to the ListItem element.)

Now, I'm exploring how to implement this using styled. Here's what I have so far:

const LinkItem = styled(ListItem, {
    shouldForwardProp: (propName: PropertyKey) => propName !== 'isSelected'
})<ListItemProps & LinkProps & { isSelected: boolean }>(({ theme, isSelected }) => ({
    ...(isSelected && { color: theme.palette.primary.main }),
}));

const ListItemLink = ({ to, label, children }: PropsWithChildren<ListItemLinkProps>) => {
    const match = useRouteMatch(to);

    return (
        // @ts-ignore
        <LinkItem button component={Link} to={to} isSelected={!!match}>
            <ListItemIcon>{children}</ListItemIcon>
            <ListItemText primary={label} />
        </LinkItem>
    );
};

Here are my questions regarding this implementation:

  1. Is this the most effective way to apply conditional styles?

  2. I'm struggling with defining the proper types for the styled declaration - hence why I had to include the // @ts-ignore comment above the LinkItem.

Answer №1

In Material-UI v5, Emotion is now used as the default style engine, with internal usage of styled to simplify the switch to styled-components for those who prefer it over Emotion. While styled works well in many cases, it may not be the most elegant solution for this specific scenario. There are two alternatives that offer a more seamless developer experience.

One option is to leverage the new sx prop on all Material-UI components (or utilize the Box component to wrap non-MUI elements for access to sx). Below is a variation of a List demo showcasing this method:

import * as React from "react";
// Components imports...
const ListItemButton = ({ selected = false, ...other }: ListItemButtonProps) => {
  // Logic...
};
export default function SelectedListItem() {
  const [selectedIndex, setSelectedIndex] = React.useState(1);
  // More logic...
}

https://codesandbox.io/s/selectedlistitem-material-demo-z29vj?fontsize=14&hidenavigation=1&theme=dark

While this approach exhibits slightly slower performance compared to styled, it remains sufficient for most use cases. The second option involves directly utilizing Emotion through its css prop, offering a similar developer experience without any speed drawbacks.

/** @jsxImportSource @emotion/react */
import * as React from "react";
// Additional imports...
const ListItemButton = ({ selected = false, ...other }: ListItemButtonProps) => {
  // Logic...
};
export default function SelectedListItem() {
  const [selectedIndex, setSelectedIndex] = React.useState(1);
  // More logic...
}

https://codesandbox.io/s/selectedlistitem-material-demo-8s2lf?fontsize=14&hidenavigation=1&theme=dark

In my upcoming migration to v5 for our app, I plan to combine the use of styled and Emotion's css functionality. I am cautious about extensive adoption of the sx prop until its performance enhancements progress further. Despite its acceptable speed in most scenarios, I prioritize efficiency when choosing between comparable options. Exceptions where the sx prop shines include managing CSS properties across breakpoints or other areas where it offers superior DX.

For more insights, check out these related answers:

  • Is there a performance difference between the sx prop and the makeStyles function in Material UI?
  • When migrating to Material-UI v5, how to deal with conditional classes?

Answer №2

I found success using sx in my implementation

import useMediaQuery from '@mui/material/useMediaQuery';
import { useTheme } from '@mui/material/styles';

...

const isMobile = useMediaQuery(useTheme().breakpoints.down('md'));

...

<Divider sx={{ whiteSpace: isMobile ? 'normal' : 'pre'}}>

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

How to make React MUI Collapse expand to the left instead of the right when collapsing horizontally

Currently, I am utilizing React along with the MUI library for my project. I am interested in creating a collapsible panel that expands to the left instead of the default right direction (similar to the demos showcased here: https://codesandbox.io/s/hzpp ...

A guide to integrating Material-UI with your Meteor/React application

I encountered an issue while trying to implement the LeftNav Menu from the Material-UI example. The error message I received is as follows: While building for web.browser: imports/ui/App.jsx:14:2: /imports/ui/App.jsx: Missing class properties transf ...

What causes the discrepancy in calculating marginTop on a desktop browser compared to a mobile browser?

In the top screenshot, you can see a representation of my Pixel 6XL connected to my laptop in USB debug mode. The checkered area represents the URL bar on the Chrome browser displayed on my Pixel device. Below that, the second screenshot shows the view fr ...

Tips for transferring a boolean value to a generic parameter in Java?

Looking to pass a boolean value to the Generic type in order to be utilized within a condition. This is the generic type interface OptionTypeBase { [key: string]: any; } type OptionsType<OptionType extends OptionTypeBase> = ReadonlyArray<Opt ...

Add the file retrieved from Firestore to an array using JavaScript

Trying to push an array retrieved from firestore, but encountering issues where the array appears undefined. Here is the code snippet in question: const temp = []; const reference = firestore.collection("users").doc(user?.uid); firestore .collec ...

JSON-based Material UI Dropdown Selector

I'm having trouble populating a Material UI Drop down with JSON Data. Can someone help me identify the issue? Below is the code snippet that contains the JSON data and the relevant code. const json = { ResponseMetadata: { RequestId: "1B29B45E145594A ...

What is the best way to invoke a function in a functional React component from a different functional React component?

I need to access the showDrawer method of one functional component in another, which acts as a wrapper. What are some best practices for achieving this? Any suggestions or insights would be appreciated! const TopSide = () => { const [visible, se ...

Utilizing Gradient Color for Overlapping Area Series in Highcharts' Polar Chart

I'm attempting to implement a gradient color at the intersection of two series in a Highchart's polar chart within my React project. You can view my JSFiddle link here: https://jsfiddle.net/pgkk/s29d51zt/604/ The desired outcome is as follows: ...

Maximizing page space with ReactJS and advanced CSS techniques

I'm currently in the process of learning CSS and struggling a bit with it. My main issue right now is trying to make my react components fill the entire height of the browser window. I've been using Display: 'grid' and gridTemplateRows: ...

Dynamic Text Labels in Treemap Visualizations with Echarts

Is it possible to adjust the text size dynamically based on the size of a box in a treemap label? I haven't been able to find a way to do this in the documentation without hardcoding it. Click here for more information label: { fontSize: 16 ...

Navigating with React Router v6 beyond the confines of individual components

When using react-router v5, I would create the history object like this: import { createBrowserHistory } from "history"; export const history = createBrowserHistory(); Then I would pass it to the Router like so: import { Router, Switch, Route, Link } from ...

Checking the formik field with an array of objects through Yup for validation

Here is a snippet of the code I'm working on: https://codesandbox.io/s/busy-bose-4qhoh?file=/src/App.tsx I am currently in the process of creating a form that will accept an array of objects called Criterion, which are of a specific type: export inte ...

Interactive menu that changes based on user input

I am attempting to create individual menus that display the names of each person in my app, but all the menus end up showing the same name. The buttons correctly display different user names, but the menu content does not change. Here is a simplified versi ...

Tips for connecting a Django API project with a nodejs and react frontend

I'm currently working on a Django API project and I am considering incorporating Node.js into the mix. Additionally, I am interested in using React for the frontend of the application. Is this combination of technologies feasible? Would it be advisabl ...

Adjust padding for smaller devices in React using Material UI

In my grid layout, I have columns set to 3,6,3 and a spacing of 3 between them. On larger screens, the spacing between grids looks fine. However, as the screen size decreases, the spacing remains the same which is not visually appealing. What I am aiming ...

Enhancing Single Cards with React and Material UI Card Expander

Currently, I am utilizing React along with Material UI to craft 3 expandable cards containing images. The setup is running smoothly as expected; the cards render and expand almost perfectly. However, there is one major issue: all the cards expand simultane ...

The Mechanics of Running "npm start" in create-react-apps

Can you explain what activity occurs behind the scenes when the npm start command is executed? Does it create a web server to facilitate communication between your browser and the application? ...

Is it possible to optimize the memory usage when running `next build`?

My Next.js app is hosted on a 1gb memory server. However, whenever I run next build to redeploy my application, the memory usage spikes from around 70% to 100%, causing the system to slow down significantly. The build process usually takes 15-20 minutes un ...

Preventing Page Scroll While New Data is Loading

I am currently working on a React class component that uses Post components. Within this component, there is a button that triggers the loading of more data from the database. The issue I am encountering is that when new data is fetched, the page automatic ...

CSS does not have the capability to style child elements

Having trouble changing the text for child elements when applying CSS classes to parent elements. Is there a specific reason why this is happening? Take a look at my current code: <Box //not affecting all child elements ...