testing drafts safely before they go public
Without a clean preview mode, editors are left with an unsatisfying choice: publish a draft blindly or check it locally on their own machine. A well designed preview mode for Nuxt Content makes drafts testable through a protected link, without unpublished content accidentally becoming publicly visible or getting indexed by search engines.
Table of Contents
- 1. Why draft content without preview mode is risky
- 2. The draft field in frontmatter and default filtering
- 3. Activating preview mode: query parameter and state
- 4. Server middleware for preview tokens
- 5. Making drafts visible on the frontend
- 6. Nuxt Studio as an alternative preview solution
- 7. Securing token expiry and access control
- 8. Common pitfalls in practice
- 9. Preview strategies compared
- 10. Summary
- 11. FAQ
1. Why draft content without preview mode is risky
Without a working preview mode, editors often face an unsatisfying trade off: publish drafts directly and hope no one discovers the unfinished page before final review, or start the entire development server locally, which is often not a practical option for non technical team members. Both paths increase the risk of half finished content accidentally going live or getting indexed by search engines before it is even done.
A clean preview mode for Nuxt Content solves this problem by creating protected access to drafts that works independently of the normal publishing status. Through a special link with a token, an editor, a reviewer or a client can see the draft exactly as it will look once live, while regular visitors and search engine crawlers continue to see only published content.
A practical example illustrates the value: a marketing team writes a blog article for a product announcement scheduled two weeks out. Without preview mode, the article would either have to go live prematurely or stay completely invisible until finished. With preview mode, the article can be shared via link with colleagues and stakeholders, exactly in its final layout, without becoming visible to the public.
2. The draft field in frontmatter and default filtering
The foundation for any preview mode in Nuxt Content is a draft field in frontmatter, typed through a content collection schema definition and set to false by default. In normal operation, every query through the query builder automatically filters out entries with draft: true, so unpublished content stays invisible in regular overview pages and through direct URLs.
This default filtering is the first and most important security layer for any preview mode: without a reliable draft field, there is no dependable distinction between published and unpublished content, and every further preview logic builds on this basic distinction. It matters to give the field a clear default value, so new files without explicit configuration are automatically treated as draft or as published, depending on which behavior suits the project better.
// content.config.ts — draft field as the foundation for preview mode
import { defineContentConfig, defineCollection, z } from '@nuxt/content'
export default defineContentConfig({
collections: {
blog: defineCollection({
type: 'page',
source: 'blog/**/*.md',
schema: z.object({
title: z.string(),
date: z.date(),
// New files default to draft until explicitly published
draft: z.boolean().default(true)
})
})
}
})
3. Activating preview mode: query parameter and state
The common way to activate preview mode for a single visit runs through a query parameter in the URL, such as ?preview=true, combined with a shared preview token. A global useState value stores whether the current visit is running in preview mode, and this information then gets taken into account in every content query to include drafts as well.
It matters that this activation happens purely client side or through an explicit server check, and never solely based on the presence of the query parameter without token validation. A preview mode that would give any visitor access to all drafts through a simple URL parameter would not be real protection, only a cosmetic hurdle anyone could bypass by simply trying the URL.
// composables/usePreviewMode.ts
export function usePreviewMode() {
const isPreview = useState('preview-mode', () => false)
function enablePreview() {
isPreview.value = true
}
return { isPreview, enablePreview }
}
// plugins/preview.client.ts — check token on page load
export default defineNuxtPlugin(() => {
const route = useRoute()
const { enablePreview } = usePreviewMode()
// Token validation happens server side, this only reads the result
if (route.query.preview === 'true' && route.query.token) {
enablePreview()
}
})
4. Server middleware for preview tokens
The actual security check for preview mode always belongs on the server, never exclusively in the client. A Nitro server middleware checks every request carrying preview query parameters to see whether the submitted token is valid, usually by comparing it against a secret value stored in environment variables. Only with a valid token does it set a server side cookie or a session flag marking subsequent requests as a preview session.
This approach ensures the actual access protection for preview mode cannot be bypassed by pure client logic that anyone could inspect and manipulate in browser developer tools. The token itself should be sufficiently long and random, ideally with an expiry, so a once shared preview link does not stay valid forever.
// server/middleware/preview.ts
export default defineEventHandler((event) => {
const query = getQuery(event)
if (query.preview === 'true') {
const token = query.token as string
const validToken = useRuntimeConfig().previewToken
if (token !== validToken) {
throw createError({ statusCode: 403, statusMessage: 'Invalid preview token' })
}
// Set a short lived cookie so subsequent requests stay in preview mode
setCookie(event, 'preview-session', 'active', {
maxAge: 60 * 60 * 2, // 2 hours
httpOnly: true
})
}
})
5. Making drafts visible on the frontend
Once preview mode is confirmed server side, the content query on the frontend needs to treat the draft field differently than in normal operation. Instead of filtering out drafts by default, a conditional query condition, based on preview status, also includes entries with draft: true. This distinction should be centrally encapsulated in a composable, so the code does not need to manually branch between the two modes at every location.
Additionally, a visual indicator on the page itself is worthwhile, such as a prominent banner showing that the currently displayed version is an unreviewed draft. This indicator prevents confusion for reviewers who would otherwise not immediately recognize whether they are looking at the already published or the draft marked version of an article.
// composables/useBlogPost.ts
export function useBlogPost(slug: string) {
const { isPreview } = usePreviewMode()
return useAsyncData(`post-${slug}`, () => {
const query = queryCollection('blog').path(`/blog/${slug}`)
// Only bypass the draft filter when preview mode is confirmed active
if (!isPreview.value) {
query.where('draft', '=', false)
}
return query.first()
})
}
6. Nuxt Studio as an alternative preview solution
Alongside a self built preview mode, Nuxt Studio, the visual editor interface for Nuxt Content, offers an alternative solution with an integrated live preview right while editing an article. Editors see changes in real time in the final layout, without needing to build their own preview infrastructure, which is especially attractive for teams without dedicated development resources.
The trade off with Nuxt Studio lies in the dependency on an additional service and a different editorial workflow than directly editing markdown files in the Git repository. For teams already fully committed to Git based workflows with pull requests, a self built preview mode through tokens is often the better fit, because it integrates seamlessly into existing review processes instead of establishing a parallel workflow.
7. Securing token expiry and access control
An often overlooked aspect of securing preview mode is handling expired or compromised tokens. A static, never changing token in an environment variable technically works, but means a once leaked token stays valid indefinitely until manually rotated. For higher security requirements, a time limited, signed token that automatically becomes invalid after expiry is the better choice instead.
Additionally, every preview route should be explicitly given noindex meta tags, so even accidental linking does not let a search engine index the draft version. This double layer of protection, token validation plus noindex, ensures that preview mode does not lead to unwanted public visibility of drafts even with smaller configuration mistakes.
// nuxt.config.ts — ensure preview routes are never indexed
export default defineNuxtConfig({
routeRules: {
'/blog/**': {
headers: {
'X-Robots-Tag': 'noindex'
},
// Only applied conditionally when preview query param is present,
// handled via server middleware for finer control per request
}
}
})
8. Common pitfalls in practice
The most common mistake when implementing a preview mode is checking the token exclusively client side. A preview status stored only in a reactive Vue variable, without server side confirmation, can be manually set by any technically skilled visitor through the browser console, rendering the entire protection mechanism useless. Validation must always happen server side, the client may only display the result of that validation.
A second pitfall is forgetting noindex headers for preview routes, which means an accidentally shared or bot discovered preview link can actually end up in a search engine index. A third mistake concerns Static Site Generation builds: since preview mode requires server side logic at runtime, it does not work in a purely static build without a Node.js runtime, which means projects with preview requirements need at least a server component for the preview route, even if the rest of the site gets statically generated.
9. Preview strategies compared
There are several practical ways to implement a preview mode for Nuxt Content, differing in effort, security and dependencies.
| Strategy | Setup effort | Dependencies | Best for |
|---|---|---|---|
| Custom preview token | Medium | No external services | Git based teams with their own deployment |
| Nuxt Studio | Low | External service required | Non technical editors |
| Local dev server | Minimal | Local environment only | Small teams, technically skilled reviewers |
For most projects with a Git based workflow and multiple reviewers, a custom token based preview mode is the most practical solution, because it requires no additional external service and can be fully tailored to your own security requirements. Nuxt Studio pays off especially when non technical editors regularly maintain content and prefer a visual interface over a Git based workflow.
Mironsoft
Vue.js and Nuxt development with secure content workflows
Need preview mode set up for your Nuxt Content project?
We set up secure token based preview access, protect drafts from indexing and integrate the workflow cleanly into your editorial team's existing review processes.
Preview architecture
Implement secure server side token validation, no client only logic
Indexing protection
noindex headers and access restriction for all draft routes
Editorial workflow
Integrate preview links seamlessly into pull request reviews
10. Summary
A well designed preview mode for Nuxt Content solves the problem of drafts either going live prematurely or staying completely inaccessible to reviewers. The draft field in frontmatter forms the foundation, while server side token validation through Nitro middleware ensures only authorized people with a valid link get access to unpublished content.
noindex headers for preview routes and a time limited token validity round out the protection. For teams with a Git based workflow, a self built preview mode is usually the better fit than an external editor interface, because it integrates seamlessly into existing pull request reviews and requires no additional service dependency.
Nuxt Content Preview Mode — Key Takeaways
draft field
Foundation of every preview logic, filters unpublished content out of all queries by default.
Server middleware
Token validation always happens server side, never manipulable in the client alone.
noindex protection
Preview routes get explicit noindex headers against accidental search engine indexing.
Token expiry
Time limited, signed tokens prevent permanently valid, leaked preview links.