Your first VS Code extension

Christian Brevik

It's not a secret that VS Code is becoming a very popular choice as editor for many developers1. I like the editor because it is snappy, and has extensions for pretty much any language or framework I dive into.

One feature I love using with VS Code is "Format on Save", especially paired with code formatters like Prettier. But a thing which has annoyed me with that feature, is that it is kind of difficult to turn on and off.

You have to go into the settings.json file and set the editor.formatOnSave field to true or false. This gets really tedious when you're switching between projects, some of it Open Source with peculiar code formatting, or if you just don't want to reformat a whole file for some small change.

Maybe we could make life easier by creating an extension for this? Turns out that's not so difficult.

Creating the extension project

The easiest way I've found to get started, is using Yeoman to create the extension project.

Install both Yeoman and the extension template by running the following in your terminal of choice:

npm install -g yo generator-code  

Then create the extension project by running:

yo code  

You will then get asked several questions to setup the project. These are the choices I made:

  1. What type of extension do you want to create? New Extension (TypeScript)
  2. What's the name of your extension? Toggle Format on Save
  3. What's the identifier of your extension? toggle-format-on-save
  4. What's the description of your extension? Provides a command to toggle Format on Save
  5. What's your publisher name? cbrevik
  6. Enable stricter TypeScript checking in 'tsconfig.json'? Yes
  7. Setup linting using 'tslint'? Yes
  8. Initialize a git repository? Yes

I chose to create an extension written in TypeScript (1), but you also have other options like color themes or code snippets.

The name (2) is what will be displayed for your extension in the VS Code marketplace. While the identifier (3) will be concatenated to your publisher name (5), which will uniquely identify your extension. In this case cbrevik.toggle-format-on-save.

If you don't have a publisher name yet, just put whatever. It can be changed later by editing the publisher field in the generated package.json. I'll show you how to create a publisher further below in this post.

The rest of the options are pretty straightforward. If in doubt, answer Yes.

Diving into the code

The template code defines a command called Hello World which will display a "Hello World!" notification at the top of your VS Code instance:

Hello world demo

This is made possible by three connected snippets of code in the package.json file and the src/extension.ts file.

You tell VS Code that your extension contributes a command by adding it in package.json under the contributes field, as an object in the commands array:

    "contributes": {
        "commands": [
                "command": "extension.sayHello",
                "title": "Hello World"

Take note of the command field value extension.sayHello, which is the key for this command. The title is the text displayed in the Command Palette you see in the gif above.

Another thing which has to be added to package.json is activationEvents:

    "activationEvents": [

This is because in VS Code, extensions are activated lazily. So you will have to define the events which will activate your extension. In our case, we say that our extension will be activated when the extension.sayHello command is run.2

To actually implement the command itself, we have a src/extension.ts file, which exports an activate function:

import * as vscode from 'vscode';

export function activate(context: vscode.ExtensionContext) {  
    // do stuff here

This function will, not surprisingly, be run when the activationEvent defined above is emitted. This happens when we run Hello World in the VS Code Command Palette.

Inside the activate function we have to register the command implementation. Note that we're using the same command key as defined in package.json.

The command is also added to the context.subscriptions array, so it can be disposed if the extension is deactivated:

export function activate(context: vscode.ExtensionContext) {  
    const disposable = vscode.commands.registerCommand(
        () => { vscode.window.showInformationMessage('Hello World!'); }


As you see the registerCommand function lets you define a callback, which is invoked when the command with the extension.sayHello key is run. This callback in turn displays the "Hello World!" notification at the top of your VS Code instance.

Debugging the extension

If you want to test the extension, this can be done by hitting F5 on your keyboard (or Debug -> Start Debugging) if you have the extension project open in VS Code. This will compile the TypeScript, and open a second test VS Code window with the extension loaded.

In the test window, you can open the Command Palette (CMD/CTRL+SHIFT+P), type Hello World, and then hit Enter. This will activate your extension, and run the command implementation, which will display the "Hello World!" notification.

You can also set breakpoints in the extension project window, and step-through the code as you would expect.

If you want to do iterative development like this, you can have the code compile continuously by running npm run watch in the project folder. This means that whenever you make a code change in the extension project, this will re-compile the TypeScript.

All you have to do then, is reload the test window, by running Reload Window from your Command Palette in that window. Almost like doing web development!

Publishing your extension

When you're ready to publish your extension to the marketplace, you will have to create a publisher.

In order to do this, you need a Visual Studio Team Services account. This is because VS Code leverages VSTS to publish to the marketplace.

During the registration process you will have to define a username. The account home page URL will have the format <your-username>

For anyone already registered, and unsure of their username, you can go to, login, and you will see your accounts.

You should then go to your account home page, click on your avatar in the menu top-right, and select Security. Under Personal Access Token you select Add. Fill in the following:

  • Description: vsce
  • Expires In: 1 year
  • Accounts: All accessible accounts
  • Authorized scopes: All scopes

Next click Create Token below. You now have the Personal Access Token which will be used to publish the extension. Keep this safe, as it will only be shown once.

Now you will have to install the publishing tool:

npm install -g vsce  

Then create the publisher by running:

vsce create-publisher <your-publisher-name>  

You will then be asked for a friendly display name, e-mail, and the Personal Access Token we just created.

Remember earlier in the post when I asked you to input whatever publisher in the yo code template generator? If your created publisher name doesn't match the publisher field in the package.json file, you will now have to update it.

If you host the code on Github or similar, you can link back to the repository from the marketplace, by specifying the repository URL in your package.json. For my extension this would be:

  "repository": {
    "type": "git",
    "url": ""

Finally, to actually publish your extension, navigate to your project folder, and run:

vsce publish  

If you want to publish updates later, you can increment the version field in package.json, and then publish. The tool can also do increment and publish in one go if you run:

vsce publish minor  

This follows semver, so you can replace minor with major or patch.

Toggle Format on Save

The very simple extension I wrote about in the introduction is published at the VS Code marketplace as Toggle Format on Save.

You can take a look at the code if you're interested, which does a bit more than just print "Hello World!".

Hope this post has been helpful!

  1. According to StackOverflow's 2017 Developer Survey it places 5th among web developers.

  2. Several more activation events can be defined. You could even use wildcard to activate your extension at startup. Though that is not recommended unless necessary.