Top 5 JavaScript Code Editors You Should Know in 2020

Top 5 JavaScript Code Editors You Should Know in 2020

In this JavaScript code editor tutorial, you'll see top 5 JavaScript Code Editors you should know in 2020. The most popular JavaScript editors rated by developers around the world: Visual Studio Code, Sublime Text, VIM, WebStorm, Atom.

There are a number of options for developers who are looking for a good JavaScript editor that provides a more efficient and pleasant working environment. Leading JavaScript code editors share many of the same great major features you might expect, including autocompletion (code completion), git integration, and plugin support. But it's the little things that can make one editor a better fit than another for a given developer.

Once you get comfortable with a code editor and familiar with the workflow that makes you more efficient, it can be difficult to switch editors as you will have to re-learn shortcuts to optimize your development processes. Even if you become more efficient in the long run, there is still a large barrier to entry when switching, so it's worthwhile investing a little time up front to pick the best editor for your needs.

Let's go through some of the most popular editor options now.

Visual Studio Code

VSCode from Microsoft is free, open-source, and pretty lightweight upon installation. This is the de facto editor for beginner JavaScript developers, partly because it is pre-loaded with a good set of functionalities that don't require additional plugins. VSCode is also popular and can be ideal for more advanced users that need to get started quickly.

A unique feature of VSCode is that it can be used through the browser. So the exact same environment you get on the desktop is possible on the go using your tablet. A code-server must be configured on a network you can access for this feature to work, but it is very convenient once it is set up.

Tip: Working on a large project in another IDE where the build process can take some time, I will often open the huge JS output file in VSCode and tweak it to instantly test a change in the browser before applying it. VSCode handles these large files swimmingly.

Git is built into the IDE, but the integration is not as robust as some other editors. For example, users of WebStorm prefer the push/merge experience over that of VSCode.

You can install many additional features you may need as extensions, of which there are thousands. But not all of them are actual features. Code snippets are mixed in with the features and add-ons, which can take time to review and find the best options to add. If you ever run into an issue, VSCode has a huge user community; someone has likely had the same issue and has solved it.

If you are not ready for a full paid IDE with all the bells and whistles, and not familiar enough with all the plugins and features you may need, this is the logical starting point.

Atom

The free Atom editor was developed by GitHub. It is actually a specialized version of the chromium browser converted into a text and source code editor. Internally it utilizes Node.js for plugin support.

A plethora of plugins are available for any features you may desire, however it's not as strong out of the box. You must gather a number of plugins to build up the dev environment to where you can be as effective as possible. If you're working with JavaScript, here are some essential Atom plugins to get you started:

  • atom-typescript
  • file-icons - to colorize and assign icons to different file types
  • atom-beautify
  • linter

Tip: Enable the autosave package which will save edits when focus is lost. It is disabled by default.

Multiple users can work on the same file at the same time, each with multiple cursors on the screen, via the teletype plugin. You can use this for mentoring, pair coding, or collaborating. This feature sets Atom apart from other editors.

The git integration is well implemented, as you would expect from GitHub software. Also useful is a git-plus plugin that lets you run git commands through keyboard shortcuts without using the git terminal.

Atom is customizable to the point where you can edit a .less file to adjust the IDE colors which is nice if you like to tweak every detail of your environment. You can run a .coffe script on startup that can be used to quickly change the editor's behavior.

You can write plugins in JavaScript against a well documented editor API. The possibility of authoring your own functionality and behaviors is nice to have, should the need arise.

The editing experience is smooth and you can enhance it with other plugins like minimap, but there is some initial time investment required to set it up with all the features you want. The benefit is that features you don't need won't take time loading which slows down the experience. However, you can experience some momentary sluggishness when loading large files or switching tabs.

I initially liked the idea of editing CSS styles to customize the IDE environment, or at least having the possibility if I ever wanted to come up with my own themes. It sounds fun but in practice, coming up with themes that include many variables are not trivial projects. Fortunately there are many polished theme plugins available for download.

Atom is a solid editor and it will be a perfect fit for many developers.

Sublime Text

Sublime Text is a paid editor but a free trial is available. It is not preloaded with many plug-ins to start, but of course they are available to address any needs you may have. Some packages like SideBarEnhancements to rename, move, copy, and paste should probably be built in to the core bundle but you can download them to enable this functionality.

Similar to Atom, it can take a bit of time to get everything set up. But once up and running, the experience is very smooth. Save on lost focus is also available.

Sublime Text is nice because it's lightweight which makes it very fast to load and work with large projects or files. The "goto anything" feature implementation stands out as it can be used with file names, symbols, and line numbers. Most IDEs provide similar features in one form or another, but being able to combine them and search with queries like "[email protected]" is quite nice.

Selecting a variable selects all occurrences of that variable and renaming it renames all occurrences automatically, so this common task becomes a very streamlined experience.

In many ways, Sublime Text is very similar to Atom. But Sublime Text has the edge with better general performance and responsiveness, which is superb.

VIM

Vim is also a free text editor and very configurable. Originally named vi and the first text editor developed for Unix, it was later extended into a more feature rich editor named Vim. It is available on most Linux distributions.

Vim has robust search and syntax highlighting capabilities, and it is super light so it can perform well with even very large files. But it does require some work to get it set up and ready to use.

A GUI is available, but it is not the default interface for Vim. Even enabling mouse support requires some action to get it working. The default is a keyboard mode that some may prefer accessing every control and feature through shortcuts.

That being said, Vim can be your perfect IDE if you spend some time learning the ins and outs of the software and set up all the behaviors and features you want in it. If you are crunched for time and can't make the initial investment to get things dialed in, Vim may not be the best choice for you.

WebStorm

WebStorm was developed by JetBrains and stands out from the rest as a true JavaScript IDE, as it has all features integrated right out-of-the-box. The development environment for different platforms like React, Angular, Vue.js, etc. is seamless. You can debug node scripts, and run tests on a built-in server. You can also execute and debug npm scripts through a tree view interface. And it doesn’t require any plugins to do this.

However, plugins are available for some rare features that are not built into the software directly. One plugin that was not included by default was a markdown split edit/preview window. But for the most part everything you may need is already there. The nice thing about this is that you will discover features you didn't know existed and how nice it is to have them.

Files save automatically as you work on them by default. When you use another app that doesn't do this, manual save feels very primitive in comparison. Though it's not unique to WebStorm, the implementation is a little nicer.

Some people may not always trust the integrity of the ctrl-z undo state stack, but in WebStorm there is built in VSC system that basically does a commit every time a file is saved. This is internal and is separate from your git commits. Files save at least whenever the file content window loses focus. So if you go a while without committing to git and have to go back or see a previous state after your last commit, it's no problem.

Tip: Ctrl-shift-up/down arrow lets you move lines or blocks of code up or down while the editor handles commas/block brackets automatically.

When you work on projects that have many files, scrolling the file tree in search of a specific file can slow you down. But if one of those items is already open and in focus, clicking the target icon scrolls the project tree view to this file. It's very convenient.

A couple cons are that it is not free. And at times, it can be a memory hog with very large projects. It has gotten better over the years and file contents are indexed internally so that searching large projects is very fast. A recent update also includes a significant improvement in startup speed.

General Editor Productivity Tips

The duplicate line/selection shortcut (in WebStorm: ctrl-d, in Atom: ctrl-shift-d, but they all can do it) is one of my favorites and can save a lot of time in many coding scenarios.

This will come up from time to time where you have a list of items and have to modify the first (or some) character on each line from say '.' to ',' but find-replace is not practical to use. WebStorm allows alt-click to place multiple cursors to make the same edits in multiple places. Still, I find the following approach faster in some scenarios:

  • Place your cursor after the first period, and start doing the change manually.
  • Press backspace, comma, arrow down. Have a finger on each key, and repeat the presses starting slowly then speeding it up as you go. Once you get the pattern down you can speed it up to where you'll go through a list of 200 lines in no time.

It's almost like playing a melody on a piano (as fast as you can). You can do this with numbering lists as well. Write the first line without the number, duplicate the line 9 times, and do the same process except have one finger press a subsequent number each time. Start the next 10 lines with a '1' and do the same process adding a digit after each '1'.

If you google “[editor name] cheatsheet” you can get a quick summary from users for important configuration or shortcuts for the editor you are trying. Print it and read all the shortcuts to become aware of new features and functionality you may not otherwise be exposed to.

Considering how the highlighted actions may improve your current processes will be beneficial. If you see one that may help, put a mark next to it so the next time you are in the situation it's easy to recall. Even if you use it rarely and mainly in the beginning, having a quick reference at hand can lower friction to learning more about your editor and can save time context switching and searching in the future.

I go as far as finding the PDF version, printing and laminating the page for future reference, but for some a bookmark or screenshot may work as well.

Conclusion

If you are a beginner hoping to learn JavaScript and use a polished development environment to start, VSCode is the obvious choice because it is easy to use with many strong features built in.

For more experienced developers who know exactly what they want, Sublime and Atom may be preferable as they will give you complete control over your dev environment. You can choose from thousands of features (packages) to install and keep the application startup and resource usage free of extras you don’t need or want. Spending some time with each will help you make the right choice.

For the hard-core power users who feel at home using the keyboard alone working on projects, you can be more effective with Vim than any other editor. Saving the time it takes for your hand to move between the keyboard and mouse will add up, but it will take some time to master this process!

Lastly, if you don't mind maintaining a paid subscription and are not concerned with memory or CPU limitations of your development machine, WebStorm will get you up and running quickly regardless of the JavaScript development platforms, transpilers, or build processes you work with. It provides a very convenient environment to work in.

I personally use WebStorm as my primary IDE, but still regularly use VSCode to edit individual or very large files when performance is a priority.

JavaScript Tutorial: if-else Statement in JavaScript

JavaScript Tutorial: if-else Statement in JavaScript

This JavaScript tutorial is a step by step guide on JavaScript If Else Statements. Learn how to use If Else in javascript and also JavaScript If Else Statements. if-else Statement in JavaScript. JavaScript's conditional statements: if; if-else; nested-if; if-else-if. These statements allow you to control the flow of your program's execution based upon conditions known only during run time.

Decision Making in programming is similar to decision making in real life. In programming also we face some situations where we want a certain block of code to be executed when some condition is fulfilled.
A programming language uses control statements to control the flow of execution of the program based on certain conditions. These are used to cause the flow of execution to advance and branch based on changes to the state of a program.

JavaScript’s conditional statements:

  • if
  • if-else
  • nested-if
  • if-else-if

These statements allow you to control the flow of your program’s execution based upon conditions known only during run time.

  • if: if statement is the most simple decision making statement. It is used to decide whether a certain statement or block of statements will be executed or not i.e if a certain condition is true then a block of statement is executed otherwise not.
    Syntax:
if(condition) 
{
   // Statements to execute if
   // condition is true
}

Here, condition after evaluation will be either true or false. if statement accepts boolean values – if the value is true then it will execute the block of statements under it.
If we do not provide the curly braces ‘{‘ and ‘}’ after if( condition ) then by default if statement will consider the immediate one statement to be inside its block. For example,

if(condition)
   statement1;
   statement2;

// Here if the condition is true, if block 
// will consider only statement1 to be inside 
// its block.

Flow chart:

Example:

<script type = "text/javaScript"> 

// JavaScript program to illustrate If statement 

var i = 10; 

if (i > 15) 
document.write("10 is less than 15"); 

// This statement will be executed 
// as if considers one statement by default 
document.write("I am Not in if"); 

< /script> 

Output:

I am Not in if
  • if-else: The if statement alone tells us that if a condition is true it will execute a block of statements and if the condition is false it won’t. But what if we want to do something else if the condition is false. Here comes the else statement. We can use the else statement with if statement to execute a block of code when the condition is false.
    Syntax:
if (condition)
{
    // Executes this block if
    // condition is true
}
else
{
    // Executes this block if
    // condition is false
}


Example:

<script type = "text/javaScript"> 

// JavaScript program to illustrate If-else statement 

var i = 10; 

if (i < 15) 
document.write("10 is less than 15"); 
else
document.write("I am Not in if"); 

< /script> 

Output:

i is smaller than 15
  • nested-if A nested if is an if statement that is the target of another if or else. Nested if statements means an if statement inside an if statement. Yes, JavaScript allows us to nest if statements within if statements. i.e, we can place an if statement inside another if statement.
    Syntax:
if (condition1) 
{
   // Executes when condition1 is true
   if (condition2) 
   {
      // Executes when condition2 is true
   }
}

Example:

<script type = "text/javaScript"> 

// JavaScript program to illustrate nested-if statement 

var i = 10; 

if (i == 10) { 

// First if statement 
if (i < 15) 
	document.write("i is smaller than 15"); 

// Nested - if statement 
// Will only be executed if statement above 
// it is true 
if (i < 12) 
	document.write("i is smaller than 12 too"); 
else
	document.write("i is greater than 15"); 
} 
< /script> 

Output:

i is smaller than 15
i is smaller than 12 too
  • if-else-if ladder Here, a user can decide among multiple options.The if statements are executed from the top down. As soon as one of the conditions controlling the if is true, the statement associated with that if is executed, and the rest of the ladder is bypassed. If none of the conditions is true, then the final else statement will be executed.
if (condition)
    statement;
else if (condition)
    statement;
.
.
else
    statement;


Example:

<script type = "text/javaScript"> 
// JavaScript program to illustrate nested-if statement 

var i = 20; 

if (i == 10) 
document.wrte("i is 10"); 
else if (i == 15) 
document.wrte("i is 15"); 
else if (i == 20) 
document.wrte("i is 20"); 
else
document.wrte("i is not present"); 
< /script> 

Output:

i is 20

Building a Powerful Virtual Machine in JavaScript

Building a Powerful Virtual Machine in JavaScript

This JavaScript tutorial explains how to build a powerful Virtual Machine in JavaScript. A flexible, extensible, register-based virtual machine. Support for signed, unsigned and floating point operations. A call stack. Interrupt capabilities. Ability to do memory mapping for IO. An assembly language with macro and module support. A higher level, C like language. We'll use and expand the library from the parser combinators from scratch series. And finally, to be able to take the whole thing into the browser and exend it to create a sort of fantasy console - an emulator for a machine that never existed

16-Bit Virtual Machine in JavaScript 001

In this episode we begin implementing a 16-bit virtual machine from scratch in JavaScript. The concepts of computation are introduced, along with the basics of assembly language and machine code.

Memory Access and Branching (16-Bit VM in JavaScript 002)

In this video we establish the core instruction set of the VM, give the VM the capabilities to read and write to main memory, and also to make decisions about how the program should proceed with branching instructions.

What is the Stack and why do we need it? (16-Bit VM in JavaScript 003)

In this episode we understand what a stack is, how it can be implemented on the lowest level, and how it can then be harnessed to allow the Virtual Machine to run subroutines without losing state.

Implementing Stack Mechanics (16-Bit VM in JavaScript 004)

In this episode, we create an implementation for the stack mechanisms that were described in the last episode.

What is Memory Mapped I/O? (16-Bit VM in JavaScript 005)

This this episode we implement memory mapped I/O, where the address space is utilised as common bus for components to communicate.

Understanding JavaScript Decorators

Understanding JavaScript Decorators

Decorators are actually nothing more than functions that return another function, and that are called with the appropriate details of the item being decorated. Using decorators in your projects today requires some transpiler configuration. What is a Decorator? JavaScript Decorators and Property Descriptors. How to Write a Decorator. Handling API Errors. Decorating Classes. A Babel Workaround

Decorators aren’t a core feature of JavaScript yet; they’re working their way through ECMA TC39’s standardization process. That doesn’t mean we can’t get familiar with them, though. It looks like they’ll be supported natively by Node and browsers at some point in the near future, and in the meantime we’ve got Babel.

What is a Decorator?

Decorator is shorthand for “decorator function” (or method). It’s a function that modifies the behavior of the function or method passed to it by returning a new function. I said “function” a lot there. That’s an occupational hazard when you’re discussing higher-order functions.

You can implement decorators in any language that supports functions as first-class citizens, e.g. JavaScript, where you can bind a function to a variable or pass it as an argument to another function. A couple of those languages have special syntactic sugar for defining and using decorators; one of them is Python:


def cashify(fn):
    def wrap():
        print("$$$$")
        fn()
        print("$$$$")
    return wrap

@cashify
def sayHello():
    print("hello!")

sayHello()

# $$$$
# hello!
# $$$$

Let’s take a look at what’s going on there. Our cashify function is a decorator: it receives a function as an argument, and its return value is also a function. We use Python’s “pie” syntax to apply the decorator to our sayHello function, which is essentially the same thing as if we’d done this below the definition of sayHello:


def sayHello():
    print("hello!")

sayHello = cashify(sayHello)

The end result is that we print dollar signs before and after whatever we’re printing from the function we decorate.

Why am I introducing ECMAScript decorators using an example in Python? I’m glad you asked!

  • Python is a great way to explain the basics because its concept of decorators is a bit more straightforward than the way they work in JS.
  • JS and TypeScript both use Python’s “pie syntax” to apply decorators to methods and properties of classes, so it’s visually and syntactically similar.

Okay, what’s different about JS decorators?

JS Decorators and Property Descriptors

While Python decorators are passed whatever function they’re decorating as an argument, JS decorators receive quite a bit more information due to the way objects work in that language.

Objects in JS have properties, and those properties have values:


const oatmeal = {
  viscosity: 20,
  flavor: 'Brown Sugar Cinnamon',
};

But in addition to its value, each property has a bunch of other behind-the-scenes information that defines different aspects of how it works, called a property descriptor:


console.log(Object.getOwnPropertyDescriptor(oatmeal, 'viscosity'));

/*
{
  configurable: true,
  enumerable: true,
  value: 20,
  writable: true
}
*/

JS is tracking quite a few things related to that property:

  • configurable determines whether or not the type of the property can be changed, and whether it can be deleted from the object.
  • enumerable controls whether the property shows up when you enumerate the object’s properties (like when you call Object.keys(oatmeal) or use it in a for loop).
  • writable controls whether or not you can change the property’s value via the assignment operator =.
  • value is the static value of the property that you see when you access it. It’s usually the only part of the property descriptor that you see or are concerned with. It can be any JS value, including a function, which would make the property a method of the object it belongs to.

Property descriptors can also have two other properties that cause JS to treat them as “accessor descriptors” (more commonly known as getters and setters):

  • get is a function that returns the property’s value instead of using the static value property.
  • set is a special function that gets passed whatever you put on the right side of the equals sign as an argument when you assign a value to the property.

Decorating Without the Frills

JS has actually had an API for working with property descriptors since ES5, in the form of the Object.getOwnPropertyDescriptor and Object.defineProperty functions. For example, If I like the thickness of my oatmeal just the way it is, I can make it read-only using that API like so:


Object.defineProperty(oatmeal, 'viscosity', {
  writable: false,
  value: 20,
});

// When I try to set oatmeal.viscosity to a different value, it'll just silently fail.
oatmeal.viscosity = 30;
console.log(oatmeal.viscosity);
// => 20

I can even write a generic decorate function that lets me mess with the descriptor for any property of any object:


function decorate(obj, property, callback) {
  var descriptor = Object.getOwnPropertyDescriptor(obj, property);
  Object.defineProperty(obj, property, callback(descriptor));
}

decorate(oatmeal, 'viscosity', function(desc) {
  desc.configurable = false;
  desc.writable = false;
  desc.value = 20;
  return desc;
});

Adding the Shiplap and Crown Molding

The first major difference with the Decorators proposal is that it only concerns itself with ECMAScript classes, not regular objects. We’re going to need to over-engineer our breakfast in order to really demonstrate what we can accomplish, so let’s make some classes to represent our bowl of porridge:


class Porridge {
  constructor(viscosity = 10) {
    this.viscosity = viscosity;
  }

  stir() {
    if (this.viscosity > 15) {
      console.log('This is pretty thick stuff.');
    } else {
      console.log('Spoon goes round and round.');
    }
  }
}

class Oatmeal extends Porridge {
  viscosity = 20;

  constructor(flavor) {
    super();
    this.flavor = flavor;
  }
}

We’re representing our bowl of oatmeal using a class that inherits from the more generic Porridge class. Oatmeal sets the default viscosity higher than Porridge‘s default, and it adds a new flavor property. We’re also using another ECMAScript proposal, class fields, to override the viscosity value.

We can re-create our original bowl of oatmeal like so:


const oatmeal = new Oatmeal('Brown Sugar Cinnamon');

/*
Oatmeal {
  flavor: 'Brown Sugar Cinnamon',
  viscosity: 20
}
*/

Great, we’ve got our ES6 oatmeal, and we’re ready to write a decorator!

How to Write a Decorator

JS decorator functions are passed three arguments:

  1. target is the class that our object is an instance of.
  2. key is the property name, as a string, that we’re applying the decorator to.
  3. descriptor is that property’s descriptor object.

What we do inside of the decorator function depends on the purpose of our decorator. In order to decorate a method or property of an object, we need to return a new property descriptor. Here’s how we can write a decorator that makes a property read-only:


function readOnly(target, key, descriptor) {
  return {
    ...descriptor,
    writable: false,
  };
}

We’d use it by modifying our Oatmeal class like this:


class Oatmeal extends Porridge {
  @readOnly viscosity = 20;
  // (you can also put @readOnly on the line above the property)

  constructor(flavor) {
    super();
    this.flavor = flavor;
  }
}

Now our oatmeal’s glue-like consistency is immune to tampering. Thank goodness.

What if we want to do something that’s actually useful? I ran into a situation while working on a recent project where a decorator saved me a lot of typing and maintenance overhead:

Handling API Errors

In the MobX/React app I mentioned in the beginning, I have a couple of different classes that act as data stores. They each represent collections of different things that the user interacts with, and they each talk to different API endpoints for data from the server. In order to handle API errors, I made each of the stores follow a protocol when communicating over the network:

  1. Set the UI store’s networkStatus property to “loading.”
  2. Send a request to the API
  3. Handle the result
    • If successful, update local state with the response
    • If something goes wrong, set the UI store’s apiError property to the error we received
  4. Set the UI store’s networkStatus property to “idle.”

I found myself repeating this pattern a few times before I noticed the smell:


class WidgetStore {
  async getWidget(id) {
    this.setNetworkStatus('loading');

    try {
      const { widget } = await api.getWidget(id);
      // Do something with the response to update local state:
      this.addWidget(widget);
    } catch (err) {
      this.setApiError(err);
    } finally {
      this.setNetworkStatus('idle');
    }
  }
}

That’s a lot of error handling boilerplate. I decided that since I was already using MobX’s @action decorators on all the methods that updated observable properties (not shown here for the sake of simplicity), I might as well just tack on an additional decorator that allowed me to recycle my error handling code. I came up with this:


function apiRequest(target, key, descriptor) {
  const apiAction = async function(...args) {
    // More about this line shortly:
    const original = descriptor.value || descriptor.initializer.call(this);

    this.setNetworkStatus('loading');

    try {
      const result = await original(...args);
      return result;
    } catch (e) {
      this.setApiError(e);
    } finally {
      this.setNetworkStatus('idle');
    }
  };

  return {
    ...descriptor,
    value: apiAction,
    initializer: undefined,
  };
}

I could then replace the boilerplate that I was writing in each API action method with something like this:


class WidgetStore {
  @apiRequest
  async getWidget(id) {
    const { widget } = await api.getWidget(id);
    this.addWidget(widget);
    return widget;
  }
}

My error handling code is still there, but now I only need to write it once and ensure that each class that uses it has a setNetworkStatus and setApiError method.

A Babel Workaround

So what’s up with that line where I’m choosing between descriptor.value and calling descriptor.initializer? That’s a Babel thing. My hunch is that it won’t work that way when JS supports decorators natively, but it’s necessary right now because of how Babel handles arrow functions defined as class properties.

When you define a class property and assign an arrow function as its value, Babel does a little trick to bind that function to the correct instance of the class and give you the right this value. It does this by setting descriptor.initializer to a function that returns the function you wrote, with the correct this value in its scope.

An example should make things less muddy:


class Example {
  @myDecorator
  someMethod() {
    // In this case, our method would be referred to by descriptor.value
  }

  @myDecorator
  boundMethod = () => {
    // Here, descriptor.initializer would be a function that, when called, would return our `boundMethod` function, properly scoped so that `this` refers to the current instance of Example.
  };
}

Decorating Classes

In addition to properties and methods, you can also decorate an entire class. In order to do that, you really only need the first argument passed to your decorator function, target. For example, I can write a decorator that automatically registers the class it’s wrapping as a custom HTML element. I’m using a closure here to enable the decorator to receive whatever name we want to give the element as an argument:


function customElement(name) {
  return function(target) {
    customElements.define(name, target);
  };
}

We’d use it like this:


@customElement('intro-message');
class IntroMessage extends HTMLElement {
  constructor() {
    super();

    const shadow = this.attachShadow({ mode: 'open' });
    this.wrapper = this.createElement('div', 'intro-message');
    this.header = this.createElement('h1', 'intro-message__title');
    this.content = this.createElement('div', 'intro-message__text');
    this.header.textContent = this.getAttribute('header');
    this.content.innerHTML = this.innerHTML;

    shadow.appendChild(this.wrapper);
    this.wrapper.appendChild(this.header);
    this.wrapper.appendChild(this.content);
  }

  createElement(tag, className) {
    const elem = document.createElement(tag);
    elem.classList.add(className);
    return elem;
  }
}

Load that into our HTML, and we can use it like this:


<intro-message header="Welcome to Decorators">
  <p>Something something content...</p>
</intro-message>

Which gives us this in the browser:

Wrapping Up

Using decorators in your projects today requires some transpiler configuration. The most straightforward guide that I’ve seen is located in the MobX docs. It has info for TypeScript and two major versions of Babel.

Keep in mind that decorators are an evolving proposal at this point, so if you use them in production code now, you’ll probably either need to make some updates or keep using Babel’s decorators plugin in legacy mode once they become an official part of the ECMAScript specification. While it’s not even well-supported by Babel yet, the latest version of the decorators proposal already contains big changes that are not backward compatible with the previous version.

Decorators, like many bleeding edge JS features, are a useful tool to have in your kit. They can greatly simplify the sharing of behavior across different, unrelated classes. However, there’s always a cost associated with early adoption. Use decorators, but do so with a clear idea of the implications for your codebase.