13 min read

Previously with Create React App, you actually didn’t have a lot of options to be able to clean things up visually. You were frequently at the whims and mercy of random Cascading Style Sheets (CSS) project maintainers, and trying to get other libraries, frameworks, or preprocessors involved in the project compilation process was frequently a nightmare.

A preprocessor in the context of Create React App is basically one of the steps in the build process. In this case, we’re talking about something that takes some of the style code (CSS or another format), compiles it down to basic CSS, and adds it to the output of the build process.

This article is taken from the book  Create React App 2 Quick Start Guide by Brandon Richey. This book is intended for those who want to get intimately familiar with the Create React App tool. It covers all the commands in Create React App and all of the new additions in version 2.  To follow along with the examples implemented in this article, you can download the code from the book’s GitHub repository.

Over the span of this article, we’ll be covering materials that span the gamut of style-related functionality and highlighting what is, in my mind, one of the best new features in Create React App: support for CSS Modules and SASS.

Introducing CSS Modules

CSS Modules give you the ability to modularize any CSS code that you import in a way that prevents introducing global, overlapping namespaces, despite the fact that the end result is still just one giant CSS file.


Better project organization

Let’s start off by cleaning up our directory structure in our project a little bit better. What we’re going to do is just separate out each component that has CSS and JavaScript code into their own folders. Let’s first create NewTodo, Todo, App, TodoList, and Divider folders and place all of their related code in each of those.

We’ll also need to create a new file in each of these directories called index.js, which will be responsible for only importing and exporting the appropriate component. For example, the App index file (src/App/index.js) will look like this:

import App from "./App";
export default App;

The new index file of Todo (src/Todo/index.js) will look like this:

import Todo from "./Todo";
export default Todo;

You can probably guess what the index files NewTodo, TodoList, and Divider will look like as well, based on this pattern.

Next, we’ll need to change each place that these files are referenced to make it easier to import all of them. This will unfortunately be a little bit of grunt work, but we’ll need to do it all the same to make sure we don’t break anything in the process.

First, in src/App/App.js, change the TodoList import component to the following:

import TodoList from "../TodoList";

There’s nothing we need to do for Divider since it is a component with no imports. NewTodo and Todo are of a similar type, so we can skip them as well. src/TodoList/TodoList.js, on the other hand, has a lot we need to deal with, since it’s one of our highest-level components and imports a lot:

import Todo from "../Todo";
import NewTodo from "../NewTodo";
import Divider from "../Divider";

But that’s not all. Our test file, src/TodoList/TodoList.test.js, also needs to be modified to include these new paths for our files or else our tests will fail! We’ll need nearly the same list of imports as earlier:

import TodoList from "./TodoList";
import NewTodo from "../NewTodo";
import Todo from "../Todo";

Now, when you reload your application, your code should still be working just fine, your tests should all pass, and everything should be cleanly separated out! Our full project structure should now look like this:

src/
  App/
    App.css
    App.js
    App.test.js
    index.js
  Divider/
    Divider.css
    Divider.js
    index.js
  NewTodo/
    NewTodo.css
    NewTodo.js
    NewTodo.test.js
    index.js
  Todo/
    Todo.css
    Todo.js
    Todo.test.js
    index.js
  TodoList/
    TodoList.css
    TodoList.js
    TodoList.test.js
    index.js
  index.css
  index.js
  setupTests.js
  ... etc ...

Introducing CSS Modules to our application

If we want to use CSS Modules, there are a few simple guidelines that we need to follow. The first is that we need to name our files [whatever].module.css, instead of [whatever].css. The next thing we need to do is to make sure that our styles are named simply and are easy to reference. Let’s start off by following these conventions and by renaming our CSS file for Todo as src/Todo/Todo.module.css, and then we’ll change the contents a tiny bit:

.todo {
  border: 2px solid black;
  text-align: center;
  background: #f5f5f5;
  color: #333;
  margin: 20px;
  padding: 20px;
}
.done {
background: #f5a5a5;
}

Next, we’ll open up src/Todo/Todo.js to take advantage of CSS Modules instead. We created a helper function in our Todo component called cssClasses(), which returns the styles we should be using in our component, and there’s not much we need to change to make this all work exactly the same as earlier. We’ll need to change our import statement at the top as well, since we renamed the file and are changing how our CSS is getting loaded into our code! Take a look at the following:

import styles from "./Todo.module.css";

This enables our code to take advantage of any class names defined in Todo.module.css by referencing them as styles.[className]. For example, in the previous file, we defined two CSS class names: todo and done, so we can now reference them in our component via styles.Todo and styles.done. We’ll need to change the cssClasses() function to use this, so let’s make those exact changes now. In src/Todo/Todo.js, our cssClasses() function should now read as follows:

  cssClasses() {
    let classes = [styles.todo];
    if (this.state.done) {
      classes = [...classes, styles.done];
    }
    return classes.join(' ');
  }

Save and reload, and our application should be back to normal! Next, let’s change the hr tags inside of the todo components to have their own styles and effects. Head back into src/Todo/Todo.module.css and add the following block for our hr tag, which we’ll give a new class of redDivider:

.redDivider {
  border: 2px solid red;
}

And finally, return back to our render() function in src/Todo/Todo.js, and change our render() function’s hr tag inclusion to the following:


Save and reload, and now we should have fully compartmentalized CSS code without worrying about collisions and global namespaces! Here’s how the output looks like:

Composability with CSS Modules

That’s not all that CSS Modules give us, although it’s certainly one of the great parts of CSS Modules that we get immediately and with no fuss. We also get CSS composability, which is the ability to inherit CSS classes off of other classes, whether they’re in the main file or not. This can be incredibly useful when you’re setting up more complicated nested components that all need to handle slightly different style sheets, but are not wildly different from each other.

Let’s say we want to have the ability to mark some components as critical instead of just regular Todos. We don’t want to change too much about the component; we want it to inherit the same basic rules as all of the other Todos. We’ll need to set up some code to make this happen. Back in src/Todo/Todo.js, we’re going to make some modifications to allow a new state property named critical. We’ll start off in the constructor component, where we’ll add our new state property and a bind tag for a function:

  constructor(props) {
    super(props);
    this.state = {
      done: false,
      critical: false
    };
this.markAsDone = this.markAsDone.bind(this);
this.removeTodo = this.removeTodo.bind(this);
this.markCritical = this.markCritical.bind(this);
}

We add a new critical property in our state property and set it to a default value of false. Then we also reference a function (which we haven’t written yet) called markCritical, and we bind this, since we’ll be using it in an event handler later. Next, we’ll tackle the markCritical() function:

  markCritical() {
    this.setState({ critical: true });
  }

We’ll also need to modify our cssClasses() function so that it can react to this new state property. To demonstrate the composability function of CSS Modules, we’ll set it so that classes is originally an empty array, and then the first item either becomes critical or todo, depending on whether or not the item is marked as critical:

  cssClasses() {
    let classes = [];
    if (this.state.critical) {
      classes = [styles.critical];
    } else {
      classes = [styles.todo];
    }
    if (this.state.done) {
      classes = [...classes, styles.done];
    }
    return classes.join(' ');
  }

And finally, in our render function, we’ll create the button tag to mark items as critical:

  render() {
    return (
      
{this.props.description}

); }

We’re not quite done yet, although we’re at least 90% of the way there. We’ll also want to go back to src/Todo/Todo.module.css and add a new block for the critical class name, and we’ll use our composable property as well:

.critical {
  composes: todo;
  border: 4px dashed red;
}

To use composition, all you need to do is add a new CSS property called composes and give it a class name (or multiple class names) that you want it to compose. Compose, in this case, is a fancy way of saying that it inherits the behavior of the other class names and allows you to override others. In the previous case, we’re saying critical is a CSS module class that is composed of a todo model as the base, and adds a border component of a big red dashed line since, well, we’ll just say that this means it is critical.

Save and reload, as always, and you should be able to mark items as Mark as Done, Mark as Critical, or both, or remove them by clicking Remove Me, as in the following screenshot:

And that about covers it for our brief introduction to CSS Modules! Before you move on, you’ll also want to quickly update your snapshots for your tests by hitting U in the yarn test screen.

Introducing SASS to our project

SASS is essentially CSS with extended feature support. When I say extended feature support here, though, I mean it! SASS supports the following feature set, which is missing in CSS:

  • Variables
  • Nesting
  • Partial CSS files
  • Import support
  • Mixins
  • Extensions and inheritance
  • Operators and calculations

Installing and configuring SASS

The good news is that getting SASS support working in a Create React App project is incredibly simple. We first need to install it via yarn, or npm.

$ yarn add node-sass

We’ll see a ton of output from it, but assuming there are no errors and everything goes well, we should be able to restart our development server and get started with some SASS. Let’s create a more general utility SASS file that will be responsible for storing standardized colors that we’ll want to use throughout our application, and something to store that neat gradient hr pattern in case we want to use it elsewhere.

We’ll also change some of the colors that we’re using so that there is some red, green, and blue, depending on whether the item is critical, done, or neither, respectively. In addition, we’ll need to change up our project a little bit and add a new file to have a concept of some shared styles and colors. So, let’s begin:

  1. Create a new file, src/shared.scss, in our project and give it the following body:
$todo-critical: #f5a5a5;
$todo-normal: #a5a5f5;
$todo-complete: #a5f5a5;
$fancy-gradient: linear-gradient(
  to right,
  rgba(0, 0, 0, 0),
  rgba(0, 0, 0, 0.8),
  rgba(0, 0, 0, 0)
);
  1. Next, hop over to src/Divider/Divider.css and rename the file to src/Divider/Divider.scss. Next, we’ll change the reference to Divider.css in src/Divider/Divider.js, as follows:
import "./Divider.scss";
  1. Now we’ll need to change up the code in Divider.scss to import in our shared variables file and use a variable as part of it:
@import "../shared";
hr {
border: 0;
height: 1px;
background-image: $fancy-gradient;
}

So, we import in our new shared SASS file in src/, and then the background-image value just references the $fancy-gradient variable that we created, which means we can now recreate that fancy gradient whenever we need to without having to rewrite it over and over.

  1. Save and reload, and you should see that nothing major has changed.

Mixing SASS and CSS Modules

The good news is that it’s basically no more complicated to introduce SASS to CSS Modules in Create React App. In fact, the steps are borderline identical! So, if we want to start mixing the two, all we need to do is rename some files and change how our imports are handled. Let’s see this in action:

  1. First, head back to our src/Todo/Todo.module.css file and make a very minor modification. Specifically, let’s rename it src/Todo/Todo.module.scss. Next, we need to change our import statement in src/Todo/Todo.js, otherwise the whole thing will fall apart:
import styles from "./Todo.module.scss";
  1. Now, we should have our SASS working for CSS Modules with the Todo component, so let’s start taking advantage of it. Again, we’ll need to import our shared file into this SASS file as well. Note the following back in src/Todo/Todo.module.scss:
@import '../shared';
  1. Next, we’ll need to start changing the references to our various background colors. We’ll change the background for regular Todos to $todo-normal. Then, we’ll change the finished Todo background to $todo-complete. Finally, we’ll want to change the critical items to $todo-critical:
.todo {
  border: 2px solid black;
  text-align: center;
  background: $todo-normal;
  color: #333;
  margin: 20px;
  padding: 20px;
}
.done {
background: $todo-complete;
}

.hr {
border: 2px solid red;
}

.critical {
composes: todo;
background: $todo-critical;
}
  1. Save and reload our project, and let’s make sure the new color scheme is being respected:

Now, we have CSS Modules and SASS  integrated nicely in our Create React App project without having to install a single new dependency. We have them playing nicely together even, which is an even greater achievement!

If you found this post useful, do check out the book, Create React App 2 Quick Start Guide. In addition to getting familiar with Create React App 2, you will also build modern, React projects with, SASS, and progressive web applications.

Read Next

React Native 0.59 is now out with React Hooks, updated JavaScriptCore, and more!

React Native Vs Ionic: Which one is the better mobile app development framework?

How to create a native mobile app with React Native [Tutorial]


Subscribe to the weekly Packt Hub newsletter. We'll send you the results of our AI Now Survey, featuring data and insights from across the tech landscape.