Configuring web-mode with JSX

I use web-mode for editing different templates like html, .erb, .js.jsx. It understands React’s JSX tags also which is great because I don’t need a separate mode just for JSX.

Web mode can support plain jsx or js.jsx by using auto-mode-alist function.

(require 'web-mode)
(add-to-list 'auto-mode-alist '("\\.jsx\\'" . web-mode))

Here we use auto-mode-alist variable which associates major modes with file patterns so that when that file is opened, the major mode gets automatically activated.

The format is as follows:

(PATTERN . major-mode)

So \\.jsx\\' matches .jsx as well as .js.jsx files and enables web-mode for them. It also sets content-type as jsx for these files so that JSX tags get highlighted and indentation for JSX works properly.

React also allows JSX in plain .js files. My configuration for .js files was as follows:

(add-to-list 'auto-mode-alist '("\\.js\\'" . web-mode))

But JSX tags don’t get highlighted and indentation also does not work with above configuration in plain .js files.

This happens because content-type is set to javascript instead of jsx.

This problem can be solved using adding a hook when web-mode as suggested in this issue.

(add-hook 'web-mode-hook
  (lambda ()
  (web-mode-set-content-type "jsx")
  (message "now set to: %s" web-mode-content-type)))

But problem with this approach is, it sets content-type as jsx for all files like .html or .erb. Due to this indentation of ERB code stopped working.

To solve this, I updated the hook as follows:

(add-hook 'web-mode-hook
  (lambda ()
  (if (equal web-mode-content-type "javascript")
  (web-mode-set-content-type "jsx")
  (message "now set to: %s" web-mode-content-type))))

Now the content-type will be set to jsx only when original content-type is javascript. This solved the issue completely as normal HTML and ERB files work as before and JSX tags and indentation works in plain JavaScript files.

My complete configuration of web mode can found on Github.

Update

Author of web-mode, François-Xavier Bois suggested to me that web-mode-content-types-alist variable provided by web-mode can be used to achieve same thing.

Based on his suggestion, we can force content-type as jsx for files ending with .js and .jsx.

(setq web-mode-content-types-alist
  '(("jsx" . "\\.js[x]?\\'")))

This will force web-mode’s content type as jsx for .js and .jsx files. This is much cleaner than using a hook and also we are using feature designed specifically for this use case. Thanks Francois!

Happy Hacking!