Customize your VS Code setup for a project, or for a language

Customize your VS Code setup for a project, or for a language

Every project you work is not the same, thank god! You have different needs for different projects. You may require, or desire, particular settings, scripts, snippets, and extensions to work productively on a project.

Fortunately, VS Code is very customisable and can accomodate you. You can achieve the majority of outcomes without elaborate configuration. I have seen some people create elaborate workflow hacks unnecessarily, usually due to some misconceptions. I want to simplify this process for you.

Let’s start with what you configure!

Configuration items - Settings, Shortcuts, Snippets…etc

When it comes to configuring VS Code up for your general usage, there are some major groups of functionality that you configure. Let’s call them configuration items.

Each of these configuration items end up in a file that lives on your system somewhere. There can be multiple versions of a configuration item, usually this is where there is a global version of a file, and a project version of the file. We will cover this with regard to settings shortly.

Let’s outline the major configuration items:

  1. Settings: These dictate how the editor looks and behaves. Also, extensions that you install can have settings. The settings of extensions are configured in the same file as the editor settings. These settings are stored in a settings.json file.
  2. Keyboard shortcuts (keybindings): VS Code lets you perform most tasks directly from the keyboard. The key combinations that trigger an action can be configured, often referred to as keybindings. These are stored in keybindings.json. You can read the keybindings guide to learn more.
  3. Snippets: A snippet is a template that can be qucikly inserted into a document, usually through some trigger text. Snippets can boost your productivity, saving you keystrokes and reducing input errors. These snippets are stored in individual language files e.g javascript.json for JavaScript. You can read the snippets guide to learn more.
  4. Tasks: Tasks in VS Code can be configured to run scripts and start processes so that popular helper tools can be used from within VS Code without having to enter a command line or write new code. They are stored in a tasks.json file. You can check out the tasks user guide to learn more.

Global versus project configuration

Global configuration is your user configuration. This applies to all instances of VS Code that you open. Depending on your platform, the global configuration items are located in:

Any of the configuraton items mentioned previously will have a file here if you configured one of them as a user.

If you want to have a different configuration for a project, these configuration items are added to <<project-name>>/.vscode.

For a multi-root workspace, a project that contains subprojects, each subproject can have its own configuration items in its .vscode.

Generally, you should favour covering general cases in your global configuration, and only override them for special cases in your projects.

Customize settings

Your settings live in more than one place. It is important to understand where a setting value is derived.

Settings scope - user versus project

VS Code provides different scopes for settings.

The value of a setting option is derived from the following sources in this order, when present:

  1. The project root settings.json e.g. <<project-name>>/.vscode/settings.json.
  2. For a multi-root workspace, the workspace configuration file can store settings e.g. <<project name>>.code-workspace.
  3. Remote user settings file: settings.json, if you are accessing a remote machine.
  4. User settings file: settings.json.
  5. In the absence of a settings file, the application defaults are used.

We will discuss these now.

User settings (global)

These settings apply to all instances of VS Code you open.

Depending on your platform, the user settings file is located here:

Single-folder workspace settings (project)

Typically, a VS Code “workspace” is just your project root folder. You don’t have to do anything for a folder to become a VS Code workspace other than open the folder with VS Code.

You can modify settings for the project and they are stored locally in <<project folder>>/.vscode/settings.json.

Multi-root workspace settings (project/subproject)

You can have more than one root folder in a VS Code workspace through a feature called multi-root workspaces. Instead of opening a folder as workspace, you will open a <<project name>>.code-workspace JSON file that lists the folders of the workspace. For example, Microsoft could organize the vscode project as multi root workspace to contain the product project (source code) and documentation project (documentation website) as subprojects:

{
"folders": [
{
// Source code
"name": "Product",
"path": "vscode"
},
{
// Docs and release notes
"name": "Documentation",
"path": "vscode-docs"
}
]
}

Project-specific settings can be added to the .code-workspacefile in the settings object, like below:

{
"folders": [
{
// Source code
"name": "Product",
"path": "vscode"
},
{
// Docs and release notes
"name": "Documentation",
"path": "vscode-docs"
}
],
"settings": {
"editor.fontSize": 18,
"files.autoSave": "afterDelay"
}
}

Also, you can have a settings.json in each root folder that apply to that subproject i.e. vscode/vscode-docs/.vscode/settings.json for the documentation settings. However, these subproject setting files have limited application, they can only specify resource-related settings such as file settings. This is avoid collisions between subprojects. Settings that affect the entire editor such as UI layout are ignored e.g. two projects cannot both set the font size. These should be set in the .code-workspacefile.

You can read more about this in the settings section of the multi-root workspace docs.

Editing Settings

You can edit settings through:

This is what the Settings UI looks like:

settings ui for a regular project

There is a tab for the scopes discussed above: User and Workspace. As soon as you add a workspace setting, it will create the workspace settings.json file for you.

If it is a multi-root, you will see another tab for the subproject folder you are in. For example, if you are in the documentation subproject, you can see a vscode-docs tab:

settings ui for a mult-root workspace

If you edit the settings JSON directly, it has intellisense for autocompletion.

You can look at this settings list to get more familiar with the options.

You can check out the User and Workspace Settings User Guide to dig deeper.

Make settings specific to language

I have seen some people have the misconception that you need to create an alternative user settings file to make language-specific settings. You can assign a different value for the same setting option for multiples language in your global settings.json. Only one file is required!

To create some language-specific settings, there is a special syntax that can be used in settings files. You specify a language ID as a property enclosed in square brackets, and supply it with an object with the settings as key-value pairs.

For example, if want to have a tabsize of 4 for python, but a tabsize of 2 for other languages. You can have the following in your settings.json:

{
"editor.tabSize": 2,
"[python]": {
"editor.tabSize": 4,
}
}

If you want to specify multiple languages for a set of options, you can provide multiple language IDs, each enclosed in individual square brackets. This is unintuitive, but thats the way it is!

If we want to modify our previous example to have the tab size of 4 for python and java, you do the following:

{
"editor.tabSize": 2,
"[python][java]": {
"editor.tabSize": 4,
}
}

You can change any setting you want on a per language basis in this way. However, it is worth considering that a feature or extension may have a setting that targets specific languages. This is usually the case for linters and formatters.

One builtin example is Emmet. By default, Emmet is enabled for html, haml, pug, slim, jsx, xml, xsl, css, scss, sass, less and stylus files. If you want to enable it for more languages, you need to set the following:

{
"emmet.includeLanguages": {
"vue-html": "html",
"javascript":"javascriptreact"
}
}

This enables Emmet for Vue and React. Again, weird syntax but it works!

There is a bug for including Emmet support for markdown. The workaround is ensure that the excluded language list is empty, as per snippet below.

{
"emmet.excludeLanguages": [],
"emmet.includeLanguages": {
"markdown": "html"
}
}

Another example is the formatter Prettier. For example, you may want to use Prettier as your formatter for most languages, but not for markdown. Prettier does not do a great job with markdown, so you may want to consider it! 😅

This is what it would look like in the settings.json:

{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[markdown]": {
"editor.defaultFormatter": "robole.marky-formatter"
}
}

Alternatively, you could disable prettier for markdown through Prettier’s disableLanguages settting.

{
"prettier.disableLanguages": ["markdown"]
}

What about configuring extensions per project or per language?

I was reading a post entitled Make VSCode lighter recently, and one of the suggestions was to disable extensions for specific workspaces. KR said the following:

I’ve over 35 extensions installed. But most of the time I don’t need all of them at one project as I work in multiple types of project. You can enable/disable extensions for specific workspaces. E.g. you don’t need flutter/dart while developing React or Nodejs Server or vice-versa. So you can just disable that just for that workspace. But don’t forget to save the vs-code workspace

It is not necessary to disable extensions most of the time. Extensions are conditionally loaded based on their Activation Events. If the extension author is diligent an extension should is only loaded when it is required.

For example, if I open a Java project, I do not expect a JavaScript-related extension such as Vetur to be loaded. Do you?

You can see a full list of loaded extensions by running the command Developer: Show Running Extensions, as per screenhost below.

view of running extensions

I discuss this topic further in the article, VS Code - How many extensions should I use? if you would like to learn more about extension activation.

However, there are some valid exceptions to this rule. You will find that some linters such as ES Lint are always loaded because they can be configured to target different languages or file patterns. You can read the GitHub issue to get the full discussion.

Therefore, you may want to disable ES Lint in non-JavaScript projects! It is a bit of a nuisance, but not a big deal.

In v1.7.6 (February 2023), VS Code added profiles that can be used to create a custom configuration set. You can could create a profile that has a reduced set of enabled extensions for regular usage.

Final thoughts

VS Code is very customisable. You should not need to go outside of VS Code to manage your workflow. I hope that I was able to demonstrate that you can have different settings for projects and languages without elaborate configuration.

You can have global configuration, and a specific project configuration. These are separate sets of files. If you have language-specific settings, you can put that in these files.

Profiles were added to VS Code in February 2023, which give even more granular control of configuration. This is the last piece of the puzzle in my opinion if you have some custom contexts or workflows. I cover this in a more recent article – VS Code Profiles - Manage configurations easily for different environments and workflows – if you want to learn more.

Even with the addition of profiles, I would favour global and project configuration when it makes sense. Keep it simple to reduce the need to manage your configuration!

Tagged