Setting up Vue 2 and Foundation 6

I have been fascinated by VueJS for a while now. I played around with React but, honestly, I never got along with either JSX or the CSS-in-JS-which-is-almost-CSS-but-not-really thing. Vue is different, since it lets front-end developers keep on doing front-end development with very little adjustments to their markup and styles. So far, I’ve had the chance to try it in a couple of projects, a Ruby on Rails application and a WordPress website. In both cases, the Vue app was quite simple (i.e. no routing required) and everything went really smooth.

The great thing about Vue is that you can get it to work in an existing project just by including it from a CDN via a script tag. This is probably inappropriate for something complex, but Vue actually works out of the box. Also, it plays really well with jQuery, which is something you need to deal with if you are using a UI framework or a CMS.

I’m about to start a rather large project which will involve four separate single-page applications, and I’ve decided to go with VueJS. I want to exploit Single File Components (.vue files), as they are easily readable and provide a scalable way to structure front-end code.

I really like Zurb Foundation, I’ve used it in a number of projects, big and small. Although using Foundation 6 in production has been a bit rough with releases 6.0 through 6.2, Foundation 6.3 fixed many issues and I consider it a great tool for layout and styling. I decided I had to get a working setup / boilerplate integrating Vue and Foundation before trying this combination for something real.

EDIT: This project was originally started using a vue-cli template based on Webpack 1. The article and the accompanying repo have now been updated to work with the latest Webpack 2 vue-cli template. The original configuration is still available in the webpackv1 branch of the repo

First steps

One really good thing about Vue is its ecosystem, which is kept essential but features some very powerful tools. I started playing around with vue-cli (the recommended way to scaffold a Vue project), using its Webpack template.

DISCLAIMER: I am a complete Webpack noob. There are probably better ways to achieve what I wanted, please leave a comment if you have suggestions.

The CLI is basically a generator with a Yeoman-like interface where you can optionally setup linting (ESLint), unit (Karma + Mocha) and e2e testing (Nightwatch). You can install it with npm i vue-cli -g. After that, vue init webpack my-project lets you choose what to include and scaffold your app. I chose the AirBNB linting rules and, for now, I did not configure testing. I also decided to install Vue Router, to simulate a real-world app. Once the generator was done creating folders and files, I could cd my-project && npm i to install the dependencies, followed by npm run dev to start the development server and confirm everything went alright. In order to be able to use SASS and include Foundation as an external script, a few extra npm modules are needed, you can get them by running npm i sass-loader script-loader node-sass --save-dev. Finally, I installed foundation-sites and jquery (I found out that both --save and --save-dev work here).


CSS

I copied the _settings.scss variables from the npm package into my app folder, to be able to customize the framework. The problem in getting the CSS to work is that, if you @import ‘foundation' in your root component, its mixins and variables won’t be available to child components. The easy solution to this is to import settings and framework in every component, through this file:

However, this is ugly and would lead to a lot of code duplication. A solution to this problem is to use custom Webpack cssLoaders (in build/utils.js), which will require variables and mixins in every component using <style lang="scss">. The framework can be then initialized with @include foundation-everything() (or just the parts you need) in the <style> tag of App.vue.


JavaScript

And now the tricky part: Foundation JavaScript components. There are a few issues that need to be tackled in order to include them in a Vue application:

  • Foundation is not meant to be used with a module system, i.e. you can’t just import Foundation from 'foundation-sites' at the top of your component JS code and use it to instantiate components (I guess the same applies to Bootstrap);
  • In an app with different views, it’s not possible to simply initialize the framework once, because $(document).foundation() will only work for the elements which are currently in the DOM;
  • Changing views in a Vue app will remove some elements from the DOM but leaving orphaned listeners. While this is not an issue with traditional web-apps (a full page reload destroys and re-initializes the framework), it needs to be taken care of in a single-page application, otherwise one gets a lot of weird side-effects (i.e. clicking on a link opens a Reveal modal from a previously visited page).

I looked for strategies to overcome these problems and, indeed, they have been discussed already by a few people. In the end, after playing around for a while, I got things working by combining the solutions proposed in various threads.

First, Foundation JavaScript can be made available by just importing it using Webpack script-loader (together with its dependencies jQuery and What Input?). If you are using ESLint with the AirBNB style guide, you will need to allow this type on inline import (just add 'import/no-webpack-loader-syntax': 0 in the rules section of .eslintrc.js). Please also note that the script-loader syntax is script! in Webpack 1 and script-loader! in Webpack 2.

I tried to use custom Vue directives, which requires adding a v-foundation attribute to the root tag of a Foundation component. While this works for a number of components (i.e. Reveal, Tooltip), I couldn’t get it to function with others (Off-Canvas and Tabs, for instance), namely those without a single root HTML tag. Instead, I decided to go with a Vue mixin, which can be required in single-file Vue components:

I tried avoiding code duplication by declaring this as a global mixin, but that was a rather poor idea: if you have nested components, Foundation will be initialized on both parent and children (you get a warning in console about that). Also, you might not want to include Foundation in every single component of a Vue application. Instead, this mixin can be used when needed, such as in this example, integrating a Foundation slider in a Vue component:

When mounted, this component sets up a listener for a custom Foundation event, in order to have dataValue tracking the handle position. The hidden input in the slider gets updated by Foundation using JavaScript and VueJS is not capable of detecting the change (even with a v-on:change listener). As explained by Evan You himself in a GitHub issue, this is intentional.

This was nearly what I wanted to achieve, but one final thing was still missing, namely the ability to use Foundation JavaScript with nested components. I wanted to use an off-canvas for the main navigation, hence including it in App.vue. Considering that inclusion of the mixin is not a viable option to achieve this (for the above mentioned reasons), I resorted to instantiating the off-canvas using new Foundation.offCanvas(element, options) in the mounted() hook of the component.


Final remarks

This concludes my initial exploration about getting these two technologies to work side by side. I am definitely more interested in Foundation CSS than in its JavaScript. Most likely, I won’t be using the Orbit slider (there are many better alternatives), but it’s good to know that I can use a Reveal modal, if I need to.

There are still aspects which could (and should) be improved. For instance, I did not understand how to load just a small subset of Foundation JavaScript, I only got it working by loading the whole framework. Also, I figured that, when using the script-loader, one should import the minified versions of Foundation and jQuery in order to avoid getting a huge bundle (those dependencies are not minified, apparently. Or I am doing something moronic with the Webpack configuration). Even so, the resulting vendor.js might be too large for some applications (138Kb gzipped). Having said that, I guess this boilerplate should be enough to start building a SPA with Vue and Foundation. You can have a look at the final result by clicking this link, the source code is available here: