-
Notifications
You must be signed in to change notification settings - Fork 6
Api Draft Mode Api
Since: 1.4.0
Draft Mode allows users to create working copies of published pages, edit them safely without affecting the live site, and publish changes when ready.
When a page is published in WordPress, any edits are immediately live. Draft Mode solves this by:
- On-demand draft creation: Click "Create Draft" to start working on a copy
- Seamless editing: Your changes are captured and transferred to the draft
- Safe editing: All subsequent saves go to the draft, not the live page
- Visual indicators: Header controls and bottom banner show you're in draft mode
- One-click publishing: Merge the draft back into the original when ready
- URL preservation: The original URL and SEO value are preserved
- Navigate to any published page in the block editor
- Click "Create Draft" in the sidebar panel or editor header
- You're redirected to the draft editor to continue working
- The "Draft Mode" panel in the sidebar shows you're editing a draft
- A bottom banner indicates you're in draft mode with a link to view the live page
When editing a draft, you'll see:
Editor Header:
- Save Draft link: Appears when you have unsaved changes (saves without publishing)
Sidebar Panel:
- Warning notice: "You are editing a draft version"
- View live page link: Opens the published page in a new tab
- Publish Changes button: Merge changes to the live page (with confirmation)
- Discard Draft button: Delete the draft without publishing (with confirmation)
Bottom Banner:
- Draft indicator: Shows you're editing a draft with the original page title
- View live page link: Quick access to the published version
When your draft is ready:
- Click "Publish Changes" in the Draft Mode sidebar panel (or click the standard "Publish" button - it's intercepted automatically)
- A confirmation modal appears: "This will replace the current live content with your draft changes."
- Click "Publish" to confirm
- Any unsaved changes are saved first
- Your changes are merged into the live page
- The draft is deleted
- You're redirected to the published page editor
To abandon your draft:
- Click "Discard Draft" in the Draft Mode sidebar panel
- A confirmation modal appears: "All changes will be lost and cannot be recovered."
- Click "Discard" to confirm
- The draft is deleted
- You're redirected to the original page editor
- The live page remains unchanged
Since: 2.0.0
When pages have pending changes, logged-in administrators see a preview banner fixed at the bottom of the public site. This lets admins review how draft content looks on the live frontend before publishing.
- Preview mode (default): Draft content is swapped into page output for the current admin. Regular visitors always see published content.
- Live mode: The admin opts out and sees the same published content as regular visitors.
The banner displays:
- Status text: "Preview mode — N pages with changes" or "Live mode — N pages have pending changes"
- Page badge: "This page has changes" (when the current page has a pending draft)
- Toggle button: Switch between "View Live" and "Preview Changes"
- Details panel: Expandable list of all pages with pending changes, with links to edit each one
Admins can click "View Live" to temporarily opt out of preview mode. This sets a cookie (dsgo_live_mode) that expires after 24 hours.
Auto-reset: If a new draft is created after the admin opted out, they are automatically returned to preview mode on their next page load. This ensures admins always see the latest pending changes.
- User must be logged in with
edit_pagescapability - Draft mode must be enabled in settings
- Frontend preview must be enabled in settings (
show_frontend_preview) - At least one published page must have a pending draft
Frontend preview can be independently disabled in the DesignSetGo settings panel under Draft Mode > Frontend > Show Frontend Preview. When disabled, the preview banner does not appear and draft content is not swapped on the frontend. Draft creation, editing, and publishing in the admin still work normally.
-
body.dsgo-has-draft-banner— Added when the preview banner is visible (adds bottom padding to prevent content overlap) -
.dsgo-preview-banner--active— Banner in preview mode (dark background) -
.dsgo-preview-banner--live— Banner in live mode (light background)
Draft Mode can be configured in DesignSetGo settings:
| Setting | Default | Description |
|---|---|---|
enable |
true |
Enable/disable draft mode entirely |
show_frontend_preview |
true |
Show preview banner on the frontend and swap draft content for editors |
show_page_list_actions |
true |
Show Create/Edit Draft row actions in page list |
show_page_list_column |
true |
Show Draft Status column in page list |
Settings are stored in the designsetgo_settings option under the draft_mode key:
$settings = get_option( 'designsetgo_settings' );
$draft_mode = $settings['draft_mode'];Namespace: designsetgo/v1
Base Path: /draft-mode/
All endpoints require authentication with appropriate capabilities:
-
Create/Publish/Discard Draft: Requires
publish_pagescapability (Editors, Administrators) -
Get Status: Requires
edit_pagescapability (Authors, Editors, Administrators) - Object-Level Permissions: Users must have permission to edit/publish/delete the specific page/draft in question
Methods:
- Basic Authentication:
Authorization: Basic base64(username:password) - Application Passwords (WordPress 5.6+)
- Cookie + Nonce:
X-WP-Nonceheader (for admin JavaScript)
Check the draft status of any page. Also returns current settings.
Endpoint: GET /draft-mode/status/{post_id}
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
post_id |
integer | Yes | The page ID to check |
Response (page with no draft):
{
"settings": {
"enabled": true,
"auto_save_enabled": true,
"auto_save_interval": 60
},
"exists": true,
"is_draft": false,
"has_draft": false,
"draft_id": null,
"original_id": null,
"can_create": true
}Response (page has a pending draft):
{
"settings": {
"enabled": true,
"auto_save_enabled": true,
"auto_save_interval": 60
},
"exists": true,
"is_draft": false,
"has_draft": true,
"draft_id": 456,
"draft_edit_url": "http://example.com/wp-admin/post.php?post=456&action=edit",
"original_id": 123,
"draft_created": "2024-01-15 10:30:00"
}Response (this IS a draft):
{
"settings": {
"enabled": true,
"auto_save_enabled": true,
"auto_save_interval": 60
},
"exists": true,
"is_draft": true,
"has_draft": false,
"draft_id": 456,
"original_id": 123,
"original_title": "My Page",
"original_edit_url": "http://example.com/wp-admin/post.php?post=123&action=edit",
"original_view_url": "http://example.com/my-page/",
"draft_created": "2024-01-15 10:30:00"
}Example:
curl -X GET "https://example.com/wp-json/designsetgo/v1/draft-mode/status/123" \
-H "Authorization: Basic $(echo -n 'user:pass' | base64)"Create a draft copy of a published page.
Endpoint: POST /draft-mode/create
Request Body:
{
"post_id": 123,
"content": "<!-- wp:paragraph --><p>Optional content</p><!-- /wp:paragraph -->",
"title": "Optional title",
"excerpt": "Optional excerpt"
}Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
post_id |
integer | Yes | The published page ID |
content |
string | No | Content to use instead of published content (captures unsaved edits) |
title |
string | No | Title to use instead of published title |
excerpt |
string | No | Excerpt to use instead of published excerpt |
Response (success):
{
"success": true,
"draft_id": 456,
"edit_url": "http://example.com/wp-admin/post.php?post=456&action=edit",
"message": "Draft created successfully.",
"draft_title": "My Page",
"original_id": 123
}Errors:
| Code | Message |
|---|---|
draft_mode_disabled |
Draft mode is disabled |
invalid_post |
Post not found |
invalid_post_type |
Draft mode is only available for pages |
invalid_status |
Only published pages can have a draft version |
draft_exists |
A draft version already exists for this page |
Example:
curl -X POST "https://example.com/wp-json/designsetgo/v1/draft-mode/create" \
-H "Authorization: Basic $(echo -n 'user:pass' | base64)" \
-H "Content-Type: application/json" \
-d '{"post_id": 123}'Merge draft content into the original published page.
Endpoint: POST /draft-mode/{draft_id}/publish
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
draft_id |
integer | Yes | The draft post ID |
Response (success):
{
"success": true,
"original_id": 123,
"edit_url": "http://example.com/wp-admin/post.php?post=123&action=edit",
"view_url": "http://example.com/my-page/",
"message": "Changes published successfully."
}What gets merged:
- Post title
- Post content (all blocks)
- Post excerpt
- All post meta (except internal WordPress meta)
- Featured image
Errors:
| Code | Message |
|---|---|
invalid_draft |
Draft not found |
not_a_draft |
This post is not a draft version |
original_not_found |
The original page no longer exists |
Example:
curl -X POST "https://example.com/wp-json/designsetgo/v1/draft-mode/456/publish" \
-H "Authorization: Basic $(echo -n 'user:pass' | base64)"Delete a draft without publishing changes.
Endpoint: DELETE /draft-mode/{draft_id}
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
draft_id |
integer | Yes | The draft post ID |
Response (success):
{
"success": true,
"original_id": 123,
"message": "Draft discarded."
}Errors:
| Code | Message |
|---|---|
invalid_draft |
Draft not found |
not_a_draft |
This post is not a draft version |
Example:
curl -X DELETE "https://example.com/wp-json/designsetgo/v1/draft-mode/456" \
-H "Authorization: Basic $(echo -n 'user:pass' | base64)"Draft Mode uses post meta to track relationships:
| Meta Key | Location | Description |
|---|---|---|
_dsgo_draft_of |
Draft post | ID of the original published page |
_dsgo_has_draft |
Original post | ID of the pending draft |
_dsgo_draft_created |
Draft post | Creation timestamp |
// Fired after a draft is created
do_action( 'designsetgo_draft_created', int $draft_id, int $original_id );
// Fired after a draft is published (merged)
do_action( 'designsetgo_draft_published', int $original_id, int $draft_id );
// Fired after a draft is discarded
do_action( 'designsetgo_draft_discarded', int $draft_id, int $original_id );// Control which meta keys are excluded when creating a draft
apply_filters( 'designsetgo_draft_excluded_meta_keys', array $keys, int $source_id );
// Control which meta keys are preserved on original when publishing
apply_filters( 'designsetgo_draft_preserve_meta_keys', array $keys, int $original_id );For use within the WordPress admin (with proper nonce):
import apiFetch from '@wordpress/api-fetch';
// Get status (includes settings)
const status = await apiFetch({
path: `/designsetgo/v1/draft-mode/status/${postId}`,
});
// Check if draft mode is enabled
if (status.settings.enabled) {
// Draft mode features available
}
// Create draft with content overrides
const result = await apiFetch({
path: '/designsetgo/v1/draft-mode/create',
method: 'POST',
data: {
post_id: postId,
content: currentContent, // Capture unsaved edits
title: currentTitle,
excerpt: currentExcerpt,
},
});
// Publish draft
await apiFetch({
path: `/designsetgo/v1/draft-mode/${draftId}/publish`,
method: 'POST',
});
// Discard draft
await apiFetch({
path: `/designsetgo/v1/draft-mode/${draftId}`,
method: 'DELETE',
});Example workflow for an automated system:
# 1. Check if page has a draft and if draft mode is enabled
STATUS=$(curl -s -X GET "https://example.com/wp-json/designsetgo/v1/draft-mode/status/123" \
-H "Authorization: Basic $AUTH")
# Check if draft mode is enabled
if [ "$(echo $STATUS | jq -r '.settings.enabled')" = "false" ]; then
echo "Draft mode is disabled"
exit 1
fi
# 2. Create draft if none exists
if [ "$(echo $STATUS | jq -r '.has_draft')" = "false" ]; then
DRAFT=$(curl -s -X POST "https://example.com/wp-json/designsetgo/v1/draft-mode/create" \
-H "Authorization: Basic $AUTH" \
-H "Content-Type: application/json" \
-d '{"post_id": 123}')
DRAFT_ID=$(echo $DRAFT | jq -r '.draft_id')
fi
# 3. Edit the draft using WordPress REST API
curl -X POST "https://example.com/wp-json/wp/v2/pages/$DRAFT_ID" \
-H "Authorization: Basic $AUTH" \
-H "Content-Type: application/json" \
-d '{"content": "<!-- wp:paragraph --><p>Updated content</p><!-- /wp:paragraph -->"}'
# 4. Publish when ready
curl -X POST "https://example.com/wp-json/designsetgo/v1/draft-mode/$DRAFT_ID/publish" \
-H "Authorization: Basic $AUTH"- Pages only: Draft mode is currently limited to pages (not posts or custom post types)
- One draft per page: Only one draft can exist for each published page
- No scheduling: Drafts must be published manually (no scheduled publishing)
-
Role requirements: Requires the
publish_pagescapability (typically Editors and Administrators)
Draft Mode is integrated into:
-
Block Editor Header: Context-aware controls positioned at the left:
- For published pages: "Create Draft" or "Edit Draft" link
- For drafts: "Save Draft" link (appears when there are unsaved changes)
-
Block Editor Sidebar: "Draft Mode" panel in the document settings with:
- Draft status indicator with warning/info notices
- View live page link (when editing a draft)
- Publish Changes button with confirmation modal
- Discard Draft button with confirmation modal
- Create Draft button (for published pages without a draft)
-
Bottom Banner: Fixed banner at the bottom of the editor (when editing a draft):
- Visual indicator that you're editing a draft version
- Original page title display
- Quick link to view the live page
-
Page List: "Create Draft" / "Edit Draft" / "View Live" row actions (can be disabled in settings)
-
Page List: "Draft Status" column showing draft state (can be disabled in settings)
When editing a draft, the native WordPress "Publish" button is intercepted to use the custom publishDraft() API instead of the standard WordPress publish action. This ensures:
- Changes are merged back to the original published page
- The draft is deleted after publishing
- The user is redirected to the original page editor
The interception uses a capture-phase event listener to run before WordPress's native handler.
Draft Mode clears the editor's dirty state before navigation to prevent browser "Leave site?" warnings. This is done by resetting the editor's reference blocks to match the current state:
const currentBlocks = wp.data.select('core/block-editor').getBlocks();
wp.data.dispatch('core/editor').resetEditorBlocks(currentBlocks, {
__unstableShouldCreateUndoLevel: false,
});This affects navigation after:
- Creating a draft (redirect to draft editor)
- Publishing changes (redirect to original page)
- Discarding a draft (redirect to original page)
The following CSS classes are added for styling:
-
.dsgo-draft-mode-active- Added to<body>when editing a draft (hides native "Save draft" button) -
.dsgo-draft-mode-save-draft- Header "Save Draft" link styling -
.dsgo-draft-mode-panel- Sidebar panel styling -
.dsgo-draft-mode-banner- Bottom banner styling
Auto-generated from
docs/api/DRAFT-MODE-API.md. To update, edit the source file and changes will sync on next push to main.
- Accordion
- Blobs
- Breadcrumbs
- Card
- Comparison Table
- Countdown Timer
- Counter Group
- Divider
- Flip Card
- Form Builder
- Grid
- Icon
- Icon Button
- Icon List
- Image Accordion
- Map
- Modal
- Modal Api Reference
- Modal Auto Triggers
- Modal Fse Compatibility
- Modal Gallery Navigation
- Modal Next Phase
- Modal Performance Fixes
- Modal Security Audit
- Modal Security Fixes Summary
- Modal Trigger
- Pill
- Progress Bar
- Reveal
- Row
- Scroll Accordion
- Scroll Gallery
- Scroll Slides
- Section
- Slider
- Sticky Sections
- Table Of Contents
- Tabs
- Timeline
- Animation
- Background Video
- Block Animations
- Clickable Group
- Custom Css
- Expanding Background
- Grid Mobile Order
- Grid Span
- Max Width
- Responsive Visibility
- Reveal Control
- Scroll Parallax
- Sticky Header
- Text Alignment Inheritance
- Text Reveal
- Ai Assisted Development
- Best Practices Summary
- Block Controls Organization
- Block Development Best Practices Comprehensive
- Block Exclusion Guide
- Control Reorganization
- Design System
- Wordpress Block Editor Best Practices
- Color Controls Pattern
- Custom Css Filters
- Performance Css Strategy
- Width Css Strategy Implementation
- Width Layout Patterns
- Antigravity Audit
- Card Block Audit
- Claude Audit
- Comprehensive Audit
- Cursor Audit
- Scroll Accordion Stacking Notes
- Security Review 1.2.1
- 2026 02 11 Icon Search Aliases Design
- 2026 02 14 Overlay Header Design
- 2026 02 15 Deactivation Block Migrator Design
- 2026 02 27 Product Categories Grid Design
- 2026 02 27 Product Categories Grid Plan
- 2026 03 07 Icon Aliases Single Source