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

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 }}

Further Reading

Dec 02, 2020 • By Garrett Winder

Donkeytail use cases for Craft CMS 3

Last month we launched Donkeytail, a Craft CMS plugin that allows website administrators to easily place points on images. Below are a few examples of how Donkeytail could be useful for your next website project. Selling multiple products within a single lifestyle image One area where Donkeytail shines is when you want the abi…

Don’t let chaotic web projects get you down.

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

Ready to launch better web projects?
Download our free guide:

3 Reasons Most Digital Projects Get Derailed (And Why Yours Doesn't Have To)