1

I have just recently switched to using CSS Modules in my project and have come into a bit of an issue.

In the simple code example below, I am creating an input component that I want to be able to reuse as a form field when creating forms. The issue comes in when I want to be able to style the input component differently in certain situations.

FormInput.js

const FormInput = props => (
  <FormControl>
    <InputLabel>{props.label}</InputLabel>
    <Input />
  </FormControl>
);

Form.js

import React from 'react';
import Button from '@material-ui/core/Button';
import Input from './input';

const Form = () => (
  <form>
    <Input label="Name" />
    <Button> Submit </Button>
  </form>
);

I know this can be done using styled-components, but I am really looking for a solution that uses CSS Modules. Any help would be appreciated

1
  • Have you tried passing a unique className or id as a prop and then append that to the existing className? Commented May 17, 2019 at 17:40

2 Answers 2

1

You can simply import the css and pass it as a prop to Input. In this case, you can pass it down as className.

Note: As you'll notice below, CSS can be a bit redundant when it comes to styling nested and pseudo elements, which is why I'd highly recommend SASS (scss or less) for preprocessed stylesheets (post-processing will convert the SASS stylesheets to a plain CSS stylesheet for you).

Working example (reusing Input but styling it with different classes):

Edit React CSS Modules


components/Input (it accepts a className string, onChange function (required), label string, name string (required), and a value string)

import React from "react";
import PropTypes from "prop-types";

const Input = ({ className, onChange, label, name, value }) => (
  <div className={className}>
    <label htmlFor={name}>{label}: </label>
    <input value={value} onChange={onChange} name={name} type="text" />
  </div>
);

// PropTypes ensures that passed down props adhere to the type checking
// rules defined below
Input.propTypes = {
  className: PropTypes.string,
  onChange: PropTypes.func.isRequired,
  label: PropTypes.string,
  name: PropTypes.string.isRequired,
  value: PropTypes.string
};

export default Input;

styles.css (you'll need to use camelCase instead of snake-case for classNames)

.appContainer {
  text-align: center;
  padding: 20px;
}

input {
  height: 40px;
  vertical-align: middle;
  display: inline-block;
  border: 0 none;
  padding: 0 10px;
  background: #fff;
  color: #666;
  border: 1px solid #e5e5e5;
  transition: 0.2s ease-in-out;
  transition-property: color, background-color, border;
  font-size: 15px;
}

.nameField {
  font-weight: bold;
  color: blue;
  margin-bottom: 20px;
}

.nameField > input {
  color: green;
}

.emailField {
  font-weight: bold;
  color: red;
  margin-bottom: 20px;
}

.emailField > input {
  color: blue;
}

.resetButton {
  cursor: pointer;
  background-color: transparent;
  color: #222;
  border: 1px solid #e5e5e5;
  margin: 0;
  overflow: visible;
  box-sizing: border-box;
  padding: 0 30px;
  vertical-align: middle;
  font-size: 14px;
  line-height: 38px;
  text-align: center;
  text-decoration: none;
  text-transform: uppercase;
  transition: 0.1s ease-in-out;
  transition-property: color, background-color, border-color;
}

.resetButton:hover {
  background-color: transparent;
  color: #222;
  border-color: #b2b2b2;
}

.resetButton:focus {
  outline: none;
}

components/App (import all of the css as classes and apply them as needed -- you can also use ES6 destructuring to pull out single classes, for example: import { appContainer } from "./styles.css";)

import React from "react";
import { render } from "react-dom";
import Input from "./components/Input";
import useFieldHandler from "./hooks/useFieldHandler";
import classes from "./styles.css";

const App = () => {
  const { values, handleChange, resetValues } = useFieldHandler({
    name: "",
    email: ""
  });

  return (
    <div className={classes.appContainer}>
      <h1>CSS Modules</h1>
      <Input
        label="Name"
        name="name"
        className={classes.nameField}
        value={values.name}
        onChange={handleChange}
      />
      <Input
        label="Email"
        name="email"
        className={classes.emailField}
        value={values.email}
        onChange={handleChange}
      />
      <div className={classes.btnContainer}>
        <button
          type="button"
          className={classes.resetButton}
          onClick={resetValues}
        >
          Reset
        </button>
      </div>
    </div>
  );
};

render(<App />, document.getElementById("root"));
Sign up to request clarification or add additional context in comments.

Comments

0

So what I was explaining above is to pass a unique id as a prop and then you need to use a template literal to append that to the existing class name in the Input component.

FormInput.js

const FormInput = props => (
  <FormControl>
    <InputLabel>{props.label}</InputLabel>
    <Input uniqueClass='unique-class'/>
  </FormControl>
);
Form.js

import React from 'react';
import Button from '@material-ui/core/Button';
import Input from './input';

const Form = () => (
  <form>
    <Input uniqueClass='unique-class' label="Name" />
    <Button> Submit </Button>
  </form>
);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.