Adding Snap.svg to Vue.js and Nuxt.js Projects

This post may be out of date for what you need… There’s an updated article that deals with adding Snap.svg to projects created with the vue-cli 3.0 and more recent 2.x versions of Vue.js. Click here to read it!

SVG is amazing, and if you’re building any custom vector graphics from your client code, one of the easiest libraries to use is Snap.svg. I’ve used it in a number of projects, including vanilla JavaScript and various transpiling setups including Transcrypt.

I’m trying to go a little more mainstream after wasted years of time on fringe technologies that fell out of favor.

I’m spending a lot of time these days learning Vue.js and really hoping this is going to be a worthwhile long term investment in my skillset. So it was only a matter of time before I found myself needing to get Snap.svg working in my Vue.js projects, which meant some extra fiddling with WebPack.

Getting Snap.svg Working with Vue.js

Out of the gate, there’s some hurdles because Snap mounts itself on the browser’s window object, so if you’re trying to load Snap through WebPack (as opposed to just including it in a project using a conventional script tag), you need to do some gymnastics to get WebPack’s JavaScript loader to feed the window object into Snap’s initialization logic. You can find an overview of the problem in this GitHub issue which illustrates the obstacles in the context of using React, but the issues as they relate to Vue.js are the same.

I’m assuming you have a Vue.js webpack project that you started with vue-cli or from a template that has everything basically running okay, so you’ve already got Node and webpack and all your other infrastructure in place.

For starters, you’ll want to install Snap.svg and add it to your project dependencies, so from a terminal window open and sitting in the directory where your project’s package.json/package-lock.json sit…

npm install --save snapsvg

That will download and install a copy of the Snap.svg source into your node_modules directory and you’ll have it available for WebPack to grab.

Normally you’d be able to use a package installed like this by using an import statement somewhere, and you’d think you could do this in your Vue project’s main.js file, if you start down this path you’ll get the window undefined issue described in that GitHub link above.

The tricky bit though is getting WebPack to load the Snap properly, and to do that we’ll need a WebPack plugin that lets us load as a JavaScript dependency and pass some bindings to it. So, in that same directory install the WebPack imports-loader plugin…

npm install --savedev imports-loader

To tell the imports-loader when it needs to do its magic, we have to add it to the WebPack configuration. I changed my webpack.base.conf.js file to include the following inside the array of rules inside the module object…

 module: {
   rules: [
      ...
       {
       test: require.resolve('snapsvg'),
       use: 'imports-loader?this=>window,fix=>module.exports=0',
       },
      ...
     ]
   },

Now we can load Snap.svg in our JavaScript, but imports-loader uses the node require syntax to load the file. So in our main.js, we can attach Snap.svg by telling WebPack to invoke the exports loader like this…

const snap = require(`imports-loader?this=>window,fix=>module.exports=0!snapsvg/dist/snap.svg.js`);

…and then attach it to our root Vue instance, still in main.js, something like this…

const vueInstance = new Vue( {
 el: '#app',
 snap,
 router,
 axios,
 store,
 template: '<App/>',
 components: { App }
} );

export { vueInstance };

There is some redundancy in that require() call and the way we setup the module resolution in the WebPack configuration. I’m fuzzy about why I seemed to need this in both spots, but it works so I’m running with it. If you have insights they’d be appreciated; let me know in the comments.

Getting Snap.svg Working with nuxt.js

Nuxt requires a slightly different twist, because as you’re aware a typical Nuxt project doesn’t have either a main.js file or a native copy of the WebPack configuration. We need to make the same changes, but just in a slightly different spots.

You need to install both snapsvg and imports-loader just like we did above…

npm install --save snapsvg
npm install --savedev imports-loader

The way we modify the WebPack configuration in a Nuxt project is to create a function that accepts and extends the WebPack configuration from with your nuxt.config.js file…

/*
 ** Build configuration
 */

 build: {
   extend(config, ctx) {
      config.module.rules.push( {
        test: require.resolve('snapsvg'),
        use: 'imports-loader?this=>window,fix=>module.exports=0',
       } );
     }
   }

Since we don’t have a main.js, we need to use a Vue.js plugin to inject shared objects and code into Vue. In your projects plugins folder, create a file named snap.js that contains code to attach a snap object created again using imports-loader…

export default ({ app }, inject) => {
  app.snap = require(`imports-loader?this=>window,fix=>module.exports=0!snapsvg/dist/snap.svg.js`);
}

…and back in your nuxt.config.js file, include this plugin…

plugins: [
   ...
   {src: '~/plugins/snap'},
   ...
],

These approaches seem to work well for me in both a standard Vue.js and Nuxt.js projects, but both of these setups have been cobbled together from reading a lot of other bits and pieces… If you’ve got a better or approach or see a way to clean up what I’ve done, please let me know.

Meanwhile, good luck with your Snap and Vue projects!

 

 

 

 

9 thoughts on “Adding Snap.svg to Vue.js and Nuxt.js Projects”

  1. I followed the instructions for nuxt configuration. I’m receiving this error. Can you tell me how to solve this.

    { ReferenceError: window is not defined
    at Object. (node_modules/snapsvg/dist/snap.svg.js:8635:0)
    at __webpack_require__ (webpack:/webpack/bootstrap 5c9de1bbd3fef07362bb:25:0)
    at module.exports.__webpack_exports__.a (src/plugins/snap.js:2:0)
    at createApp (.nuxt/index.js:159:0)
    at
    at process._tickCallback (internal/process/next_tick.js:182:7) statusCode: 500, name: ‘ReferenceError’ }

    1. Dkppf, I suspect you’re running into an issue where you’re trying to run some Snap.svg code on a server-side rendered page. Because Snap mounts on the browser’s window object, it won’t be available during the server render process. You’ll want to look at wrapping components in “no-ssr” tags in your templates and checking process.browser in any code that directly references the snap instance.

  2. Hi,

    I’ve created a Vuejs project with the most recent vue-cli. So I have no webpack.base.conf.js.
    Do you now how to get it to work?

    Thank you!

  3. I’m just learning vue.js and I followed the instructions for vue.js but I can not figure out how to actually use snap in my components.

    For example a simple component like:

    export default {
    created: function () {
    var s = Snap(‘#mysvg’) // <— Snap is undefined
    s.circle(150,150,100)
    }
    }

    How is this supposed to work with vue.js? Thanks.

  4. Would you mind posting an example code snippet of how to correctly import & use Snap from within a nuxt vue component? Thanks!

  5. I got it to work by;

    nuxt.config.js:
    extend (config) {
    config.module.rules.push({
    test: require.resolve(‘snapsvg’),
    use: ‘imports-loader?this=>window,fix=>module.exports=0’
    });
    },
    plugins: [ {src: ‘~/plugins/snap’, ssr: false}]

    plugins/snap.js;
    export default ({ app }, inject) => {
    inject(‘snap’, require(`imports-loader?this=>window,fix=>module.exports=0!snapsvg/dist/snap.svg.js`))
    }

    component.vue;
    created () {
    if (process.browser) {
    let s = this.$snap(800, 600);
    let bigCircle = s.circle(150, 150, 100);
    bigCircle.attr({
    fill: “#bada55”,
    stroke: “#000”,
    strokeWidth: 5
    });
    }
    }

Leave a Reply to Ralph Cancel reply

Your email address will not be published. Required fields are marked *