Ode to VSCode Snippets

7 min readPublished May 24, 2022Updated May 24, 2022

Snippets are templates that make it easier to add the stuff you type all the time. Most often they are used for code you type a lot like for loops or if statements, but they can be any text that you type a lot.

I like to use them for boilerplate code that I find tedious to write. Things like the spec-style functions wrapping my tests (describe() and it() blocks) or a Ruby class definition with # frozen_string_literal: true as the first line.

They also come in handy for making it easy to follow your team's style conventions. If you like to organize your model spec files with sections for associations, validations, instance methods, and class methods, you can write a snippet to set that up. Or if you have conventions for writing your Storybook stories, you can write a snippet to make it easy for everyone on the team to follow.

The nitty-gritty

VSCode provides some snippets out of the box. And there are extensions you can install which will add more to your environment. And even more useful, you can write your own!

You can organize your snippets however you like. VSCode supports global snippets which are available in all languages or on a per-language basis. And you can even write snippets per workspace (though I don't do that very often).

To add a snippet, go to the menu bar and select Code (or File) > Preferences > User Snippets. Then choose the scope of the snippet you want to add.

When you see a token that starts with a $ in the body of the snippet, that's a placeholder. When using a snippet, you can quickly jump to the next placeholder with Tab. Then you can edit the placeholder or jump to the next one. The string after the colon (if any) is the default text, for example, "element" in ${1:element}. Placeholder traversal order is ascending, starting from 1. $0 is an optional special case that always comes last, and exits snippet mode with the cursor at the specified position.

You can find the full snippet docs on VSCode's website.

In this post, I'll show you some of the fun things you can do with user snippets. And you can find all of my favorites in my snippets repo on Github. Please take them and use them! Or feel inspired and write your own!

Basic snippets

Insert text

The simplest snippet you can set up is one to just insert text. I use this for my debugger statements in all my coding languages.

I set up the same prefix (the trigger text) across all my coding languages and let the snippet handle inserting the correct text.

In Javascript, the snippet looks like this.

"debugger": {
  "prefix": "db",
  "body": [
    "debugger"
  ],
  "description": "debugger"
}

Debugger snippet - js

And in Ruby, it looks like this.

"pry": {
  "prefix": "db",
  "body": [
    "require \"pry\"; binding.pry"
  ],
  "description": "debugger"
}

Debugger snippet - ruby

Multiple placeholders for you to add custom text

Besides inserting text, you'll probably want to insert some customizable boilerplate most frequently.

My most used snippets are the describe and it blocks I use in tests. It gives me the wrapping functions and then lets me add the description string.

By using placeholders, I can control where my cursor goes and tab when I'm done typing and want to move to the next location.

For Javascript files (*.js), the snippets look like this

"test describe": {
  "prefix": "describe(",
  "body": [
    "describe(\"$1\", () => {",
    "  $0",
    "})"
  ],
  "description": "jest describe block"
},
"test assertion (it)": {
  "prefix": "it(",
  "body": [
    "it(\"$1\", () => {",
    "  $0",
    "})"
  ],
  "description": "jest it assertion"
}

Multiple placeholders snippet - js

I use those same prefixes to get the same functionality in Ruby (with an extra context block since that doesn't exist in Jest specs). And that looks like this.

"test describe": {
  "prefix": "describe ",
  "body": [
    "describe \"$1\" do",
    "  $0",
    "end"
  ],
  "description": "spec describe block"
}
"test assertion": {
  "prefix": "it ",
  "body": [
    "it \"$1\" do",
    "  $0",
    "end"
  ],
  "description": "spec it assertion"
}

Multiple placeholders snippet - ruby

Intermediate snippets

Use the filename

Sometimes you'll want to use the filename in the body of a snippet.

Let's say you have a React component and you want to fill in your normal functional component boilerplate. By using the $TM_FILENAME_BASE variable, you get the filename without its extension for free. VSCode provides a lot of variables for you to use in your snippets.

"functional component": {
  "prefix": "fc",
  "body": [
    "import React from 'react'",
    "",
    "export function $TM_FILENAME_BASE(props) {",
    "  return (",
    "    $0",
    "  )",
    "}",
  ],
  "description": "boilerplate for a React functional component"
}

Filename snippet

Use the same placeholder in multiple places

We can also use a snippet to insert the same custom text in multiple places.

I use this to nicely format my console log statements. This snippet has multiple placeholders, but the second placeholder's default value is the first placeholder's value which also has a default value. It might make more sense when you see it in action.

"Print to console": {
  "prefix": "log",
  "body": [
    "console.log('$1:', ${2:${1:loggedValue}})"
  ],
  "description": "Log output to console"
}

Same placeholder multiple places snippet

Advanced snippets

Use the filename but mutate it in some way

What if you want to use the filename but change the case? Or use only part of it?

You can do both of those things via variable transforms. There are a few parts to a transform.

  1. The variable or placeholder you want to transform
  2. A regular expression to match against the value of the variable or placeholder (before transformation)
  3. A format string that tells the snippet how to transform the text
  4. Any options you want to pass to the regex (i for case insensitive, etc.)

Each of these parts is separated by a /. This can make reading the regex a bit challenging.

For example, let's look at a snippet that you could use in a test file to give you the boilerplate for a component test. It takes a file named FancyComponent.test.js and gives you FancyComponent in the snippet body.

"testFile": {
  "prefix": "test",
  "body": [
    "import React from 'react'",
    "import { ${TM_FILENAME/(.*)\\.test\\..+$/$1/} } from '../${TM_FILENAME/(.*)\\.test\\..+$/$1/}'",
    "",
    "describe('${TM_FILENAME/(.*)\\.test\\..+$/$1/}', () => {",
    "  beforeEach(() => {",
    "    $2",
    "  })",
    "",
    "  describe('render', () => {",
    "    it('renders correctly', () => {",
    "     $3",
    "    })",
    "  })",
    "})"
  ],
  "description": "boilerplate for a component test"
}

Mutate filename snippet

Let's break down the variable transform, ${TM_FILENAME/(.*)\\.test\\..+$/$1/}

  1. The variable: TM_FILENAME
  2. The regex: (.*)\\.test\\..+$
    • The \\. is confusing but this is a literal .. In the snippet, we need to double-escape the .. This is what it would look like as an unescaped regex (like you might use in code)
  3. The format string: $1
    • This refers to the first capture group. You can do many different capture groups and combine/format them how you need.
  4. The regex options: None

There are also some built-in transformations for you. You can transform the case with upcase, downcase capitalize camelcase, or pascalcase.

"class": {
  "prefix": "class ",
  "body": [
    "# frozen_string_literal: true",
    "",
    "class ${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/g}",
    "  $0",
    "end"
  ],
  "description": "class boilerplate"
}

Mutate filename change case snippet

And we can combine both of those approaches. If we have a Ruby spec file and we want a snippet for the test boilerplate, we can use part of the file name and transform the case all in one go.

The following snippet takes the file name like fancy_model_spec.rb and transforms it to FancyModel.

"vanilla spec": {
  "prefix": "spec ",
  "body": [
    "# frozen_string_literal: true",
    "",
    "require \"rails_helper\"",
    "",
    "RSpec.describe ${TM_FILENAME_BASE/^(.*)_spec$/${1:/pascalcase}/} do",
    "  describe \"$1\" do",
    "    $0",
    "  end",
    "end"
  ],
  "description": "spec boilerplate"
}

Mutate filename in multiple ways snippet

1 placeholder, multiple places with a transform

Building on that, we can also transform our text in just one of the instances of a placeholder that we use multiple times.

This is helpful when we want the same text but to change the case in one of the locations.

"useState": {
  "prefix": "usestate",
  "body": [
    "const [${1:stateName}, set${1/(.)/${1:/upcase}/}] = useState([$2])"
  ]
}

Multiple instances of a placeholder with different transforms snippet

Complex regex transform

Like I alluded to earlier in this post, when you're doing a variable or placeholder transform, the world is your oyster. I used Storybook on a recent project and I wanted to convert the filename, FancyComponent.stories.js into a kebab cased story <fancy-component>. This proved more challenging than I thought so I'm including an abbreviated example here just for fun 🙃

"kebab case example": {
  "prefix": "kebab",
  "body": [
    "<${TM_FILENAME/([A-Z][a-z]*)([A-Z][a-z]*)*\\.stories\\..+$/${1:/downcase}${2:+-}${2:/downcase}/g}>"
  ],
  "description": "kebab case example"
}

Complex regex transform snippet

Snippets are powerful. They can speed up your typing and reduce the number of syntax errors due to missing closing tags. They're also fun to play with and create. Work smarter not harder 🤗

Well-Rounded Dev

Liked this post? Subscribe to receive semi-regular thoughts by email.

    I won't send you spam. Unsubscribe at any time.