Tips for writing Ember Addons

By: Brian Cardarella

After having published many Ember addons I have started to develop my own sense of "Best Practices" and I'd like to share those with you:

1. Keep it minimal, don't include stylesheets

I see quite a few addons out there that include their own look & feel by including sytlesheets. I actually think this is a bad idea. Keep in mind, every line of code you put into your addon will end up in the final footprint of the apps consuming it. This means if you are including stylesheets those will end up in vendor.css. The odds are that whatever styles you decide look good, someone else might not. They'll waste even more space by including their own overrides. This is wasteful.

Instead, you should keep it minimal. See ember-admin. I intentionally did not style the addon so it is left as minimal as possible. If you want to show off a styled version of the addon, you can either include styles in the dummy app's styles for the addon's test dummy. Allow people to run the addon's server locally and view what could be. Or, you can include an addon wrapper library that depends upon your addon. This wrapper can include default styles that consumers may choose not to alter. For example, ember-admin-bootstrap styles ember-admin with Twitter Bootstrap. If this is good enough for you then you just install this library and it pulls in ember-admin but gives you some nice styling that you don't have to spend time doing.

2. Allow for overrides

I believe strongly in composable addons. A consumer should have the ability to easily extend your addon to do whatever they want. This means organizing your code a certain way. To provide this you should put all of your business logic into addon/ and then include wrapper classes in app/ that just import then export the extended class. For example:

// addon/components/foo-bar.js
import Ember from 'ember';
export default Ember.Component.extend({
  // business logic

// app/components/foo-bar.js
import FooBar from 'my-addon/components/foo-bar';
export default FooBar;

These light wrapper classes should not include any business logic. Again, they simply import then export the extended class. This gives consumers the option of overriding this in their own app/components/foo-bar.js file to extend and add customization.

3. Turn off Prototype Extensions

Currently ember-cli will not generate an addon project with Prototype Extensions turn off. However, I have requested this be the default. Turning off Prototype Extensions will cause the following syntax to fail in your addon's test suite:

foo: function() {
  // whatever

There are several syntax shortcuts that Ember injects into the base Types. Arrays have quite a bit. Turning off Prototype Extensions will force you to write the above code as:

foo: Ember.computed('bar', function() {
  // whatever

And this will play nice with consumer applications that must run with the Prototype Extensions turned off.

It should be noted that Ember 1.10 has a bug where turning off Prototype Extensions causes Ember itself to fail. This should be fixed in 1.11 (Update: This has been addressed in Ember.).

Avoiding Prototype Extensions can be difficult. I plan on writing a future blog post to outline certain strategies to duplicate the behavior that you miss out on without Prototype Extensions.

To turn off Prototype Extensions you'll need to install the ember-disable-prototype-extensions:

npm install --save-dev ember-disable-prototype-extensions

See ember-validations for an example.

4. Test your addon

This one should go without saying but I have seen way too many addons out there that are untested (the generated tests don't count). Please keep in mind that there are people building products that might consume your work. Untested code is just one more thing that could go wrong in someone's app. If unit testing the code is too difficult, at the very least write integration tests against the dummy application to ensure the happy paths.

5. Depend on other addons

You may not know this but addons can depend upon addons. Rather than recreating behavior per-addon it would be best to extract out common behavior to its own dependency. For example, ember-data-route and ember-cli-async-button are both being used in ember-admin.

To use an addon as a dependency it must be put into the dependencies object in package.json, not devDependencies. You may need to add this keyword to your package.json as it is not part of the auto-generated file.

Ember's addon eco-system is getting better every day, and as a community we are learning as we grow how best to build and maintain addons. I'm hoping you find these tips helpful. Please feel free to share your own in the comments below.