How to use Vue to template your Eleventy projects
From the article
Okay quick disclaimer before I kick this off — this post does not cover how to use interactive Vue components in an Eleventy project. This post covers using Vue entirely server-side! The client will not receive any Vue code. All of the code for this project can be found in this repo on GitHub. TL;DR If you’re using Eleventy because it’s lovely, but like using Vue syntax to handle single-file components, template merging, interpolation, etc, I’ve got your back. This post teaches how to start a new project in Eleventy, integrate eleventy-plugin-vue , and deal with any arising quirks or idiosyncrasies. Getting started This tutorial assumes you have Node and NPM installed. If you don’t yet, you can head over here to get that running on your machine. Once that’s in place, we can create a new directory, initialize it as a Node project, and install the two dependencies we need to get cookin’. mkdir eleventy-vue-tutorial && cd eleventy-vue-tutorial npm init # you can use the -y flag to skip the init setup npm install -D @11ty/eleventy@beta @11ty/eleventy-plugin-vue Let’s also create some standard directories for an Eleventy project. I personally prefer to keep my app code in a src directory, and reserve the base for configuration and meta files (like package.json , .eleventy.js , .prettierrc , etc.) but for this tutorial we’ll do as little opinionated configuration as possible! mkdir _data _includes Creating the Eleventy configuration and scripts To get any of this working, we’ll need to add our configuration file to use the Vue plugin in our app. First, we’ll create our .eleventy.js file in the base directory of the project and populate it! const eleventyVue = require ( '@11ty/eleventy-plugin-vue' ) ; // import the plugin module . exports = function ( eleventyConfig ) { eleventyConfig . addPlugin ( eleventyVue ) ; // tell Eleventy about the plugin } ; Eleventy experimental features Here’s the first quirk of this process. At the time of writing, the default installations for eleventy and eleventy-plugin-vue rely on an experimental feature of Eleventy for Custom File Extensions. I’ve gotten around this requirement by installing the @11ty/eleventy@beta package, which implements custom file extensions as a feature. To add the necessary scripts, we’ll go to our package.json file, and add the following: { "scripts" : { "start" : "npx @11ty/eleventy --serve" , "build" : "npx @11ty/eleventy" } } If for some reason you can’t use Eleventy 1.0.0, that’s actually fine, you’ll just edit your scripts to activate the experimental features flag: { "scripts" : { "start" : "ELEVENTY_EXPERIMENTAL=true npx @11ty/eleventy --serve" , "build" : "ELEVENTY_EXPERIMENTAL=true npx @11ty/eleventy" } } Layouts, data files, and Vue templates In this section, you’ll create a layout, specify it as the default layout, and create your first Vue page template. Creating a layout As reported in the eleventy-plugin-vue README.md , you can’t use .vue files as base layouts, so what we’ll do instead is create a layout.html and specify it as the global layout template. This file will use some Nunjucks templating, so, sorry, you can’t escape it entirely (yet) . First, create a file _includes/layout.html , and populate it with the absolute bare minimum code to make this tutorial work! < html lang = " en " > < head > < meta charset = " UTF-8 " /> < meta http-equiv = " X-UA-Compatible " content = " IE=edge " /> < meta name = " viewport " content = " width=device-width, initial-scale=1.0 " /> < title > {{ title }} title > head > < body > {{ content }} body > html > Note those {{ title }} and {{ content }} tags. These will use the Eleventy data engine to inject our page’s HTML from our Vue templates. You can read more about how that works in the Eleventy documentation . Next, we’ll need to tell Eleventy to use that file as the default layout. This is as simple as creating a layout.js in our _data directory and inputting the following JavaScript: module . exports = 'layout.html' ; Adding our first .vue template Now we can get into the actual Vue side of things. Let’s create our first page template, which, for now, will just have some content and set the page title. I’m using Vue 2 for this tutorial, but Vue 3 is supported! I haven’t gone through all of these steps with Vue 3 syntax to find the challenges the new API presents. We’ll create a file index.vue at the base of our app. < template > < article > < h1 > This is a test h1 > < ul > < li v-for = " (listItem, index) in listItems " :key = " index " > {{ listItem }} li > ul > article > template > < script > export default { data ( ) { return { title : "Wow I'm So Excited To Use Vue In My Templates!" , listItems : [ 'This is the first item' , 'This is the second' , 'This is perhaps the third, though who could truly say.' , ] , } ; } , } ; script > The way the eleventy-plugin-vue works is by simply rendering the tag and making that the content data property in the Eleventy cascade, and in the same stroke using the Vue data object as additional page data. Your page should look something like this: Here, we used Vue iteration to turn an array of strings into an unordered list. You’ll even notice that the layout.html file automatically used the title data property as the title of the page. Nice work on this one, really just top notch work. Rendering content and pagination Rendering content from either an API like Contentful introduces some challenges. Let’s create a fake Markdown blog post API response and a post template. In our _data directory, we can create a posts.js file that just exports a JSON array of posts, like what we might expect from a headless CMS API response. module . exports = [ { title : 'This is a test blog post' , slug : 'this-is-a-test-blog-post' , content : '## Subtitle\nLorem ipsum dolor sit amet consectetur adipisicing elit. Repellendus, vero, odit animi praesentium obcaecati autem velit, labore voluptates itaque consequuntur ea reprehenderit quod eveniet nobis perspiciatis neque quas cum voluptatum.' , } , { title : 'This is another blog post' , slug : 'another-blog-post' , content : 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Repellendus, vero, odit animi praesentium obcaecati autem velit, labore voluptates itaque consequuntur ea reprehenderit quod eveniet nobis perspiciatis neque quas cum voluptatum.' , } , ] ; Next, create a file posts/_slug.vue which will be the template for our rendered blog posts. < template > < div > < h1 > {{ post.title }} h1 > < div v-html = " post.content " /> div > template > < script > export default { data ( ) { return { pagination : { size : 1 , data : 'posts' , alias : 'post' , } , permalink : ( data ) => ` posts/ ${ data . post . slug } /index.html ` , eleventyComputed : { title : ( data ) => data . post . title , } , } ; } , } ; script > There’s a couple of things to note in that