Muhi Logo Text
AboutBlogWork With Me

How to Create and Style a Readonly TextField in Material UI (MUI) React

Understand the usage of readonly TextField in Material UI (MUI) React with full customization and styling and why it's different from a disabled property.

Last updated on January 26, 2024

react
mui
MUI Readonly

Readonly is a native HTML attribute that can be used with the input element. Its primary purpose is to prevent the user from editing the input value while still incorporating it as part of the form submission. It’s important to understand that the readonly behavior differs from disabled as we can still interact with elements like focus into and tab in and out. Meanwhile, the disabled input is non-interactable and can’t be submitted with the form.

In this tutorial, we will learn how to create a Readonly TextField in Material UI (MUI) and React with customization and styling on different variants.

Create a Readonly TextField

MUI has no direct prop to create a Readonly TextField. However, we can use the inputProps and set the readOnly property to true. The reason behind it is that the TexField component is a wrapper containing the input element along with other elements, so the inputProps is used to pass down the attributes to the underlying input element.

import React from "react";
import TextField from "@mui/material/TextField";

export default function App() {
  return (
    <TextField
      label="Readonly"
      variant="outlined"
      defaultValue="You Can't Edit Me"
      inputProps={{ readOnly: true }}
    />
  );
}

In the example above, we can select the text and focus on the input field, but we can’t edit the value. Below is a screenshot of the TextField component in the browser dev tool.

Dev Tools

We can see that the readonly attribute is added to the input element, which is nested inside the root div element.

Alternatively, we can create our own custom TextField component that extends MUI TextField and pass in a new prop called readOnly, which will be used to set the inputProps value. This approach is particularly useful when reusing it in multiple places.

import React from "react";
import MuiTextField from "@mui/material/TextField";

// Add to a separate file TextField.js
function TextField({ readOnly, ...props }) {
  return <MuiTextField {...props} inputProps={{ readOnly }} />;
}

export default function App() {
  return (
    <TextField
      label="Readonly"
      variant="outlined"
      defaultValue="You Can't Edit Me"
      readOnly
    />
  );
}

We renamed the MUI TextField component to MuiTextField to avoid naming conflict with our custom component. Now we have a custom TextField component that can quickly turn on a readonly mode. Of course, extending components can also help create other features such as validation, formatting, etc.

Style a Readonly TextField

Styling a Readonly TextField in MUI can be tricky. Since the component is a wrapper with different elements, changing styles will persist whether a readonly attribute is present or not. To get around this, we can use the CSS :has selector on the root element to check if it has the readonly prop.

In the example below, we can change the border and label color of the TextField to #cecece only when triggering readonly.

import React from "react";
import { TextField as MuiTextField } from "@mui/material";

function TextField({ readOnly, ...props }) {
  return (
    <MuiTextField
      inputProps={{ readOnly }}
      sx={{
        // change label and border color when readonly
        "&:has([readonly]) ": {
          "& .MuiInputLabel-outlined": {
            color: "#cecece",
          },
          "& .MuiOutlinedInput-notchedOutline": {
            borderColor: "#cecece",
          },
        },
      }}
      {...props}
    />
  );
}

export default function App() {
  return (
    <TextField
      label="Readonly"
      variant="outlined"
      defaultValue="You Can't Edit Me"
      readOnly
    />
  );
}

Note: thre is an active issue to add a Mui-readOnly class to the root element. When resolved, we won’t require the :has selector anymore since we can directly target the class.

Another approach is to change the styles dynamically based on the readOnly prop value. Each class object can be a function that returns the styles based on the condition.

import React from 'react';
import MuiTextField from '@mui/material/TextField';

function TextField({ readOnly, ...props }) {
  return (
    <MuiTextField
      inputProps={{ readOnly }}
      sx={{
        '& .MuiInputLabel-outlined': () =>
          readOnly
            ? {
                color: '#cecece',
              }
            : null,
        '& .MuiOutlinedInput-notchedOutline': () =>
          readOnly
            ? {
                borderColor: '#cecece',
              }
            : null,
      }}
      {...props}
    />
  );
}

export default function App() {
  return (
    <TextField
      label="Readonly"
      variant="outlined"
      defaultValue="You Can't Edit Me"
      readOnly
    />
  );
}

We are returning null because if readOnly is false, then we’re keeping the default style. Of course, we can change the default however we like.

Styling Different Variants

The TextField component has different variants such as standard, outlined, and filled. Previously, we only styled the outlined variant. To style the other variants, we must change the selector’s class name.

For example, we can use the following class selector to style the border and label colors of the filled variant.

import React from "react";
import MuiTextField from "@mui/material/TextField";

function TextField({ readOnly, ...props }) {
  return (
    <MuiTextField
      inputProps={{ readOnly }}
      sx={{
        "&:has([readonly]) ": {
          "& .MuiInputLabel-filled": {
            color: "#cecece",
          },
          "& .MuiFilledInput-root": {
            "&:before": {
              borderBottom: "1px solid #cecece",
            },
          },
        },
      }}
      {...props}
    />
  );
}

export default function App() {
  return (
    <TextField
      label="Readonly"
      variant="filled"
      defaultValue="You Can't Edit Me"
      readOnly
    />
  );
}

If we want to style the standard variant, we can do the following.

import React from "react";
import MuiTextField from "@mui/material/TextField";

function TextField({ readOnly, ...props }) {
  return (
    <MuiTextField
      inputProps={{ readOnly }}
      sx={{
        "&:has([readonly]) ": {
          "& .MuiInputLabel-standard": {
            color: "#cecece",
          },
          "& .MuiInput-root": {
            "&:before": {
              borderBottom: "1px solid #cecece",
            },
          },
        },
      }}
      {...props}
    />
  );
}

export default function App() {
  return (
    <TextField
      label="Readonly"
      variant="standard"
      defaultValue="You Can't Edit Me"
      readOnly
    />
  );
}

Using the Theme

MUI provides a createTheme function that can be used to create a new theme and override the default styles. This is useful when we want the readonly styles to be applied globally.

import React from "react";
import { createTheme, ThemeProvider } from "@mui/material/styles";
import TextField from "@mui/material/TextField";

const theme = createTheme({
  components: {
    MuiTextField: {
      styleOverrides: {
        root: {
          "&:has([readonly]) ": {
            "& .MuiInputLabel-outlined": {
              color: "#cecece",
            },
            "& .MuiOutlinedInput-notchedOutline": {
              borderColor: "#cecece",
            },
          },
        },
      },
    },
  },
});

export default function BasicTextFields() {
  return (
    <ThemeProvider theme={theme}>
      <TextField
        label="Readonly"
        variant="outlined"
        defaultValue="You Can't Edit Me"
        inputProps={{ readOnly: true }}
      />
    </ThemeProvider>
  );
}

Now, anywhere we use the MUI TextField component the readonly mode will be styled based on the custom theme.

For more details on creating and customizing themes and colors, refer to this in-depth guide.

Summary

In this tutorial, we learned how to create a Readonly TextField in Material UI (MUI) and React with customization and styling on different variants. Using the Theme, we also discovered how to style the readonly TextField globally.

Bye for now 👋

If you enjoyed this post, I regularly share similar content on Twitter. Follow me @muhimasri to stay up to date, or feel free to message me on Twitter if you have any questions or comments. I'm always open to discussing the topics I write about!

Recommended Reading

Learn how to create encapsulated and reusable Fieldset component with Material UI (MUI) and React.

react
mui

Discussion

Upskill Your Frontend Development Techniques 🌟

Subscribe to stay up-to-date and receive quality front-end development tutorials straight to your inbox!

​

No spam, sales, or ads. Unsubscribe anytime you wish.

© 2024, Muhi Masri