Sticky option for custom post types without using custom fields or plugins

First of all, we have to restrict this solution to run only with the custom post types that we target because running it globally will make the default post type have 2 sticky checkboxes.

So, I suggest adding a new support type to the custom post types that we want them to have the sticky option.

Suppose we have the custom post type news, we can run the following function to add a new support type for it, and we can do that with each custom post type that we need it to have the sticky option:

add_post_type_support('news', 'sticky');

Or we can add that directly the supports array in the custom post type register function:

add_action( 'init', 'register_post_type_news' );
function register_post_type_news() {
    $labels = [
        ...,
    ];
    $args = [
        ...,
        'supports' => ['title', ..., 'sticky'],
        ...,
        ],
    ];
    register_post_type( 'news', $args );
}

Now we are ready to run our function(s) safely.

Add the sticky option to the Post Edit page

Add the following code to your functions.php file:

function show_sticky_option__post_edit($post) {
    if ( current_user_can( 'edit_others_posts' ) && post_type_supports( $post->post_type, 'sticky' ) ) {
        $sticky_checkbox_checked = is_sticky( $post->post_id ) ? 'checked="checked"' : '';
        $sticky_span = '<span id="sticky-span" style="margin-left:0"><input id="sticky" name="sticky" type="checkbox" value="sticky" ' . $sticky_checkbox_checked . ' /> <label for="sticky" class="selectit">' . __( 'Stick this post to the front page' ) . '</label><br /></span>';
        echo $sticky_span . '<br>';
    }
}
add_action( 'post_submitbox_start', 'show_sticky_option__post_edit' );

And that’s it! ๐ŸŽ‰ ๐ŸŽ‰ ๐ŸŽ‰

Now you will see a new checkbox before the Publish button that works exactly like the native WordPress sticky checkbox.

Add sticky option to the custom post type edit page

NEXT is more enhancements to:

  • Show this field in the same place where WordPress shows it (under the Visibility section).
  • Add the sticky option to the Quick Edit box.
  • Add the sticky option to the Bulk Edit box.
  • Add a counter for the sticky posts in the subsubsub menu.

And all of that is done by using the same methodology; adding the same original HTML tag of the sticky option in the right place.

Show the sticky checkbox under the Visibility section

Instead of echoing the HTML directly in the Submit box, we can echo a Javascript code that inserts the HTML tag after the Public status in the Visibility section, like this:

function show_sticky_option__post_edit($post) {
    if ( current_user_can( 'edit_others_posts' ) && post_type_supports( $post->post_type, 'sticky' ) ) {
        $sticky_checkbox_checked = is_sticky( $post->post_id ) ? 'checked="checked"' : '';
        $sticky_span = '<span id="sticky-span"><input id="sticky" name="sticky" type="checkbox" value="sticky" ' . $sticky_checkbox_checked . ' /> <label for="sticky" class="selectit">' . __( 'Stick this post to the front page' ) . '</label><br /></span>';
        $is_sticky = is_sticky( $post->post_id ) ? 'true' : 'false';
        echo '
        <script>
        window.addEventListener("load", (event) => {
            document.getElementById("visibility-radio-password").insertAdjacentHTML("beforebegin", `' . $sticky_span . '`);
            if (' . $is_sticky . ')
                document.getElementById("post-visibility-display").innerHTML = "Public, Sticky";
        });
        </script>';
    }
}
add_action( 'post_submitbox_start', 'show_sticky_option__post_edit' );

And here’s the result:

Add sticky option to the custom post type edit page in the visibility section

Show the Sticky Posts Count at the top of the posts list table

Before we can hook into the Quick/Bulk Edit boxes, we need to create a new admin column, and I used the same function to print the sticky posts count that shows the number of the sticky posts of the current post type with a link to filter the posts list to show only those sticky posts.

I have no idea if there’s a better hook to show the sticky posts count at the top of the posts list table. If you know, please suggest.

And I added some Javascript code that will hide the new custom admin column because it’s useless to show it in the posts list table.

And here’s the code:

function add_sticky_column_and_counter($columns, $post_type)
{
    if ( post_type_supports( $post_type, 'sticky' ) ) {
        $columns['sticky'] = 'Sticky';
        $args = [
            'post__in'    => get_option('sticky_posts'),
            'post_type'   => $post_type,
        ];
        $sticky_posts = new WP_Query($args);
        $sticky_count = $sticky_posts->found_posts;
        $sticky_count_li = '
        <li class="sticky">
            | <a href="edit.php?post_type=" . $post_type . "&amp;show_sticky=1">Sticky <span class="count">(' . $sticky_count . ')</span></a>
        </li>';
        echo '
        <script>
            window.addEventListener("load", (event) => {
                let toggle_sticky_column  = document.getElementById("sticky-hide");
                if (toggle_sticky_column.checked) toggle_sticky_column.click();

                let sticky_count_listitem = document.getElementsByClassName("subsubsub")[0];
                sticky_count_listitem.insertAdjacentHTML("beforeend", `' . $sticky_count_li . '`);
            });
        </script>';
    }
    return $columns;
}
add_filter('manage_posts_columns', 'add_sticky_column_and_counter', 10, 2);

Now, we can see the sticky posts count at the top of the posts list table.

Add the sticky posts count

And the new custom admin column is always hidden:

Add a new custom admin column to show the sticky option on the Quick and Bulk edit boxes

We are ready now to add the sticky option to the Quick/Bulk Edit boxes.

Add the sticky option to the Quick Edit box

Now we can hook into the quick_edit_custom_box and insert the HTML of the sticky option in the Quick Edit box, using this code:

function show_sticky_option__quick_edit($column, $post_type) {
    if ( post_type_supports( $post_type, 'sticky' ) ) {
        $sticky_span = '
        <label id="quickedit-sticky" class="alignleft">
            <input type="checkbox" name="sticky" value="sticky" />
            <span class="checkbox-title">' . __( 'Make this post sticky' ) . '</span>
        </label>';
        echo '
        <script>document.getElementsByClassName("inline-edit-status")[0].insertAdjacentHTML("afterend", `' . $sticky_span . '`);</script>';
    }
}
add_action('quick_edit_custom_box', 'show_sticky_option__quick_edit', 10, 2);

And here’s the result:

Add the sticky option to the Quick Edit box

Add the sticky option to the Bulk Edit box

In the same way, we will hook into the bulk_edit_custom_box and insert the HTML of the sticky option in the Bulk Edit box, using this code:

function show_sticky_option__bulk_edit($column, $post_type) {
    if ( post_type_supports( $post_type, 'sticky' ) ) {
        $sticky_span = '
        <label id="quickedit-sticky" class="alignright">
            <span class="title">' . __( 'Sticky' ) . '</span>
            <select name="sticky">
                <option value="-1">' . __( '&mdash; No Change &mdash;' ) . '</option>
                <option value="sticky">' . __( 'Sticky' ) . '</option>
                <option value="unsticky">' . __( 'Not Sticky' ) . '</option>
            </select>
        </label>';
        echo '
        <script>document.querySelectorAll("#bulk-edit .inline-edit-status")[0].insertAdjacentHTML("afterend", `' . $sticky_span . '`);</script>';
    }
}
add_action('bulk_edit_custom_box', 'show_sticky_option__bulk_edit', 10, 2);

And the result is:

Add the sticky option to the Bulk Edit box

The full solution code

If you only need to show the sticky option checkbox on the post edit page, you can add the first piece of code to your functions.php file. But if you need to run the full solution, you can add the rest pieces of code, or create a new php file that holds all these functions and, then, include it in the functions.php file.

Here’s the full solution code that you need with some comments to understand what’s going on:

/**
 * This function shows the sticky option checkbox for the post types that supports 'sticky'.
 * So, to show this checkbox with a certain post type, just add 'sticky' to the 'supports' 
 * array in the post type register function.
 *
 * WordPress will handle saving the value of this field automatically because we used
 * the same HTML structure of the sticky <input> field that WordPress uses.
 *
 * @return void
 */
function show_sticky_option__post_edit($post) {
    if ( current_user_can( 'edit_others_posts' ) && post_type_supports( $post->post_type, 'sticky' ) ) {
        $sticky_checkbox_checked = is_sticky( $post->post_id ) ? 'checked="checked"' : '';
        $sticky_span = '<span id="sticky-span"><input id="sticky" name="sticky" type="checkbox" value="sticky" ' . $sticky_checkbox_checked . ' /> <label for="sticky" class="selectit">' . __( 'Stick this post to the front page' ) . '</label><br /></span>';
        $is_sticky = is_sticky( $post->post_id ) ? 'true' : 'false';
        /**
         * The following JS code does the following (after DOM is ready):
         * - Inserts the sticky option checkbox after Public status item in the
         * Visibility section
         */
        echo '
        <script>
        window.addEventListener("load", (event) => {
            document.getElementById("visibility-radio-password").insertAdjacentHTML("beforebegin", `' . $sticky_span . '`);
            if (' . $is_sticky . ')
                document.getElementById("post-visibility-display").innerHTML = "Public, Sticky";
        });
        </script>';
    }
}
add_action( 'post_submitbox_start', 'show_sticky_option__post_edit' );



/**
 * This function adds a new custom column 'sticky' that's mandatory to hook into
 * the quick and bulk edit modes.
 * This will not be applied only to the post types that support 'sticky'.
 *
 * @param $columns   : The admin columns of the current screen
 * @param $post_type : The post type of the current screen
 * @return $columns  : The new set of columns
 */
function add_sticky_column_and_counter($columns, $post_type)
{
    if ( post_type_supports( $post_type, 'sticky' ) ) {
        // Add a new custom column
        $columns['sticky'] = 'Sticky';
        // Get the count of the sticky posts of the current post type
        $args = [
            'post__in'    => get_option('sticky_posts'),
            'post_type'   => $post_type,
        ];
        $sticky_posts = new WP_Query($args);
        $sticky_count = $sticky_posts->found_posts;
        // The HTML tag of the sticky posts count
        $sticky_count_li = '
        <li class="sticky">
            | <a href="edit.php?post_type=" . $post_type . "&amp;show_sticky=1">Sticky <span class="count">(' . $sticky_count . ')</span></a>
        </li>';
        /**
         * The following JS code does the following (after DOM is ready):
         * - Hides the new custom column 'sticky' if visible on the posts list table
         * - Appends the sticky count <li> element to the 'subsubsub' <ul>
         */
        echo '
        <script>
            window.addEventListener("load", (event) => {
                let toggle_sticky_column  = document.getElementById("sticky-hide");
                if (toggle_sticky_column.checked) toggle_sticky_column.click();

                let sticky_count_listitem = document.getElementsByClassName("subsubsub")[0];
                sticky_count_listitem.insertAdjacentHTML("beforeend", `' . $sticky_count_li . '`);
            });
        </script>';
    }
    return $columns;
}
add_filter('manage_posts_columns', 'add_sticky_column_and_counter', 10, 2);



/**
 * This function adds the sticky option checkbox to the Quick Edit panel.
 * This will not be applied only to the post types that support 'sticky'.
 *
 * @param $columns   : The admin columns of the current screen
 * @param $post_type : The post type of the current screen
 * @return void
 */
function show_sticky_option__quick_edit($column, $post_type) {
    if ( post_type_supports( $post_type, 'sticky' ) ) {
        // The HTML tag of the sticky option checkbox
        $sticky_span = '
        <label id="quickedit-sticky" class="alignleft">
            <input type="checkbox" name="sticky" value="sticky" />
            <span class="checkbox-title">' . __( 'Make this post sticky' ) . '</span>
        </label>';
        /**
         * The following JS code does the following (after DOM is ready):
         * - Appends the sticky option checkbox element after the post 'status' field
         */
        echo '
        <script>document.getElementsByClassName("inline-edit-status")[0].insertAdjacentHTML("afterend", `' . $sticky_span . '`);</script>';
    }
}
add_action('quick_edit_custom_box', 'show_sticky_option__quick_edit', 10, 2);



/**
 * This function adds the sticky option menu to the Bulk Edit panel.
 * This will not be applied only to the post types that support 'sticky'.
 *
 * @param $columns   : The admin columns of the current screen
 * @param $post_type : The post type of the current screen
 * @return void
 */
function show_sticky_option__bulk_edit($column, $post_type) {
    if ( post_type_supports( $post_type, 'sticky' ) ) {
        // The HTML tag of the sticky option menu
        $sticky_span = '
        <label id="quickedit-sticky" class="alignright">
            <span class="title">' . __( 'Sticky' ) . '</span>
            <select name="sticky">
                <option value="-1">' . __( '&mdash; No Change &mdash;' ) . '</option>
                <option value="sticky">' . __( 'Sticky' ) . '</option>
                <option value="unsticky">' . __( 'Not Sticky' ) . '</option>
            </select>
        </label>';
        /**
         * The following JS code does the following:
         * - Appends the sticky option menu element after the post 'status' field
         */
        echo '
        <script>document.querySelectorAll("#bulk-edit .inline-edit-status")[0].insertAdjacentHTML("afterend", `' . $sticky_span . '`);</script>';
    }
}
add_action('bulk_edit_custom_box', 'show_sticky_option__bulk_edit', 10, 2);

Conclusion

As you noticed, adding the same original HTML tag of the sticky option field to the right place will make WordPress use it natively. So no more custom fields or plugins are needed to add the sticky option field to the custom posts types.

AND DON’T FORGET to exclude the default post type in any way to avoid the sticky checkbox duplication. You can add a new support type as suggested above.