Live preview using Vue.js I'm building a Vue.js site with Craft as the backend, which works pretty well so far. The only thing I can't get to work yet is live preview.
Vue-Router relies on the window.location which is flawed in live preview (https://example.com/admin/entries/pages/1-homepage instead of https://example.com/homepage). Is there any way to get this right?

Update: A new issue popped up. About to test things, I noticed the content does not change because - obviously - I pull it from the Element API, which pulls it from the database. I'll update my answer below when I've found a way around this.
Craft 3.2 introduces Headless Preview which works a treat with SPA's. Essentially Craft exposes tokens which you can forward on to subsequent Element API requests and Craft will do the rest. The interesting thing to note with this release is that Live Preview isn't actually "live" rather draft entries that are saved and accessible by tokens generated to identify the entry version.

Your example outlined above is adequate in configuring Vue Router for these previews to work. But we also need to pass on these preview tokens to Element API. These tokens allow Craft to do away with obfuscating URL's loaded in the Live Preview iframe and now load the actual entry URL as you would access it on the actual website.

For example, previously Live Preview would load /admin/entries/282-project-slug in an iframe, it now loads the entry target URL /projects/project-slug?x-craft-preview=XXX&amp;token=XXX. Fantastic!



Detecting Preview Mode

Live Preview loads the entry URL in an iframe with tokens exposed via a query string. These are x-craft-preview and token. We can extract these from the URL either client-side or server-side. The choice is entirely up to you but we just need to grant easy access to these tokens.

Client-side detection example:


let hashes = window.location.search.slice(window.location.search.indexOf('?') + 1).split('&amp;')
let params = {}
hashes.map(hash =&gt; {
  let [key, val] = hash.split('=')
  params[key] = decodeURIComponent(val)
})

// x-craft-preview = params['x-craft-preview']
// token = params['token']


Server-side detection example (Twig):

{% if craft.app.request.isPreview %}
   token = {{ craft.app.request.getQueryParam('token') }}
   x-craft-preview = {{ craft.app.request.getQueryParam('x-craft-preview') }}
{% endif %}



  It's worth noting that Craft 3.2 also deprecates isLivePreview in
  favour of isPreview since Live Preview is no longer programatically
  live rather drafts saved on-the-go and tokenized.




Configuring Vue

For demonstration purposes we will declare the following variables and assume we now have access to the tokens.

const craftPreview = // value of 'x-craft-preview',
const craftToken = // value of 'token'


Set the router mode to mode: 'abstract', for example:

const router = new VueRouter({
  mode: craftPreview ? 'abstract' : 'history',
  ...
})


The router will resolve the preview URL so there's no need to replace the path. If you're still having trouble with this then you can try setting it to see if it resolves your problem...

router.replace({ path: window.location.pathname })


Below is an example of forwarding these params with Axios as our HTTP client.

axios.get('/api/v1/project.json', {
  params: {
    slug: 'project-slug',
    token: craftToken,
    'x-craft-preview': craftPreview
  }
})


Alternatively, you could utilise Axios interceptors and configure params before each request.

axios.interceptors.request.use(
  (config) =&gt; {
    // Forward Craft Preview tokens to API
    if (craftPreview) {
      const params = {
        token: craftToken,
        'x-craft-preview': craftPreview
      }
      if (!config.params) {
        config.params = params
      }
      else {
        Object.keys(params).map(key =&gt; config.params[key] = params[key])
      }
    }
    return config
  },
  (err) =&gt; {
    return Promise.reject(err)
  }
)


Hopefully this steers people in the right direction. As always, huge shout out to the Craft team for making this possible.