Andrew Welch · Insights · #craft-3 #preview #frontend

Published , updated · 5 min read ·


Please consider 🎗 sponsoring me 🎗 to keep writing articles like this.

Headless Preview in Craft CMS

Craft CMS 3.2 intro­duced head­less con­tent pre­view. Here’s an explo­ration of how it works, and how you can imple­ment it

Craft CMS has long had a live pre­view” fea­ture that allows con­tent authors to see a pre­view of exact­ly what their con­tent will look like when pub­lished to the web.

With Craft CMS 3.2, one of the major fea­tures added was head­less preview”.

This fea­ture allows devel­op­ers who are ren­der­ing their pages as a Sin­gle Page Appli­ca­tion (SPA) via a fron­tend frame­work like React, Vue.js, Svelte, etc. the abil­i­ty to have Craft CMS con­tent pre­view, too.

Link Why it required a rewrite

Even though nowhere in con­tent man­age­ment sys­tem” is the promise of a ren­der­ing engine, all tra­di­tion­al CMSs don’t just man­age con­tent, but also ren­der it as web pages as well.

A CMS ren­der­ing con­tent was prob­a­bly born out of convenience.

When you use a CMS head­less” you are lop­ping off the part that does the ren­der­ing. Essen­tial­ly, your CMS then man­ages your con­tent, but instead of ren­der­ing it, it pro­vides an API so some­thing else can con­sume it.

The rea­son Craft’s live pre­view” fea­ture worked is that the CMS had con­trol over the whole edit­ing ⟷ pre­view­ing loop.

Now with some­thing else doing the ren­der­ing, that’s no longer the case. So they had come up with a clever solution.

Link Tokenized Preview

The solu­tion the fine folks at Pix­el & Ton­ic came up with is a com­bi­na­tion of auto-saved entry drafts and a token that’s sent along to the web page that is being previewed.

When you click on Pre­view, rough­ly the fol­low­ing happens:

  1. A draft of the entry you’re edit­ing is saved
  2. A token is gen­er­at­ed for that draft, and infor­ma­tion about the draft entry ele­ment is saved to the database
  3. The token is sent along to wher­ev­er the web page hap­pens to be as a token URL param
  4. The web page then sends back that same token with any API requests

It looks rough­ly like this:

Craft CMS head­less pre­view token detail

So why all of this token non­sense? Remem­ber, we’re pre­view­ing an auto-saved draft of the entry that’s being edited.

It’s done this way because the con­tent edi­tor and con­tent ren­der­er no longer share any state, so the saved draft is that state.

The token is what Craft uses to link a pre­view web request to the auto-saved entry draft.

As you’re editing content with preview open, the draft entry is being regularly saved

When a request comes in to Craft that has a token in the URL params, rough­ly the fol­low­ing happens:

  1. Craft looks up the route infor­ma­tion asso­ci­at­ed with the token in the tokens data­base table
  2. In the case of head­less pre­view, the Preview con­troller’s actionPreview() method is called
  3. The auto-saved draft ele­ment that’s being pre­viewed is then added to a list of placeholder elements
  4. When­ev­er an ele­ment query is done that would match any placeholder ele­ments, they are swapped in

This is what that caus­es it all to just work”. Check out the ElementQuery meth­ods _​placeholderCondition() and _​createElement().

Since the token was passed down to the web page that’s being pre­viewed, if it is passed back up to the API that retrieves data from Craft, the place­hold­er ele­ments get mocked in.

Just like magic.

Link Make it so

The Live pre­view using Vue.js post details it pret­ty well in terms of what you need to do to add sup­port on your end.

Essen­tial­ly, it boils down to just extract­ing the token URL param, and send­ing it back to the Craft CMS API end­point, whether that be Ele­ment API, CraftQL plu­g­in, or what­ev­er else you may be using.

Craft CMS head­less pre­view token lifecycle

Here’s some JavaScript that Bran­don Kel­ly posted:

// Get the preview token from the URL
let m = document.location.href.match(/\btoken=([^&]+)/);
let token = m ? m[1] : '';

// Then forward that on whenever you are sending an API request
let url = `...?token=${token}`;
// ...

That’s real­ly all there is to it. Extract the token URL param, and send it back with your API calls.

If you send Craft back the token it sent you, it’ll take care of the rest

The x-craft-preview URL param & request head­er is just a way you can dis­tin­guish the request defin­i­tive­ly as a Craft pre­view, since you might be using the token URL param for oth­er things as well.

There is a token­Param gen­er­al con­fig set­ting if you need to change it to some­thing oth­er than the default of token.

This exact same technique is also used for Share links as well!

If you’re con­cerned about pre­view not remem­ber­ing the scroll posi­tion cross-domain, check out Clive’s ScrollMemNonEs6.js gist.

Hap­py head­less previewing!