passing props to a root Vue component from its surrounding html

Vue is a great framework for making an entire client-side web applications—it's also great for adding interactivity to parts of existing pages. Indeed, I got started using the framework by adding some reactivity to existing static prototypes.

Here's a pretty trivial Vue component...

// App.vue
<template>
  <h1>{{title}}</h1>
</template>

<script>
  export default {
    props: ['title'],
  }
</script>

... and the corresponding code that initializes it, mounting it within the DOM, replacing the content of the `#app` element:

// main.js
import Vue from 'vue';
import App from './app';

new Vue({
  render: h => h(App),
}.$mount('#app');

Now, I'd like to pass information from the surrounding HTML into the `App` component. One way to do this would be to place attributes on the mountpoint, like this.

// index.html
<html>
  <body>
    <div id="app"
      data-title="Hello, world!"
    >
    </div>
  </body>
</html>

Unfortunately, the element is wiped out when the Vue instance is created, so we can't do something like access $el or $parent and just read the attributes out since they are no longer present in the DOM. But by rewriting the render function, we can read attributes and pass them as props into the app (note that commonly h is used as a shorthand for createElement for some reason):

// main.js
import Vue from 'vue'
import App from './App.vue'

const mountEl = document.querySelector('#app');

new Vue({
  render: createElement => {
    const context = {
      props: { ...mountEl.dataset },
    };
    return createElement(App, context);
  }
}).$mount('#app');

Take care with types here: HTML attributes come as strings and so you may need to parseFloat or whatnot to ensure they are read in the form that you expect.

Case study: integrating Vue with Shopify theme sections

For a client project, I wanted to build interactive visualizations using d3, but I wanted my partners to be able to update underlying data and customize options without needing to work with code.

Unfortunately, in Shopify you're mostly limited to editing the HTML generated by the liquid templating language.

The above approach enabled the following workflow to work:

  1. In a shopify theme's schema section, I define some properties that the admin should be able to update, such as the width and height of the content.
  2. In the template itself, I render the properties into data properties on elements that I will use as mountpoints.
  3. The template itself also imports the Vue app javascript as an asset. That looks for the elements parametrized with the user data and instantiates interactive elements wherever those appear.

I suspect that this approach will be useful in many other higher-level authoring environments, like Wordpress or Squarespace, where it's easy to output user data into HTML and then have imported Javascript pick it up.