Your web browser is out of date. Update your browser for more security, speed and the best experience on this site.

Update your browser

Feb 15, 2019 • By Jake Dohm

Passing your Craft CMS data to Vue.js with Twig

An illustration of physically passing Vue.js from a shopping cart

One of the unique challenges of using Vue JS with Craft CMS is how you get your data from Craft into Vue. Vue's whole model of reactive UI is based on having your data in Vue, and your markup reacting to changes in that data automatically. That means Vue must "own" the data required for your templates and components. But, if your data lives in Craft, how do you get that data into Vue?

There are multiple ways to tackle this problem, and choosing the best way depends on how your project is structured. If your project is a Single Page Application (SPA) you'll (most likely) need to create an API, and make HTTP requests to your API to get your data. But, if your project is a "traditional" server-side rendered website, then creating an API and delaying the render of your markup by waiting for an HTTP request to finish is usually not the best idea.

In this article, we'll cover two ways you can pass your data into Vue using just Twig templates and Vue components.

Method #1: Passing your data into a component using props

Vue components have the ability to receive external data using props, which is convenient when using Vue components on your Craft site. Using this pattern allows you to pass your data from Twig into a component, whether it's an object, array, string, or anything else. Here's an example:

<!-- page.twig -->
{% set fruits = ['apple', 'banana'] %}
{% set fruitEmoji = { 'apple': '🍎', 'banana': '🍌' } %}

 :fruits="{{ fruits | json_encode | raw }}"
 :fruitemoji="{{ fruitEmoji | json_encode | raw }}">
/* page.js */
// create a component, and accept our data with props

Vue.component('emoji-fruit-component', {
 props: ['fruits', 'fruitEmoji'],
 template: '...'

In the example above, we create two sets of data within our Twig templates: fruits (an array), and fruitEmoji (an object). Then we pass that data into our Vue component using props. Then in our JS, when we define our Vue component, we accept fruit and fruitEmoji both as props. Now the fruit and fruitEmoji data will be accessible within our Vue component.

Tip: Use the json_encode filter to escape your data and make it JS-readable, then use the raw filter to allow HTML entities so characters like quotes are printed correctly.

Method #2: Attaching data to the window, then loading it into Vue

Another way to pass your data into Vue from Craft is to assign it as a global variable in JavaScript, then load that data into your Vue component or instance. Consider the following example.

<!-- page.twig -->
{% set fruits = ['apple', 'banana'] %}
{% set fruitEmoji = { 'apple': '🍎', 'banana': '🍌' } %}

 window.craftData = {
 fruits: {{ fruits | json_encode | raw }},
 fruitEmoji: {{ fruitEmoji | json_encode | raw }},

/* fruit-page.js */
// load the data into your root Vue instance
new Vue({
 el: '#vue-app',
 data: window.craftData
// load the data into a single Vue component
 template: '...',
 data() { return window.craftData }

Let me explain what I'm doing in the examples above:

  • First we create an object (craftData) that is attached to the global window object (which is globally available to all scripts).
  • The craftData object contains any data that we need access to in Vue (or a specific component).
  • Then when we instantiate our Vue instance (most likely in a JS file), or component, we tell it to pull the craftData object into our Vue instance as local data.

How to choose which method to use

Now you know two methods to accomplish passing your data into Vue, but which one should you use? Well, it depends.

Here are a few thoughts to help you decide which method to use:

  • If you need your data in the root Vue instance (not a component), you'll need Method #2, because the root Vue instance cannot accept props.
  • If you're not including the Vue template renderer in your bundle, you'll need to use Method #2. If you're unclear on what this means, check out this article on Avoiding DOM Templates:
  • For any other scenario, I'd generally reach for Method #1, because to me it seems simpler and cleaner.

Real world examples

The examples that we looked at above are using hard-coded data, but on your website you'll generally be pulling data from Craft so I wanted to provide some more relevant examples.

Slider (Method #1)

{% set images = [] %}
{% for image in entry.images %}
 {% set images = images|merge([{
 alt: image.title
 }]) %}
{% endfor %}

<vue-slider :images="{{ images | json_encode | raw }}"></vue-slider>

Testimonials (Method #1)

{% set testimonials = [] %}
{% for block in entry.testimonials %}
 {% set testimonials = testimonials|merge([{
 quote: block.quote,
 }]) %}
{% endfor %}

 :testimonials="{{ testimonials| json_encode | raw }}"
 title="{{ entry.title }}">

Blog post (Method #2)

{% set entries = craft.entries.section('blog').all() %}
{% set posts = [] %}
{% for post in entries %}
 {% set posts = posts|merge([{
 url: post.url,
 title: post.title,
 content: post.content
 }]) %}
{% endfor %}

 window.craftData = {
 posts: {{ posts | json_encode | raw }}
An abstract illustration for the Craft CMS Multi-Site feature

Further Reading

Aug 15, 2019 • By Garrett Winder

Use cases for a Craft CMS Multi-Site setup

The team at Good Work is no stranger to Craft CMS, and we love highlighting its great features, both from the development and user side of things. One of my favorite features is called Craft CMS Multi-Site, and allows users to run multiple sites from a single Craft installation. With Multi-Site you can share content (and co…

Don’t let chaotic web projects get you down.

Get a web project — and a development process — that wows.

Sign up for our newsletter.

We send a few emails every month with helpful articles and resources for people who make and manage websites.