By default, create-react-app
comes without scss
enabled. We are going to create a workflow to enable watching, compiling and exporting .scss
files to import
them as .css
files into our components — all without having to eject
.
What You Need
create-react-app
installed.node
installed.- Basic knowledge of React.
Make It
Being able to integrate a streamlined workflow that converts .scss
files to .css
without our direct involvement will make us more productive and efficient in our React development.
Recipe Guide
Building CSS from SCSS
Let's start by creating a sample react application:
create-react-app sample-app
Then, we first need to install the command-line interface for Sass as follows:
yarn:
yarn add node-sass-chokidar
npm:
npm install --save node-sass-chokidar
After the package is installed, in our package.json
, we are going to create a script to compile .scss
to .css
:
"build-css": "node-sass-chokidar src/ -o src/"
The script
build-css
takes the .scss
files present within the src
folder and subfolders and compiles them to .css
files. The .css
file will be present in the same location as the original .scss
file. Without having to think about any other paths, it becomes easy to import the .css
file in the components that we are styling.
Watching SCSS and CSS
Next, we are going to create a script to run build-css
to compile any existing files into .css
but also to keep watching the src
folder for changes — as in changes to the content of existing .scss
files or the addition of new ones:
"watch-css": "npm run build-css && node-sass-chokidar src/ -o src/ --watch --recursive"
Our initial package.json
would look like this:
{
"name": "sample-app",
"version": "0.1.0",
"private": true,
"devDependencies": {
"react-scripts": "1.1.1"
},
"dependencies": {
"node-sass-chokidar": "^0.0.3",
"react": "^16.2.0",
"react-dom": "^16.2.0"
},
"scripts": {
"build-css": "node-sass-chokidar src/ -o src/",
"watch-css": "npm run build-css && node-sass-chokidar src/ -o src/ --watch --recursive",
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
Let's test these scripts. Within src
, create test.scss
with the following content:
body {
background: red;
}
Run your React app and notice that there are no changes yet. At this point, we cannot import the styles of test.scss
into any of our components yet since we need its .css
version. Let's run the build-css
command manually:
yarn:
yarn build-css
npm:
npm run build-css
Depending on your development environment, you may see the .css
file nested within the .scss
file or adjacent to it. I am using WebStorm and my .css
file is nested. If you are using Visual Studio Code, for example, the file is adjacent.
We can now import test.css
into the App
component. Open App.js
and add the following import:
import "./test.css";
Save App.js
and now your React app should have a blinding red background. Let's change it to a softer color. Go back to test.scss
and change its contents to:
body {
background: lightcoral;
}
Save test.scss
and look at the browser. Nothing happened. That's because we have not run build-css
manually. Run the script once again and the background color should change. Running this script manually is very tedious and that's why we created that watch-css
script. Let's run our watch script to see its time-savings benefits:
yarn:
yarn watch-css
npm:
npm run watch-css
Let's go back to test.scss
and change the background color to lightblue
. Save it. This time the browser reloaded and the new background color is being displayed.
Let's add a new file to test that our watch-css
script is recognizing new .scss
sources. Create another-test.scss
within the src
folder with the following content:
body {
color: navy;
}
Save the file. You won't see any changes in the browser (because the file has not been imported anywhere) but you will see that another-test.css
has been created. Let's import it into App.js
and then we should see the text color in the browser change to navy.
We have achieved the task of automating the compilation of .scss
into .css
files that we can import into our components for styling; however, as of now, we would have to run watch-css
manually every time we start the project. What we are going to do next is to run this script when we start the project.
Building and Watching SCSS from the Start
We are going to rename the current start
script to start-react
.
From
"start": "react-scripts start"
To
"start-react": "react-scripts start"
Do not forget to add commas at the end of every JSON property when there is more than one property — if you do, it would break your
package.json
file.
We are going to recreate start
as a script that runs both start-react
and watch-css
in parallel. Out of the box, npm
doesn't offer that functionality, but we can use a handy package named npm-run-all
that exactly does that for us.
Install npm-run-all
:
yarn:
yarn add npm-run-all
npm:
npm install npm-run-all
Once the package is installed, we create start
as follows:
"start": "npm-run-all -p watch-css start-react"
The -p
flag signals npm-run-all
to run the commands that follow in parallel.
Stop your application if it's running and rerun it again:
yarn:
yarn start
npm:
npm run start
Once again, go to test.scss
and change the background color to lightseagreen
. Save the file. This time the browser updates and showcases the new background color.
One last important step that we need to take is to update our build
script. When we build our project, we need to ensure that any .scss
file is compiled. We are going to rename the existing build
to build-react
and then create new script logic for build
— just as we did with start
:
"build-react": "react-scripts build",
"build": "npm-run-all -s build-css build-react"
This time around, we want to build-css
first and then build-react
. Using npm-run-all
we can do that by specifying the -s
flag which signals it to run the following commands in sequence.
Conclusion
Your final package.json
should look like this:
{
"name": "sample-app",
"version": "0.1.0",
"private": true,
"devDependencies": {
"react-scripts": "1.1.1"
},
"dependencies": {
"node-sass-chokidar": "^0.0.3",
"npm-run-all": "^4.1.2",
"react": "^16.2.0",
"react-dom": "^16.2.0"
},
"scripts": {
"build-css": "node-sass-chokidar src/ -o src/",
"watch-css": "npm run build-css && node-sass-chokidar src/ -o src/ --watch --recursive",
"start-react": "react-scripts start",
"start": "npm-run-all -p watch-css start-react",
"build-react": "react-scripts build",
"build": "npm-run-all -s build-css build-react",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
You can save this package.json
somewhere else and simply copy and paste its contents into the body of the package.json
of new projects you start.
We were able to bring scss
power into the context of create-react-app
without resorting to eject
by using npm
scripts and the npm-run-all
package. As always, I hope that you enjoyed this blog post. If you want to share improvements or comments on this process of handling scss
, feel free to reach out to me on Twitter, @getDanArias. Thanks for reading!