I am currently working on a web project where I have been using Web Components. They are a great way to create customized html elements and limit code duplication. However, this means writing a lot of HTML code inside standard JavaScript files.

The problem

For vim users, this can be a bit annoying because the syntax highlighting is not enabled by default. The HTML code is treated as a regular string:

const template = document.createElement('template')
template.innerHTML = `
  <style>
    .my-component {
      color: red;
    }
  </style>
  <div class="my-component">
    Hello, World!
  </div>
`

class MyComponent extends HTMLElement {
  constructor() {
    super()
    this.attachShadow({ mode: 'open' })
    this.shadowRoot.append(template.content.cloneNode(true))
  }
}

customElements.define('my-component', MyComponent)

The solution

To fix this for vim you can simply add the following lines to your .vimrc:

call plug#begin('~/.vim/plugged')
Plug 'inkarkat/vim-SyntaxRange'
call plug#end()

augroup SyntaxRangeHTML
  autocmd!
  autocmd BufRead,BufNewFile *.js silent! call SyntaxRange#Include('.*HTML.*`', '`', 'html')
  autocmd BufRead,BufNewFile *.js silent! call SyntaxRange#Include('<style>', '</style>', 'css')
augroup END

I looked for a plugin that would handle web components syntax highlighting but it seems that it only exists for VSCode. So I found the next best thing, a plugin that allows a user to partially change a file’s syntax.

Breakdown

This part sources the vim-SyntaxRange plugin using vim-plug. After having changed your .vimrc file and reloaded it (using :source ~/.vimrc), run :PlugInstall to actually install it. You can of course use another plugin manager or install it manually if you prefer.

call plug#begin('~/.vim/plugged')
Plug 'inkarkat/vim-SyntaxRange'
call plug#end()

This part of the configuration will actually enable the syntax highlighting for HTML code in Web Components:

augroup SyntaxRangeHTML
  autocmd!
  autocmd BufRead,BufNewFile *.js silent! call SyntaxRange#Include('.*HTML.*`', '`', 'html')
  autocmd BufRead,BufNewFile *.js silent! call SyntaxRange#Include('<style>', '</style>', 'css')
augroup END

The autocmd triggers when opening a .js file and calls the SyntaxRange#Include function with the following parameters:

  • .*HTML.*` and ` are patterns for the start and end delimiters of the HTML code.
  • html is the syntax group that will be applied to code between the delimiters.

Here it works if the the string HTML is found before the backtick opening the template string. The closing backtick is used as the end delimiter. So in the example above the template.innerHTML = ` line will trigger the HTML syntax highlighting.

These delimiters are specific to my use case, so you might need to adjust them to fit your needs. However the use of the innerHTML property is not mandatory. With this pattern a simple /* HTML */ or /* html */ comment before the backtick will also work.

While I was there I also added CSS syntax highlighting. This is simply triggered by the <style> tag and ended by the </style> closing tag.

Note

The !silent flag is used to prevent the plugin from displaying error messages when the SyntaxRange#Include function is called. A variable is undefined in the script but it does not seem to affect the plugin’s functionality.

Conclusion

This is an acceptable workaround for the lack of built-in support for HTML syntax highlighting in Web Components. It is not perfect but it gets the job done. I hope this helps you as much as it helped me. Happy coding!