In Sublime Text 3, how do you enable Emmet for JSX files?

I had been previously using Allan Hortle's JSX package until I ran into an issue with how it handled syntax highlighting. I then noticed that there is an official package, sublime-react.

With Allan Hortle's package, he included a snippet in the Preferences > Key Bindings – User for enabling Emmet functionality that looks like this:

{
    "keys": ["tab"],
    "command": "expand_abbreviation_by_tab", 
    "context": [
        {
            "operand": "source.js.jsx", 
            "operator": "equal", 
            "match_all": true, 
            "key": "selector"
        }
    ]
}

This snippet doesn't appear to work with the official sublime-react package. It seems to be something to modify with the key bindings but an initial perusal of the Sublime documentation yielded no light on the subject. Help?


Solution 1:

In April 2015 Emmet added support for jsx, but it doesn't work by default. Well, for my surprise it was actually working with the control + E shortcut, but I wanted to use the TAB key to expand. Following the official instructions did the trick for me.

Basically, I had to paste the following inside my user key bindings file ( Preferences > Key Bindings — User ):

{ "keys": ["tab"], "command": "expand_abbreviation_by_tab", "context":
    [
        { "operand": "source.js", "operator": "equal", "match_all": true, "key": "selector" },
        { "match_all": true, "key": "selection_empty" },
        { "operator": "equal", "operand": false, "match_all": true, "key": "has_next_field" },
        { "operand": false, "operator": "equal", "match_all": true, "key": "auto_complete_visible" },
        { "match_all": true, "key": "is_abbreviation" }
    ]
}

This is the code without all the comments, and with the right SCOPE_SELECTOR in place.

Solution 2:

If you type shift+super+p in a file it will let you see the current selection's context in the bottom left.

The first word is always the base file type. (source.js, text.html) In the case of the JSX I chose to change this to source.js.jsx. This is because before it is compiled JSX really isn't javascript, though it does look rather similar. There are plenty of completions and sublime sugar that you'd like to have happen in JSX but not JS. sublime-react on the other hand uses plain old source.js.

So this snippet you have is right you just need to replace source.js.jsx with source.js

Solution 3:

From the JSX-SublimeText Package readme:

Emmet's default is to not support JS files. So you will need to add a keyboard shortcut to tab complete in JSX files.

open up Preferences > Key Bindings - user and add this entry:

{
    "keys": ["tab"],
    "command": "expand_abbreviation_by_tab", 
    "context": [
        {
            "operand": "source.js.jsx", 
            "operator": "equal", 
            "match_all": true, 
            "key": "selector"
        },
        {   
            "key": "selection_empty", 
            "operator": "equal", 
            "operand": true,
            "match_all": true 
        }
    ]
}

Solution 4:

In 2021 it's not necessary to configure anything as Emmet has support for JSX files by default.

Obviously you need to install the JSX language definition from package control after installing Emmet.

Now TAB will work but only when the HTML tag is prefixed with a <. For example <div. To change this behavior open the Emmet settings and change this setting to false:

"jsx_prefix": false

To open the Emmet settings either use the File menu as show in the image below, or open the command palette (CMD+Shift+P on macOS) and write "Emmet settings".

enter image description here

Solution 5:

Just expanding upon this answer.
You may not want all letters you write to be expandable into html. You can set another extra object in your context to restrict when the tab completion is applied. This code was found in this gist however I modified the Regex to be a bit better.

{
    "keys": ["tab"],
    "command": "expand_abbreviation_by_tab", 
    "context": [{
        "operand": "source.js", 
        "operator": "equal", 
        "match_all": true, 
        "key": "selector"
    },{
        "key": "preceding_text", 
        "operator": "regex_contains", 
        "operand": "(\\b(a\\b|div|span|p\\b|button)(\\.\\w*|>\\w*)?)", 
        "match_all": true
    },{
        "key": "selection_empty", 
        "operator": "equal", 
        "operand": true, 
        "match_all": true
    }]
}

You'll also need to install the packages RegReplace and Chain of Command as recommended in the gist to even get span.class to turn into <span className="class"></span>
If you'd like to add more elements to listen for just add them to the list i.e. (a\\b|div|span|p\\b|button|strong)
The \\b refers a word boundary and stops the following from expanding abc into <abc></abc>