github github
stars 95
issues 9
subscribers 2
forks 6



4 months ago


Reduce your boilerplate code the lazy-bones way.

What is esqueleto?

esqueleto.nvim is a lua-based plugin that intends to make the use of templates (or as the Neo/Vim community calls, "skeletons") as easy and straightforward as possible. The plugin provides the following functionality:

  • Template insertion triggered by file type or file name matching.
  • Multiple template directories support.
  • Template insertion prompt.
  • Template creation and modification.
  • Wild-card expansion with user-defined and lua-function wild-card support.


esqueleto.nvim requires the following:

  • Neovim 0.8+

Install esqueleto.nvim with your preferred package manager:

  opts = {},
    config = function()
  { "cvigilv/esqueleto.nvim" },
Plug 'cvigilv/esqueleto.nvim'
call dein#add('cvigilv/esqueleto.nvim')
git clone --depth=1 ~/.vim/bundle/
git clone --depth=1 \

[!NOTE] To make use of the latest version of the plug-in you must configure your favorite package manager to point to the develop branch. But beware, you must expect new functionality, bugs and breaking changes from time to time.

Usage & configuration

Quick start

esqueleto.nvim uses the philosophy of ftplugin for organizing templates, that is, inside the template directory one must organize its templates making reference to its (i) filetype or (ii) file name. As an example, let's assume you have the following structure in you ~/.config/nvim directory:

├── init.lua
└── skeletons
    ├── LICENSE
    │   └── MIT
    └── python

Here, we have a single skeleton directory and two possible triggers for template insertion: python files and files named LICENSE.

To configure and use esqueleto.nvim, we need to tell esqueleto that we want to trigger the insertion for either of this two cases. For that, we add the following to your init.lua:

    patterns = { "LICENSE", "python" },

With this configuration, one will be prompted with the template insertion whenever an empty (i) python file type or (ii) file named LICENSE are create. This configuration will replicate the behavior seen in the video introduction.


The default options of esqueleto are the following:

  -- Standard options
  directories = { vim.fn.stdpath("config") .. "/skeletons" }, -- template directories
  patterns = { }, -- trigger patterns for file creation, file name trigger has priority
  autouse = true, -- whether to auto-use a template if it's the only one for a pattern

  -- Wild-card options
  wildcards = {  
    expand = true, -- whether to expand wild-cards
    lookup = { -- wild-cards look-up table
      -- File-specific
      ["filename"] = function() return vim.fn.expand("%:t:r") end,
      ["fileabspath"] = function() return vim.fn.expand("%:p") end,
      ["filerelpath"] = function() return vim.fn.expand("%:p:~") end,
      ["fileext"] = function() return vim.fn.expand("%:e") end,
      ["filetype"] = function() return end,

      -- Datetime-specific
      ["date"] = function() return"%Y%m%d", os.time()) end,
      ["year"] = function() return"%Y", os.time()) end,
      ["month"] = function() return"%m", os.time()) end,
      ["day"] = function() return"%d", os.time()) end,
      ["time"] = function() return"%T", os.time()) end,

      -- System-specific
      ["host"] = utils.capture("hostname", false),
      ["user"] = os.getenv("USER"),

      -- Github-specific
      ["gh-email"] = utils.capture("git config", false),
      ["gh-user"] = utils.capture("git config", false),

  -- Advanced options
  advanced = {
    ignored = {}, -- List of glob patterns or function that determines if a file is ignored
    ignore_os_files = true, -- whether to ignore OS files (e.g. .DS_Store)

For more information regarding esqueleto.nvim options, refer to docs (:h esqueleto).


As previously showcased, esqueleto.nvim has two types of triggers: (i) file type and (ii) file name triggers. This correspond to the backbone of esqueleto, therefore is essential to correctly understand how the plugin works for proper creation and organization of templates.

esqueleto will prioritize file name over file type templates, because the first are more specific than the later. This means that, for example, if one has a named python template,, and a set of python templates, esqueleto will work as follows:

  • If file created is named, only the template will be prompted for insertion (file name trigger).
  • If file created is named, all the python file type templates will be prompted for insertion (file type trigger).

This is the intended behavior for esqueleto, since one can have, for example, a list of templates that match all python files and link some of this to trigger exclusively when a file with a specific name is found.

So, from the quickstart example, let's create a new trigger for files named First, we will add the trigger to the configuration table:

    patterns = { "LICENSE", "python", "" },

Then, instead of creating a new file, we will create a softlink to the template we already have in the python directory. From this, we should obtain the following structure:

│   └── MIT
├── python
│   ├──
│   └──
    └── template -> ../python/

and now we will trigger template insertion for empty (i) python type, (ii) LICENSE and (iii) named files:

Finally, the command EsqueletoNew provides of a user-friendly way for creating new templates.


esqueleto.nvim supports wild-card expansion for dynamically filling templates with relevant information. The current format for wild-cards is the following:


This wild-cards can be defined by the user under the lookup table found in the wildcards section of the configuration table. This wild-cards can either be (i) static values, e.g. strings, numbers, etc., or (ii) functions. Additionally, a special type of wild-cards are Lua-based function calls, which have the following structure:

${lua:function call}

esqueleto comes with a series of ready-to-use wildcards:

  • File related

    • filename, the current file name
    • fileabspath, the current file absolute path
    • filerelpath, the current file relative path to $HOME
    • fileext, the current file extension
    • filetype, the current file type
  • Date and time related

    • date, current date in YYYYMMDD format
    • year, current year
    • month, current month
    • day, current day
    • time, current time in HH:MM:SS format
  • System related

    • host, current host name
    • user, current user name
  • Github related

    • gh-email, email of GitHub user
    • gh-user, name of Github user

Additionally, a special wild-card exists for cursor placement (denoted with ${cursor}), which moves the cursor to the last instance of this wildcard once the template is written in the current buffer.

[!WARNING] DO NOT OVERWRITE THIS WILDCARD! This wildcard is protected, therefore replacing it in the wildcards look-up table will produce unexpected behaviors.


esqueleto.nvim is in its infancy (expect breaking changes from time to time). I intend on extending this plugin with some functionality I would like for a template manager.

For version 1.0 (currently in development), the following should be implemented:

  • Project specific templates Multiple template directories support
  • Wild-cards
    • Format spec
    • Expansion rules
    • User defined wild-cards
  • Template creation interface

For version 2.0, the following should be implemented:

  • General UI/UX improvements
  • Telescope-based template selector
  • Floating window template selector
  • User customizable prompt UI
  • User customizable insertion rules

Similar plugins

esqueleto.nvim is just on of the template insertion plugins currently available for Neovim. Here are some other proyects that are similar in nature:


Pull requests are welcomed for improvement of tool and community templates. Please contribute using GitHub Flow. Create a branch, add commits, and open a pull request.

Please open an issue for support.