Because CoffeeScript is installed by default in Rails, you might be tempted to use it in your Ember app. Unfortunately, CoffeeScript brings a few annoyances and can cause problems for Ember developers. If you instead use the features provided by ES6 (the next version of JavaScript), you probably won’t even miss it.

CoffeeScript Gotcha #1 - Implicit Returns

CoffeeScript, like Ruby, returns the last line of a function. This lets you avoid writing the word return. In Ember this can cause problems in actions. When a truthy value is returned in an action in Ember, it causes the action to bubble.

# routes/application.coffee
ApplicationRoute = Ember.Route.extend
  actions:
    doSomething: ->
      @set 'something', 'done'

In this example, @set returns the Application Route instance, so it is considered truthy. As a result, the action will bubble up to the parent route until it is handled. If you do not want that behavior, you must add a return statement as the final line to avoid the implicit return.

# routes/application.coffee
ApplicationRoute = Ember.Route.extend
  actions:
    doSomething: ->
      @set 'something', 'done'
      return

Here’s the same action in JavaScript (using ES6 syntax):

// routes/application.js
export default Ember.Route.extend({
  actions: {
    doSomething() {
      this.set('something', 'done');
    }
  }
});

CoffeeScript Gotcha #2 - The code you debug is different than the code you write

When learning a framework, you’re going to do a lot of debugging. If you write your app in CoffeeScript, the final source code that runs in the browser will be JavaScript. You’ll have additional mental overhead converting between source and output while trying to trace down a bug.

By using JavaScript instead, you’ll remove that additional overhead. The code you write is essentially the code you debug.

There is a long-standing open issue to provide CoffeeScript sourcemaps in ember-cli.

CoffeeScript Gotcha #3 - Module imports and exports require more code

An ember application using ember-cli will make heavy use of ES6 modules. That means you’ll have a lot of lines that look like this:

import Ember from 'ember';

export default Ember.Route.extend();

CoffeeScript does not natively understand ES6 imports and exports, so you have to use backticks to escape them. Additionally, you must set the module as a variable before you export it:

`import Ember from 'ember'`

ApplicationRoute = Ember.Route.extend()

`export default ApplicationRoute`

An additional addon ember-cli-coffees6 can be installed to allow you to write the standard syntax.

ES6 equivalents to CoffeeScript Features

String Interpolation

A major gripe a lot of developers have about JavaScript (and one reason why they use CoffeeScript) is that JavaScript has no string interpolation. ES6 fixes that:

// js version
'Hey ' + person.firstName + ','
// es6 version
`Hey ${person.firstName},`

Fat Arrows

Fat arrows are used in CoffeeScript to keep the outer context of this when defining functions. ES6 provides the same thing:

# routes/people.coffee
people.map (person) =>
  thing = @get 'thing'
  person.doSomething(thing)
// routes/people.js
people.map((person) => {
  let thing = this.get('thing');
  person.doSomething(thing);
});

Function Keyword Shorthand

There is no skinny arrow function in ES6, only the fat arrow. However, there is a function shortcut you can use when defining a property that contains a function (which you do frequently in Ember):

# routes/application.coffee
ApplicationRoute = Ember.Route.extend
  model: (params) ->
    @store.find 'person', params
// routes/application.js
export default Ember.Route.extend({
  model(params) {
    return this.store.find('person', params);
  }
});

Destructuring Assignment

The syntax for destructuring is almost identical in CoffeeScript and JavaScript. One use of destructuring is to set multiple variables to their corresponding property names from an object:

# models/person.coffee
{ attr, hasMany } = DS

Person = DS.Model.extend(
  firstName: attr('string'),
  friends: hasMany('friend')
)

`export default Person`
// models/person.js
let { attr, belongsTo } = DS;

export default DS.Model.extend({
  firstName: attr('string'),
  friends: hasMany('friend')
})

Due to some gotchas when using CoffeeScript and equivalent functions provided by ES6, you probably don’t need it on your Ember project. In fact, you might not even miss it.

If you still really want to use CoffeeScript, there is an addon to make it work with ember-cli.

Updated: Jun 4 2015