Working with ReactJS in WebStorm: Linting, refactoring and compiling
We recently explored coding assistance that WebStorm provides for React and JSX. Now we would like to talk a bit about the tools in the React ecosystem. In this area it’s not easy to provide a complete overview as tools are developing at a crazy pace. So right now we’ll focus on linters (code quality tools), refactoring and tools that can help us compile code.
Code analysis
As you may know, WebStorm has a wide range of built-in inspections for JavaScript and HTML, and these inspections also work for JSX code.
For example, WebStorm alerts you in case of unused variables and functions, missing closing tags, missing statements and much more. For some inspections WebStorm provides quick-fixes, like add a missing semicolon:
You can customize the list of inspections in Preferences | Editor | Inspections. Disable those you don’t want to see, or change severity level from warning to error or vice versa.
On top of such inspections, you can also use linters like ESLint and JSCS for the JSX code. Let’s talk about these in more detail.
ESLint
ESLint is a linting utility that provides a wide range of linting rules, which can also be extended with plugins. WebStorm integrates with ESLint and allows you to see warnings and errors reported by ESLint right in the editor, as you type.
While ESLint itself understands JSX syntax, authors recommend using eslint-plugin-react if you are working with React. To get started, add eslint and eslint-plugin-react modules to your project via npm, then add an ESLint configuration file .eslintrc.
Here’s what .eslint file structure looks like when using ESLint 1.x and react plugin:
{ "ecmaFeatures": { "jsx": true }, "plugins": [ "react" ], "rules": {} }
In ecmaFeatures object you can specify additional language features you’d like to use, for example ES6 classes, modules, etc.
In rules object you can list ESLint built-in rules that you would like to enable, as well as rules available via the react plugin.
For example, thanks to ESLint with react plugin we can get warnings when the display name is not set for React component, or when some dangerous JSX properties are used. Here’s how it looks in the editor, if you have ESLint integration enabled in WebStorm:
To enable ESLint, go to Preferences | Languages & Frameworks | JavaScript | Code quality | ESLint (or simply search for ESLint in Preferences) and check the Enable checkbox. WebStorm will automatically locate ESLint in your project’s node_modules folder and then use .eslintrc configuration by default.
Refactoring
WebStorm offers lots of different refactorings to modify and maintain your code. For example, when you rename a file with Refactor -> Rename, all the references will be renamed automatically. Or, you can easily rename a variable, class or method throughout your whole project.
For React applications, WebStorm can also help you rename components. Place the cursor on the component name and press Ctrl+T to open the Refactor This popup. Select Rename…, type the new name and press Enter. Done!
Here’s an example of renaming a component that is defined and used in only one file:
In the same way, you can rename components defined in one file and then imported using a named export to another file:
Compiling the code
You can set up a build process for your React app in multiple ways. The React Getting started page suggests using Browserify or Webpack which are CommonJS module systems. You will also need Babel and, if using Babel 6 and ES6 code, babel-preset-react and babel-preset-es2015 to compile your code. You can find lots of articles and tutorials with recommendations for the build process using various tools.
As the Getting started tutorial suggests, install the following modules via npm:
npm install --save react react-dom browserify babelify babel-preset-es2015 babel-preset-react
To automate the build process a little bit, let’s add the command suggested in the tutorial to the scripts section of the project’s package.json file:
"scripts": { "build": "browserify -t [ babelify --presets [ react ] ] main.js -o bundle.js" }
where main.js is the main app file and bundle.js is the output file.
WebStorm displays npm tasks listed in package.json in a separate tool window. Just double-click on the task name to run it. No need to run commands in the terminal.
In a similar way you can start Gulp or Grunt tasks in WebStorm.
You can also set up a File watcher for Babel and Browserify in WebStorm to execute similar tasks (you can read about it here), but running tasks via npm scripts or Gulp gives you more flexibility if you add more steps.
More on using React in WebStorm:
- Working with ReactJS in WebStorm: Coding Assistance
- Debugging React apps created with Create React App
- Developing mobile apps with React Native
– JetBrains WebStorm Team
A java architect trying to learn React and Node. says:
May 18, 2016Hi. I’m new to Webstorm. Have been trying to get an example React application with server-side rendering and Flux to work. It’s a nice tutorial blog, uses Gulp, Babel, Webpack among others. It works fine from the command line, but for some reason, I’m getting errors from Webstorm. Googling, if I change the version of Babel in one place (for example), it causes an error in another place. Wonder what I’m doing wrong. The said tutorial code is at https://github.com/zen-js-code/react-universal-web-apps/tree/flux+ssr+context+promises , and is explained here: https://www.smashingmagazine.com/2016/03/server-side-rendering-react-node-express/
Don’t expect you or your team to spend too much time on this, but fyi, it’s a good example, and will be great for your product if it could work for it.
Ekaterina Prigara says:
May 18, 2016Can you please let us know what kind of errors you get from WebStorm? Thanks!
Ekaterina Prigara says:
May 18, 2016As for me, I’m just enable to start the app with npm start with either Node.js v4, 5 or 6 🙁
A java architect trying to learn React and Node says:
May 18, 2016Hi. This is what is happening for me.
1.
If I run npm start from within Webstorm (without trying to enable babel presets for react or es6 via a .babelrc), it works. But breakpoints aren’t working (running debug on npm start, or npm start:debug). Trying to debug results in the process to stop. Also, the jsx stuff is marked with syntax errors.
2.
Adding the following in .babelrc
{
“presets”: [“react”]
}
or
{
“presets”: [“react”, “es2015” ]
}
and running npm start from within Webstorm is giving the errors below. I’m also investigating in parallel.
3.
One thing to note is that the code from git needs a minor massaging before it will run (versioning issue):
git clone -b
npm install
then run this to force an older version of node-dev. else things will fail (TypeError)
npm i node-dev@3.0.0 –save-dev
npm start
4.
The error message after adding a .babelrc in (2) above is:
error { [ModuleBuildError: Module build failed: ReferenceError: [BABEL] /Users/am/workspaces/sandbox/universal-web-app-root/test-react3/app/client.js: Unknown option: /Users/am/workspaces/sandbox/universal-web-app-root/test-react3/.babelrc.presets
at Logger.error (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-core/lib/transformation/file/logger.js:58:11)
at OptionManager.mergeOptions (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-core/lib/transformation/file/options/option-manager.js:126:29)
at OptionManager.addConfig (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-core/lib/transformation/file/options/option-manager.js:107:10)
at OptionManager.findConfigs (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-core/lib/transformation/file/options/option-manager.js:168:35)
at OptionManager.init (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-core/lib/transformation/file/options/option-manager.js:229:12)
at File.initOptions (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-core/lib/transformation/file/index.js:147:75)
at new File (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-core/lib/transformation/file/index.js:137:22)
at Pipeline.transform (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-core/lib/transformation/pipeline.js:164:16)
at transpile (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-loader/index.js:12:22)
at /Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-loader/lib/fs-cache.js:140:16]
name: ‘ModuleBuildError’,
message: ‘Module build failed: ReferenceError: [BABEL] /Users/am/workspaces/sandbox/universal-web-app-root/test-react3/app/client.js: Unknown option: /Users/am/workspaces/sandbox/universal-web-app-root/test-react3/.babelrc.presets\n at Logger.error (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-core/lib/transformation/file/logger.js:58:11)\n at OptionManager.mergeOptions (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-core/lib/transformation/file/options/option-manager.js:126:29)\n at OptionManager.addConfig (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-core/lib/transformation/file/options/option-manager.js:107:10)\n at OptionManager.findConfigs (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-core/lib/transformation/file/options/option-manager.js:168:35)\n at OptionManager.init (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-core/lib/transformation/file/options/option-manager.js:229:12)\n at File.initOptions (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-core/lib/transformation/file/index.js:147:75)\n at new File (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-core/lib/transformation/file/index.js:137:22)\n at Pipeline.transform (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-core/lib/transformation/pipeline.js:164:16)\n at transpile (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-loader/index.js:12:22)\n at /Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-loader/lib/fs-cache.js:140:16’,
module:
DependenciesBlock {
dependencies: [],
blocks: [],
variables: [],
context: ‘/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/app’,
reasons: [],
debugId: 1000,
lastId: -1,
id: 0,
index: null,
index2: null,
chunks: [],
warnings: [],
dependenciesWarnings: [],
errors: [],
dependenciesErrors: [],
request: ‘/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-loader/index.js?{“compact”:false,”cacheDirectory”:true}!/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/eslint-loader/index.js!/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/app/client.js’,
userRequest: ‘/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/app/client.js’,
rawRequest: ‘/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/app/client.js’,
parser: Parser { _plugins: [Object], options: undefined },
resource: ‘/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/app/client.js’,
loaders:
[ ‘/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-loader/index.js?{“compact”:false,”cacheDirectory”:true}’,
‘/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/eslint-loader/index.js’ ],
fileDependencies: [ ‘/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/app/client.js’ ],
contextDependencies: [],
error:
{ [ModuleBuildError: Module build failed: ReferenceError: [BABEL] /Users/am/workspaces/sandbox/universal-web-app-root/test-react3/app/client.js: Unknown option: /Users/am/workspaces/sandbox/universal-web-app-root/test-react3/.babelrc.presets
at Logger.error (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-core/lib/transformation/file/logger.js:58:11)
at OptionManager.mergeOptions (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-core/lib/transformation/file/options/option-manager.js:126:29)
at OptionManager.addConfig (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-core/lib/transformation/file/options/option-manager.js:107:10)
at OptionManager.findConfigs (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-core/lib/transformation/file/options/option-manager.js:168:35)
at OptionManager.init (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-core/lib/transformation/file/options/option-manager.js:229:12)
at File.initOptions (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-core/lib/transformation/file/index.js:147:75)
at new File (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-core/lib/transformation/file/index.js:137:22)
at Pipeline.transform (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-core/lib/transformation/pipeline.js:164:16)
at transpile (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-loader/index.js:12:22)
at /Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-loader/lib/fs-cache.js:140:16]
name: ‘ModuleBuildError’,
message: ‘Module build failed: ReferenceError: [BABEL] /Users/am/workspaces/sandbox/universal-web-app-root/test-react3/app/client.js: Unknown option: /Users/am/workspaces/sandbox/universal-web-app-root/test-react3/.babelrc.presets\n at Logger.error (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-core/lib/transformation/file/logger.js:58:11)\n at OptionManager.mergeOptions (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-core/lib/transformation/file/options/option-manager.js:126:29)\n at OptionManager.addConfig (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-core/lib/transformation/file/options/option-manager.js:107:10)\n at OptionManager.findConfigs (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-core/lib/transformation/file/options/option-manager.js:168:35)\n at OptionManager.init (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/babel-core/lib/transformation/file/options/option-manager.js:229:12)\n at File.initOptions /Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/webpack-core/lib/NormalModuleMixin.js:151
throw e;
^
TypeError: Cannot read property ‘toString’ of null
at printReport (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/tools/build/webpack.js:15:33)
at Watching.handler (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/tools/build/webpack.js:43:9)
at Watching._done (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/webpack/lib/Compiler.js:81:7)
at Watching. (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/webpack/lib/Compiler.js:47:24)
at Compiler. (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/webpack/lib/Compiler.js:395:18)
at /Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/tapable/lib/Tapable.js:99:11
at Compilation. (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/webpack/lib/Compilation.js:433:11)
at errorAndCallback (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/webpack/lib/Compilation.js:340:3)
at Compilation. (/Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/webpack/lib/Compilation.js:400:12)
at /Users/am/workspaces/sandbox/universal-web-app-root/test-react3/node_modules/webpack/lib/Compilation.js:123:4
npm ERR! Darwin 15.4.0
npm ERR! argv “/usr/local/bin/node” “/usr/local/bin/npm” “run-script” “gulp”
npm ERR! node v5.7.1
npm ERR! npm v3.6.0
npm ERR! code ELIFECYCLE
npm ERR! react-ssr-example@0.0.1 gulp: `gulp`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the react-ssr-example@0.0.1 gulp script ‘gulp’.
npm ERR! Make sure you have the latest version of node.js and npm installed.
npm ERR! If you do, this is most likely a problem with the react-ssr-example package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR! gulp
npm ERR! You can get information on how to open an issue for this project with:
npm ERR! npm bugs react-ssr-example
npm ERR! Or if that isn’t available, you can get their info via:
npm ERR! npm owner ls react-ssr-example
npm ERR! There is likely additional logging output above.
Thanks!
A java architect trying to learn React and Node says:
May 18, 2016Update: enabling JSX Harmony makes the earlier errors go away!
In the “run” mode, it works fine now. Trying to figure out the debug mode where the process is exiting, but just wanted to let you know so you don’t waste time on the previous path.
Ekaterina Prigara says:
May 19, 2016The way npm scripts are written in the app you’ve sent makes it very hard to debug it with WebStorm – there’s quite a lot of various tools and utilities that wrapped around the build and serve processes. option.
The WebStorm built-in node debugger works only with the node processes started with –debug-brk=
The start, debug and serve tasks in this projects’ package.json use node-dev tool as a wrapper – the node process is started by this tool then. There’s no way for WebStorm to know what happens inside this node-dev process. It can’t add the required –debug-brk option itself.
You might try to run the gulp task to build the app, then create a new Node.js debug configuration, specify the JS file you need to start to start the app and then start the debug process. As long as you have source maps generated, WebStorm should be able to stop on the breakpoints put in the original source files. (Haven’t tested that myself, I still can’t even build the app).
am says:
May 19, 2016Thank you. I’ll try that.
fyi, the build fails because one of the dependencies in package.json pulls a later version.
After npm insall, one needs to do this:
npm i node-dev@3.0.0 –save-dev
as explained here: http://stackoverflow.com/questions/37259608/getting-typeerror-cannot-read-property-filename-of-undefined-when-calling/37259794#37259794
Thank you so much for all your help!
Ekaterina Prigara says:
May 23, 2016Thanks, I was trying the opposite – to get the later version 🙂
Greg Sandell says:
November 15, 2016I am using React extended with Babel “do expressions” (see https://babeljs.io/docs/plugins/syntax-do-expressions/)
I have set my parser to babel-eslint which is supposed to accept the do expressions. It does when I eslint from the command line, but not in Webstorm. (Everything else is correctly parsed, so I’m confident I have the Webstorm config correct.)
Here is my .eslintrc:
{
“extends”: “”,
“parser”: “babel-eslint”,
“rules”: {
“react/jsx-no-bind”: 1,
“jsx-quotes”: [“warn”, “prefer-single”]
}
}
Ekaterina Prigara says:
November 15, 2016WebStorm does not support this syntax.
The errors that you probably see are from the WebStorm parser, because this syntax is not supported. Using a different parser in ESLint doesn’t affect the way WebStorm understands the code. Please vote for ‘do’ support in this issue:
https://youtrack.jetbrains.com/issue/WEB-20609
Greg Sandell says:
November 15, 2016I might not have been clear in my post, here is my eslint config in Webstorm: http://imgur.com/a/0Zerc
So I am pointing to the nodeJs eslint package. I assume that overrides the Webstorm parser…correct me if I’m wrong. So if eslint is being configured with babel-eslint, I would expect Webstorm to accept the do syntax.
Ekaterina Prigara says:
November 15, 2016No, as I’ve said, that does not override the WebStorm parser and does not change the way WebStorm builds its AST to provide code completion, highlighting, refactorings, etc.
Specifying the parser in the .eslintrc file only affects the way ESLint parses the file when reporting back to WebStorm the lines and columns with the errors. WebStorm then just shows those errors in addition to the errors it had found itself.
Vibhanshu Chaturvedi says:
February 18, 2018A link is broken in the last line of the post.
Correct link for Developing mobile apps with React Native link is https://blog.jetbrains.com/webstorm/2016/12/developing-mobile-apps-with-react-native-in-webstorm/ instead of https//blog.jetbrains.com/webstorm/2016/12/developing-mobile-apps-with-react-native-in-webstorm/
Note missing colon after https
Ekaterina Prigara says:
February 19, 2018Thank you for noticing. I will fix that!
Javier says:
June 23, 2018Hi, Im having issues while formatting the code with the props like:
is turned into this after formatting:
I ve tried around code style JS and Typescript but failed to fix it
Ekaterina Prigara says:
June 26, 2018Can you please use the code tag for the code samples, attach a screenshot or a link to a gist? Thank you!