Skip to content

Instantly share code, notes, and snippets.

@jbmoelker
Created December 10, 2021 08:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jbmoelker/2807b10f3f1dd72dea8df5a45545db95 to your computer and use it in GitHub Desktop.
Save jbmoelker/2807b10f3f1dd72dea8df5a45545db95 to your computer and use it in GitHub Desktop.

Preview Mode plugin

Extends Nuxt's built-in preview mode:

  • 🚪 Enables preview mode through routing (?preview=true)
  • 🔒 Secures preview mode with a (client-side) secret (&secret=...)
  • 👉 Enables redirect when entering preview mode (&location=/...)
  • 🧠 Persists preview mode through routing & page refresh using storage
  • 🎛️ Provides programmatic way to enter() and exit() preview mode
  • 🍫 Provides <PreviewModeBar> component to display and control preview mode

Installation

...

Usage

...

/**
* @type {import('@nuxt/types').Plugin}
*/
export default function (context, inject) {
const { $config, enablePreview, error, query, redirect, route } = context
const { location = route.path, preview, secret } = query
const pluginName = 'previewMode'
// The `window.onNuxtReady(fn)` are needed to avoid hydration issues:
// @see https://github.com/nuxt/nuxt.js/issues/4491#issuecomment-648979464
const onReady = fn => window.onNuxtReady(() => fn())
// Feature check for Local Storage API
// @see https://mathiasbynens.be/notes/localstorage-pattern
const storage = (() => {
// eslint-disable-next-line new-parens
const uid = new Date
let storage
let result
try {
(storage = window.localStorage).setItem(uid, uid)
// eslint-disable-next-line eqeqeq
result = storage.getItem(uid) == uid
storage.removeItem(uid)
return result && storage
} catch (exception) {}
})()
const enterPreview = (data = {}) => {
if (storage) {
storage.setItem(pluginName, JSON.stringify(data))
}
enablePreview(data)
}
const exitPreview = () => {
if (storage) {
storage.removeItem(pluginName)
}
window.location.reload()
}
inject(pluginName, {
enter: enterPreview,
exit: exitPreview,
})
if (storage && storage.getItem(pluginName)) {
const data = JSON.parse(storage.getItem(pluginName))
return enterPreview(data)
}
if (preview !== 'true') {
// Not in preview mode, so we don't need to do anything.
return
}
if (!$config.previewModeSecret) {
return onReady(() => error({
statusCode: 500,
message: 'Set publicRuntimeConfig.previewModeSecret to enable preview mode.',
}))
}
if (secret !== $config.previewModeSecret) {
return onReady(() => error({
statusCode: 401,
message: 'Invalid secret.',
}))
}
enterPreview()
return onReady(() => redirect(location))
}
<template>
<ClientOnly v-if="$nuxt.isPreview">
<div role="alert">
<span>Preview mode: {{ statusMessage }}</span>
<button type="button" @click="exitPreview">
Exit preview
</button>
</div>
</ClientOnly>
</template>
<script>
export default {
computed: {
status () {
return this.$datocms.state.status
},
statusMessage () {
return this.$datocms.state.error
? 'An error occurred.'
: this.$datocms.state.statusMessage
},
},
methods: {
exitPreview () {
this.$previewMode.exit()
},
},
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment