Skip to content

How to Share a Draft in WordPress (Without a Plugin)

Easily share unlisted WordPress drafts without plugins or logins using one simple code snippet. Perfect for client reviews and collaboration.

Published
Reading 4 min
By Nick Cesarz
How to Share a Draft in WordPress (Without a Plugin)

I originally built this for a brand whose PR team needed to fact-check product-launch posts before they went live. They didn’t have WordPress accounts (and we weren’t setting them up just for one approval workflow), so the standard options didn’t fit: Private posts hide the page entirely, Password Protected publishes a public URL with a locked door, Draft mode requires a login. The snippet below was the answer. A handful of clients have asked for the same thing since.

Think of it like a YouTube “unlisted” video: not published, not indexed, but viewable to anyone with the link.

I recommend using WP Code if you don’t have a child theme installed on your website. Child themes allow you to make changes to your theme that won’t get overwritten when the developer releases a new update.

Step 1: enable draft sharing with one snippet

By default, WordPress doesn’t allow non-logged-in users to see drafts. But we can change that by injecting a small PHP snippet.

Child theme instructions

For those using a child theme, disregard the following instructions and paste the code snippet below into your theme’s functions.php file and jump to step 2.

Code snippet plugin instructions

For those needing to use a code plugin, install and activate WPCode via the WordPress Plugins repository or from the developer’s website. Then, from the WordPress dashboard sidebar, click + Add Snippet.

Next, click on the button below Add Your Custom Code (New Snippet) and choose PHP Snippet. Paste in the following code (shoutouts to New Pulse Labs for this one).

This code is provided as-is, without warranty. By using it you take responsibility for any issues that come up on your site. Test on a staging environment before deploying live.

The code

<?php 
add_action('pre_get_posts', 'allow_draft_preview');
 
function allow_draft_preview($query) {
    if (isset($_GET['key']) && $_GET['key'] == 'guest') {
        if ($query->is_main_query()) {
            $query->set('post_status', array('publish', 'draft'));
        }
    }
}

Name your snippet, click save, and then toggle the slider to Active to turn the snippet on.

Step 2: share your link

After saving your draft post or page, click Preview in the editor. You’ll get a URL like:

https://yoursite.com/?p=1234&preview=true

Now just replace preview=true with key=guest, like this:

https://yoursite.com/?p=1234&key=guest

Share that link, and your client can now view the draft. No login needed.

The biggest problem with this solution? Having to remember key=guest every time you want to share a draft with a client. So, we’re going to take this a step further.

Step 3: add “copy link” + “open in new tab” buttons (optional, but awesome)

With one more code snippet, we’re going to add a simple UI to the Gutenberg sidebar when editing posts with:

  • A button to copy the draft link
  • A button to open the link in a new tab

I like this tweak and use this to preview my posts over the native Gutenberg preview button. Why? Because the native solution requires two clicks. With this solution, I can click Open in New Tab right from the sidebar. I can also copy the link to the preview and send it right to the client.

Here’s a small HTML + JavaScript snippet you can copy to enable this functionality. Note, if you use Custom Post Types, you’ll need to specify them in the code on the line commented with “// Add CPTs as needed.”

If using WPCode, repeat the process of adding a new snippet and paste the following code below.

Copy/open UI snippet

add_action('add_meta_boxes', function () {
    add_meta_box(
        'sharable_link_box',
        'Sharable Link',
        'render_sharable_link_box',
        ['post', 'page'], // Add CPTs as needed
        'side',
        'high'
    );
});

function render_sharable_link_box($post) {
    $is_published = $post->post_status === 'publish';

    if ( $is_published ) {
        $url = esc_url( get_permalink( $post->ID ) );
        $label = 'Live URL';
    } else {
        $preview_url = get_preview_post_link( $post->ID );
        $url = esc_url( add_query_arg( 'key', 'guest', $preview_url ) );
        $label = 'Preview URL';
    }

    echo '<label style="font-weight: bold; display: block; margin-bottom: 5px;">' . esc_html( $label ) . '</label>';
    echo '<input type="text" readonly value="' . $url . '" style="width:100%; font-size:12px;" id="sharableLinkURL">';
    
    echo '<div style="margin-top:8px; display:flex; gap:8px; flex-wrap:wrap;">';
    echo '<button type="button" class="button" onclick="copySharableURL()">Copy Link</button>';
    echo '<button type="button" class="button button-secondary" onclick="openSharableURL()">Open in New Tab</button>';
    echo '</div>';

    // JS for buttons
    echo <<<HTML
<script>
function copySharableURL() {
    const input = document.getElementById('sharableLinkURL');
    input.select();
    input.setSelectionRange(0, 99999); // For mobile
    document.execCommand('copy');
    alert('URL copied to clipboard!');
}

function openSharableURL() {
    const input = document.getElementById('sharableLinkURL');
    const url = input.value;
    window.open(url, '_blank');
}
</script>
HTML;
}

Once your client is done reviewing the draft, it’s a good idea to disable the snippet or remove the key=guest parameter to keep things secure.

For added protection, you can also use a more unique key, something like key=34dhs82k instead of the default, to make the link harder to guess.

And of course, always test the snippet on a staging site before deploying it live, just to make sure everything works as expected without any surprises.