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&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('&')
let params = {}
hashes.map(hash => {
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) => {
// 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 => config.params[key] = params[key])
}
}
return config
},
(err) => {
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.