Introduction to Vim Code Snippets
Code snippets are one of the most powerful productivity features available to developers using Vim. They allow you to define reusable templates that expand into full blocks of code with a simple trigger word and keystroke. Whether you're writing boilerplate HTML, repetitive function definitions, or complex language constructs, snippets can save you thousands of keystrokes every day.
What Are Vim Code Snippets?
A snippet is a short, predefined piece of text that expands into a larger template when triggered. In Vim, snippets typically work by typing a trigger keyword followed by a expansion key (usually <Tab>). The expanded text can include placeholders, tab stops, mirrors, and even dynamic content generated by shell commands or VimScript expressions.
For example, typing def followed by <Tab> could expand into a complete Python function definition with your cursor positioned at the function name, ready to type.
Why Snippets Matter
- Speed: Reduce typing for repetitive code patterns dramatically.
- Accuracy: Eliminate syntax errors in boilerplate code.
- Consistency: Enforce coding standards across your team.
- Focus: Spend less time on scaffolding and more on logic.
- Customization: Tailor templates to your specific workflow and project needs.
Setting Up a Snippet Engine in Vim
Vim does not include snippet support out of the box. You need a snippet engine plugin. The two most popular options are UltiSnips and vim-snipmate. We'll cover both, with a focus on UltiSnips due to its richer feature set.
Installing UltiSnips
If you use vim-plug, add the following to your ~/.vimrc:
" UltiSnips requires Python support in Vim
Plug 'SirVer/ultisnips'
" Optional: a community-maintained snippet collection
Plug 'honza/vim-snippets'
let g:UltiSnipsExpandTrigger = '<tab>'
let g:UltiSnipsJumpForwardTrigger = '<tab>'
let g:UltiSnipsJumpBackwardTrigger = '<s-tab>'
After adding the lines, run :PlugInstall inside Vim. The vim-snippets repository provides hundreds of ready-to-use snippets for many languages.
Installing vim-snipmate
For a lighter-weight alternative, you can use vim-snipmate:
Plug 'garbas/vim-snipmate'
Plug 'honza/vim-snippets'
SnipMate uses a simpler snippet syntax and is a good choice if you want minimal dependencies.
Writing Your First Snippet
UltiSnips stores snippet files in ~/.vim/UltiSnips/ by default. Each file is named after the filetype, for example python.snippets, javascript.snippets, or html.snippets.
Basic Syntax
The basic structure of an UltiSnips snippet is:
snippet trigger "description" [options]
expanded text
endsnippet
Let's create a simple Python function snippet. Create or edit ~/.vim/UltiSnips/python.snippets:
snippet def "Python function definition" b
def ${1:function_name}(${2:args}):
"""${3:Docstring.}"""
${0:${VISUAL:pass}}
endsnippet
Now, in a Python file, type def and press <Tab>. The snippet expands, and your cursor jumps to function_name. Press <Tab> again to move to args, then to the docstring, and finally to the function body.
Snippet Options
The letter after the description controls behavior. Common options include:
b— Only expand when the trigger is at the beginning of a line.i— Expand anywhere, even in the middle of a word.w— Expand only at word boundaries.A— Auto-expand without requiring the trigger key.r— Treat the trigger as a regular expression.
Advanced Snippet Features
Placeholders and Tab Stops
Placeholders are numbered positions the cursor jumps to when you press <Tab>. The syntax is ${1:default_text}. The ${0} tab stop is special — it marks the final cursor position.
snippet for "For loop" b
for ${1:i} in range(${2:0}, ${3:n}):
${0:pass}
endsnippet
You can also use simple tab stops without default text:
snippet class "Python class" b
class ${1:ClassName}(${2:object}):
def __init__(self${3:, args}):
super().__init__()
${0:pass}
endsnippet
Mirrors
Mirrors duplicate the value of a tab stop at multiple locations. Use the tab stop number without braces:
snippet const "JavaScript const" b
const ${1:name} = ${2:value};
console.log(${1}, ${2});
endsnippet
When you type the variable name at tab stop 1, it automatically appears in the console.log statement as well.
Visual Placeholders
Visual placeholders let you wrap selected text in a snippet. Select text in visual mode, press <Tab> (or your configured trigger), and the selected text is placed at ${VISUAL}:
snippet try "Try/except block" b
try:
${VISUAL}
except ${1:Exception} as e:
print(f"Error: {e}")
endsnippet
Shell Commands
UltiSnips can execute shell commands using backticks. This is useful for inserting dynamic content like dates or usernames:
snippet header "File header" b
# -*- coding: utf-8 -*-
# Created on: `!v strftime("%Y-%m-%d")`
# Author: `!v system('whoami')[:-1]`
# Description: ${1:Description}
endsnippet
The !v prefix tells UltiSnips to evaluate the expression as VimScript. You can also use ! for raw shell output:
snippet date "Current date" b
`date +%Y-%m-%d`
endsnippet
Python Interpolation
One of UltiSnips' most powerful features is Python interpolation. You can embed Python code between `!p and ` markers to generate dynamic snippet content:
snippet prop "Python property" b
@property
def ${1:name}(self):
"""Get ${1/name/_/ }."""
return self._${1}
@${1}.setter
def ${1}(self, value):
self._${1} = value
endsnippet
Here's a more advanced example that generates a list of arguments:
snippet init "Python __init__ with args" b
def __init__(self, `!p
args = []
for i in range(int(t[1])):
args.append(f"arg{i+1}")
snip.rv = ", ".join(args)
`):
`!p
for i in range(int(t[1])):
snip.rv += f"self.arg{i+1} = arg{i+1}\n "
`${0:pass}
endsnippet
When you trigger this snippet and type 3 at the first tab stop, it generates an __init__ method with three arguments and three assignment statements.
Nested Snippets
UltiSnips supports nesting snippets within other snippets. You can expand a snippet while inside another snippet's tab stop. This is useful for building complex structures incrementally:
snippet ifmain "If __name__ == '__main__'" b
if __name__ == '__main__':
${0:pass}
endsnippet
snippet main "Main function" b
def main():
${0:pass}
if __name__ == '__main__':
main()
endsnippet
Snippet Examples by Language
HTML Snippets
snippet html5 "HTML5 boilerplate" b
<!DOCTYPE html>
<html lang="${1:en}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${2:Document Title}</title>
</head>
<body>
${0:}
</body>
</html>
endsnippet
snippet a "Anchor tag" b
<a href="${1:#}">${2:Link Text}</a>
endsnippet
JavaScript Snippets
snippet fn "Arrow function" b
const ${1:name} = (${2:args}) => {
${0:}
};
endsnippet
snippet async "Async function" b
async function ${1:name}(${2:args}) {
try {
${0:}
} catch (error) {
console.error(error);
}
}
endsnippet
snippet fetch "Fetch with async/await" b
const response = await fetch('${1:url}', {
method: '${2:GET}',
headers: {
'Content-Type': 'application/json',
},
${3:body: JSON.stringify(${4:data}),}
});
const ${5:data} = await response.json();
endsnippet
React Snippets
snippet rfc "React functional component" b
import React from 'react';
const ${1:ComponentName} = (${2:props}) => {
return (
<div>
${0:}
</div>
);
};
export default ${1:ComponentName};
endsnippet
snippet useState "useState hook" b
const [${1:state}, set${1/(.)/\u$1/}] = useState(${2:initialValue});
endsnippet
snippet useEffect "useEffect hook" b
useEffect(() => {
${0:}
}, [${1:dependencies}]);
endsnippet
Managing Snippets Across Projects
Filetype-Specific Snippets
UltiSnips automatically loads snippet files based on the current filetype. You can also create snippets for multiple filetypes by separating them with a dot, for example html.css.snippets loads for both HTML and CSS files.
Project-Level Snippets
You can configure UltiSnips to load snippets from your project directory. Add this to your ~/.vimrc:
let g:UltiSnipsSnippetDirectories = ["UltiSnips", "mysnippets"]
" Load project-local snippets
autocmd BufRead,BufNewFile * let &runtimepath .= ',' . getcwd() . '/.vim'
Then place project-specific snippets in .vim/UltiSnips/ within your project root.
Sharing Snippets with a Team
Commit your snippet files to a shared repository or include them in your project's dotfiles. The vim-snippets repository is a great starting point — fork it and customize for your team's conventions.
Best Practices
Keep Snippets Focused
Each snippet should do one thing well. Avoid creating massive snippets that generate entire files unless you use them frequently. Smaller, composable snippets are easier to maintain and combine.
Use Meaningful Triggers
Choose trigger words that are short but memorable. def for function definitions, cls for classes, ifmain for the main guard. Avoid triggers that conflict with common words you type regularly.
Provide Default Values
Always include sensible default text in placeholders. This makes snippets self-documenting and helps you understand what each field expects:
snippet req "Express require" b
const ${1:express} = require('${2:express}');
endsnippet
Test Snippets Thoroughly
After writing a snippet, test it in a real file. Check that tab stops are in a logical order, mirrors work correctly, and Python interpolation produces the expected output.
Version Control Your Snippets
Store your custom snippets in a dotfiles repository under version control. This ensures you never lose your snippets and can sync them across machines.
Don't Over-Snippet
Resist the urge to create snippets for everything. Snippets are most valuable for code you write frequently. If you create a snippet for something you use once a month, you'll forget the trigger and never use it.
Learn from Community Snippets
Browse the vim-snippets repository to see how experienced users structure their snippets. You'll discover patterns and techniques you can apply to your own workflow.
Debugging Snippets
If a snippet doesn't expand, check the following:
- Ensure the snippet file is named correctly for the filetype (run
:set ft?to verify). - Check that the snippet file is in a directory listed in
g:UltiSnipsSnippetDirectories. - Use
:UltiSnipsEditto quickly open the relevant snippet file. - Run
:UltiSnipsResetto reload snippets after changes. - Verify your Vim has Python support with
:echo has('python3')— it should return1.
Conclusion
Vim code snippets are a transformative productivity tool that can dramatically reduce the time you spend on repetitive coding tasks. By investing a few hours in setting up a snippet engine like UltiSnips and writing custom templates for your most common patterns, you'll reap returns every single day. Start with simple snippets for your most frequent boilerplate, gradually incorporate advanced features like Python interpolation and visual placeholders, and keep your snippet collection under version control. Over time, your snippet library becomes a personalized coding assistant that knows exactly how you like to write code, letting you focus on solving problems rather than typing syntax.