300,000+
October 19, 2026
July 24, 2013
Simple Custom Post Order is the easiest way to manually sort your WordPress content using drag-and-drop functionality. Whether you’re managing blog posts, portfolio items, WooCommerce products, team members, testimonials, or any custom post type, this plugin gives you complete control over your content order.
Sort Any Content Type
Intuitive Admin Interface
Smart Query Integration
orderby => title (see the FAQ for the one get_posts() exception)get_posts(), WP_Query, and standard loops'scpo_ignore_custom_order' => trueReset & Restore
Portfolio Management
Arrange your portfolio items to showcase your best work first, group similar projects together, or create a visual narrative of your creative journey.
Team Page Organization
Display team members by hierarchy, department, or seniority rather than by when they were added to the system.
Product Highlighting
Feature seasonal items, bestsellers, or new arrivals at the top of your WooCommerce shop without relying solely on sorting by date or price.
Content Curation
Create curated reading lists by manually ordering posts in the exact sequence you want readers to discover them.
FAQ Organization
Sort frequently asked questions by importance or topic, ensuring the most relevant questions appear first.
Testimonial Display
Show your most compelling testimonials first to maximize their impact on potential customers.
Action Hooks
scp_update_menu_order – Fires after post order is updated, by drag-and-drop or the Order column (no arguments)scp_update_menu_order_tags – Fires after taxonomy term order is updated (no arguments)Filter Hooks
scpo_post_types_args – Modify which post types appear in settingsscpo_use_sortablejs – Force the drag engine in code (overrides the setting); return true for the modern SortableJS engine or false for the classic jQuery UI sorterscpo_capability – Change the capability required to reorder (default edit_posts)scpo_reverse_adjacent_posts – Return true to swap the direction of previous/next post linksscpo_cache_flush_group_threshold – Number of changed rows (default 500) above which cached order values are cleared in bulk instead of one by oneExample — let only editors and administrators reorder:
add_filter( 'scpo_capability', function () { return 'edit_others_posts'; } );
Drag & Drop Engine
Choose between the modern, dependency-free SortableJS engine (default) and the classic jQuery UI sorter under Settings > SCPOrder. The classic engine remains fully supported as a fallback.
Advanced View Mode
Enable the advanced view in settings to see all registered post types, including those normally hidden from the admin menu.
Simple Custom Post Order works with:
show_ui enabled that appears in the admin menu (turn on Advanced View in Settings to list the rest)show_ui enabledThe plugin uses WordPress’s native menu_order field for posts and pages, and adds a term_order column for taxonomy terms. This approach ensures compatibility and data persistence.
For support questions, please use the WordPress.org support forum.
Report bugs on our GitHub repository.
Contributions are welcome! Please submit pull requests to our GitHub repository.
This plugin is made with love by the team at Colorlib.
From 2.8.10, safe reordering requires InnoDB for the posts table and, when taxonomy sorting is enabled, the terms table. If a table uses another engine, ask your hosting provider to migrate it after taking a backup. The plugin reports unsupported storage and does not convert tables automatically. Since 2.8.11 the error only appears when the database positively reports a non-transactional engine such as MyISAM; if your host does not reveal the engine, saving proceeds normally.
Yes! Simple Custom Post Order works with any custom post type that has show_ui enabled. This includes post types from popular plugins like WooCommerce, Easy Digital Downloads, Jetpack, and any custom post types you’ve created.
Yes, the custom order is automatically applied to all front-end queries for enabled post types. Your posts, pages, and custom content will display in your chosen order throughout your site.
Yes, as long as the page builder uses standard WordPress queries, your custom order will be respected. Most major page builders are compatible.
Yes. Go to Settings > SCPOrder and use the “Reset Post Order” section at the bottom of the page. Select the post types you want to reset and click “Reset Order”.
Be aware that this sets the order number of every item of those types back to 0 — including values typed into Page Attributes > Order and WooCommerce’s own product order — and turns sorting off for those types. It cannot be undone. Taxonomies cannot be reset from here.
No, the plugin automatically preserves WordPress’s relevance-based sorting for search results. Your search functionality will work exactly as expected.
Yes, you can use Simple Custom Post Order to manually sort WooCommerce products, product categories, and product tags.
No, the plugin is highly optimized. It only loads scripts on relevant admin pages and uses efficient database queries. There is no impact on front-end performance.
Yes. Modern and Classic drags send the list’s original order, and numeric moves send the item’s original value. If these are stale, the plugin rejects the save and asks you to reload before reordering again. Older integrations that omit these fields retain their legacy behavior. See the recovery details below.
Yes, Simple Custom Post Order is compatible with WordPress multisite installations. Each site can have its own ordering configuration.
Simply go to the post list for any enabled post type (Posts, Pages, Products, etc.), click and hold on a row, then drag it to the desired position. The new order is saved automatically.
Yes. On phones and tablets, press and hold a row for a moment, then drag it to reorder. Normal taps and vertical scrolling continue to work as expected.
Yes. Tab to a row’s drag handle, press Space (or Enter) to pick it up, use the Up/Down arrow keys (or Home/End) to move it, then press Space to drop or Escape to cancel. Each step is announced for screen-reader users.
Yes. Go to Settings > SCPOrder and set the “Drag & Drop Engine” to “Classic”. Developers can also force it in code with the scpo_use_sortablejs filter (return false for the classic engine). The modern engine is the default and recommended for most sites.
Yes. As of 2.7.0 the save request is always sent same-origin, which fixes silent save failures behind reverse proxies, load balancers, non-standard ports, and HTTP/HTTPS or domain mismatches. Expired security tokens now also refresh and retry automatically.
New drag requests include the order displayed before the move. If another request changed that order, the save is rejected and the Modern engine offers “Reload list”. Numeric moves also check the item’s original order value. Requests from older integrations without these fields retain their legacy behavior.
Drag and numeric order saves use InnoDB transactions: a failure in one batch rolls back the operation’s earlier batches. Existing transactions are preserved with savepoints. The relevant posts or terms table must use InnoDB; the plugin reports unsupported storage and never converts tables automatically. An interrupted connection during commit can leave the outcome uncertain, so reload before retrying. Reset Order remains a separate operation; these transaction guarantees do not cover its settings update.
The Modern engine warns before leaving while an order is unsaved. Once saving succeeds, the warning is removed.
Yes. With WP_Query, a query for an enabled post type that sets its own orderby (for example 'orderby' => 'title', 'order' => 'DESC') keeps it. A query without an orderby gets the custom order.
get_posts() is different: it fills in `orderby => date, order => DESC` before the plugin sees the query, so the plugin can't tell what you actually asked for. It gives the default date sort the custom order and, for compatibility, turns a descending sort ascending — except on the date columns `post_date`, `modified` and `post_modified`. So to get the newest items first with `get_posts()`, use `'orderby' => 'post_date'`; for any other descending sort, use `WP_Query`.
To bypass this plugin’s automatic post ordering for one query, pass 'scpo_ignore_custom_order' => true. This works with both WP_Query and get_posts(), including descending title or date sorts:
$posts = get_posts( [
'post_type' => 'post',
'orderby' => 'title',
'order' => 'DESC',
'scpo_ignore_custom_order' => true,
] );
The opt-out leaves the query’s ordering arguments untouched; it does not change saved order values or other queries. Omit it to retain the existing automatic ordering behavior.
For taxonomy terms, get_terms() and similar calls get the custom order for the usual sorts (name, slug, ID, WooCommerce’s category order). Sorts that mean something else are respected: count, include, slug__in, name__in, parent, term_taxonomy_id, meta values and none. Clicking a column heading in the admin always wins.
WordPress’s own previous/next functions already follow the custom order for every enabled post type, pages included. Many themes only print those links on blog posts, though. In a block theme, add the Previous Post and Next Post blocks to the relevant template (Appearance > Editor > Templates). In a classic theme, call the_post_navigation() in the template, or add the links under every page’s content from your child theme’s functions.php:
add_filter( 'the_content', function ( $content ) {
if ( ! is_page() || ! in_the_loop() || ! is_main_query() ) {
return $content;
}
return $content . get_the_post_navigation();
} );
For pages, the links follow the order shown under Pages > All Pages: each parent, then its children. Use the scpo_reverse_adjacent_posts filter if “previous” and “next” come out the wrong way round for you.
Drag-and-drop works within the page you are looking at. To move an item further, turn on the “Order column” option in Settings > SCPOrder and type the position you want into the item’s Order box. The Order column is not available for hierarchical types such as Pages.
Those views are sorted by something other than your custom order (last modified date, search relevance), so dragging there would scramble it. Reorder from the “All” view instead. The same applies after clicking a column heading to sort by it.
Dragging a parent page moves its child pages with it. With the keyboard, a page moves among its own siblings only. Reordering never changes a page’s parent — use Quick Edit or the page editor for that.
Deactivating leaves everything in place, so reactivating restores your order. Deleting the plugin removes its settings and the term_order column it added for taxonomy ordering, so term order is lost. Post and page order values (menu_order) are WordPress’s own and are left as they are.
Bug fixes
SHOW TABLE STATUS and only refuses when the database positively reports a non-transactional engine such as MyISAM.Safer saves and recovery
Developers and compatibility
scpo_ignore_custom_order query argument for WP_Query and get_posts() to bypass automatic post ordering without changing saved values or existing defaults.Bug fixes
wp_get_recent_posts() — used by many “recent posts” widgets and blocks — returning the oldest posts of a sorted post type. A descending sort on a date column is now respected by get_posts() too.orderby => count (and other meaningful term orders such as include, slug__in and meta values) is now respected. WooCommerce product categories keep following your custom order.is_search() when queries ran before the main one.get_the_terms() (used by most themes to list a post’s categories and tags) on sites with a persistent object cache.max_input_vars limit).term_order column is now only recorded as installed once it really exists. If the database refused to add it, term queries keep working unsorted instead of failing.Improvements
term_order column that another plugin created.Bug fixes
Note: full parent/child ordering (keeping a drag within its own level) is still to come.
Bug fixes
Housekeeping
Bug fixes
Improvements
Bug fixes
Performance
Performance
refresh()) ran on every admin page — Dashboard, Plugins, Tools, Settings — even those that never display a sortable list. On large sites it re-numbered thousands of rows one query at a time, adding thousands of database queries and several seconds of load time to unrelated screens (one report measured ~9.5s and 14,000+ queries on the Plugins page with ~3,000 posts). It now runs only on the post/taxonomy list screens where the manual order is actually shown, and renumbers rows in a single batched query instead of one-per-row. No change to how ordering behaves. Reported by @crossy.Security
Bug fixes
Bug fixes
Improvements
scpo_reverse_adjacent_posts filter to reverse the direction of the previous/next post navigation links for manually-ordered posts and custom post types. The 2.7.2 fix (#146) made “previous” the item before the current one in the arranged order and “next” the item after — correct for sequential content, but the opposite of what sites built around WordPress’s native chronological convention expect. Return true from the filter to restore the pre-2.7.2 direction without editing your theme’s template tags. Reported by @sarahmelyne.New features
scpo_capability filter for developers. Props @mplusb (#95, #133).Bug fixes
Bug fixes
menu_order correctly and account for the date/ID tiebreaker WordPress adds to the query. Props @beatricelucaci (#146).Bug fixes
menu_order is re-normalized after gaps appear (e.g. after deleting an item). The gap-compacting step relied on a MySQL user-variable ranking whose evaluation order is undefined on those databases; re-numbering is now done deterministically in PHP. Props @alexgw and @sebastiencyr (#147 / #119).orderby=include are now respected instead of being overridden by the custom term order. Props @glebkema (#67 / #66).Improvements
orderby as a fallback tiebreaker. Props @goaround (#104).Modern Drag-and-Drop Engine (SortableJS)
Reliability
New Settings (Settings > SCPOrder)
Accessibility
prefers-reduced-motion settingPerformance & Build
SCRIPT_DEBUG is enabledDeveloper
scpo_use_sortablejs — force the drag engine on/off in code (overrides the setting)scp_update_menu_order, scp_update_menu_order_tags, scpo_post_types_args), the global $scporder, and the scporder_options structure are unchanged (two optional keys added: engine, show_handle)Compatibility
Settings Page Overhaul
Security Enhancements
Performance Improvements
PHP 8.4 Compatibility
Code Quality
Bug Fixes
get_current_screen() returns null| Version | Download | Type |
|---|---|---|
| 2.8.11 | Download | Stable |
| 2.8.10 | Download | Stable |
| 2.8.9 | Download | Stable |
| 2.8.8 | Download | Stable |
| 2.8.7 | Download | Stable |
| 2.8.6 | Download | Stable |
| 2.8.5 | Download | Stable |
| 2.8.4 | Download | Stable |
| 2.8.3 | Download | Stable |
| 2.8.2 | Download | Stable |
| 2.8.1 | Download | Stable |
| 2.8.0 | Download | Stable |
| 2.7.3 | Download | Stable |
| 2.7.2 | Download | Stable |
| 2.7.1 | Download | Stable |
| 2.7.0 | Download | Stable |
| 2.6.1 | Download | Stable |
| 2.6.0 | Download | Stable |
| 2.5.11 | Download | Stable |
| 2.5.10 | Download | Stable |
| 2.5.9 | Download | Stable |
| 2.5.8 | Download | Stable |
| 2.5.7 | Download | Stable |
| 2.5.6 | Download | Stable |
| 2.5.5 | Download | Stable |
| 2.5.4 | Download | Stable |
| 2.5.3 | Download | Stable |
| 2.5.2 | Download | Stable |
| 2.5.1 | Download | Stable |
| 2.5.0 | Download | Stable |
| 2.4.9 | Download | Stable |
| 2.4.8 | Download | Stable |
| 2.4.7 | Download | Stable |
| 2.4.6 | Download | Stable |
| 2.4.5 | Download | Stable |
| 2.4.4 | Download | Stable |
| 2.4.3 | Download | Stable |
| 2.4.2 | Download | Stable |
| 2.4.1 | Download | Stable |
| 2.4.0 | Download | Stable |
| 2.3.9 | Download | Stable |
| 2.3.8 | Download | Stable |
| 2.3.7 | Download | Stable |
| 2.3.6 | Download | Stable |
| 2.3.5 | Download | Stable |
| 2.3.4 | Download | Stable |
| 2.3.3 | Download | Stable |
| 2.2 | Download | Stable |
| 2.1 | Download | Stable |
| 2.0 | Download | Stable |
| 1.5 | Download | Stable |
| 1.0 | Download | Stable |
| Development | Download | Trunk |