A lightweight WordPress plugin that allows you to mark one post per category as "sticky" to appear first in category archives. Unlike WordPress's native sticky posts feature (which only works on the main blog page), this plugin provides per-category sticky functionality.
- Per-Category Sticky Posts: Mark different posts as sticky in different categories
- One Sticky Per Category: Automatically replaces previous sticky post when selecting a new one
- Visual Admin Interface: Intuitive checkbox interface in the post editor
- Admin Column: See sticky status at a glance in the posts list
- Quick Edit Support: Manage sticky status from the quick edit panel
- Bulk Edit Support: Update multiple posts at once
- Post State Indicator: Visual badge showing sticky status
- Auto Query Modification: Automatically prioritizes sticky posts in category archives
- Translation Ready: Full i18n/l10n support
- WPCS Compliant: Follows WordPress Coding Standards
- Secure: Proper sanitization, validation, and escaping
- Download the plugin files
- Upload the
yt-category-sticky-postsfolder to/wp-content/plugins/ - Activate the plugin through the 'Plugins' menu in WordPress
composer require krasenslavov/yt-category-sticky-posts- Edit any post that has categories assigned
- Find the Category Sticky meta box in the sidebar (below the Publish box)
- Check the categories where you want this post to be sticky
- Click Update or Publish
Note: Only one post can be sticky per category. Selecting a category will replace any existing sticky post for that category.
- Go to Posts → All Posts
- Hover over a post and click Quick Edit
- Check/uncheck Category Sticky
- Click Update
- Go to Posts → All Posts
- Select multiple posts using checkboxes
- Select Edit from the Bulk Actions dropdown
- Click Apply
- Choose Make Sticky or Remove Sticky from the Category Sticky dropdown
- Click Update
Sticky posts automatically appear first in their respective category archives. No additional configuration needed!
yt-category-sticky-posts/
├── class-yt-category-sticky-posts.php # Main plugin file
├── assets/
│ ├── css/
│ │ └── yt-category-sticky-posts-admin.css # Admin styles
│ └── js/
│ └── yt-category-sticky-posts-admin.js # Admin scripts
├── languages/ # Translation files (pot/po/mo)
└── README.md # This file
YT_CATEGORY_STICKY_POSTS_VERSION // Plugin version: 1.0.0
YT_CATEGORY_STICKY_POSTS_BASENAME // Plugin base name
YT_CATEGORY_STICKY_POSTS_PATH // Plugin directory path
YT_CATEGORY_STICKY_POSTS_URL // Plugin directory URLget_instance()- Singleton instance retrieval__construct()- Initialize plugincsp_init_hooks()- Register WordPress hookscsp_load_textdomain()- Load translations
csp_add_meta_box()- Add meta box to post editorcsp_render_meta_box()- Render meta box contentcsp_save_post_meta()- Save sticky category selections
csp_modify_query()- Detect category archives and inject sticky logiccsp_orderby_sticky()- Modify SQL ORDER BY clausecsp_fields_sticky()- Placeholder for field modifications
csp_add_admin_column()- Add sticky column to posts listcsp_render_admin_column()- Display sticky status in columncsp_quick_edit_box()- Add quick edit fieldcsp_quick_edit_script()- JavaScript for quick editcsp_bulk_edit_box()- Add bulk edit fieldcsp_display_post_state()- Show sticky badge on posts
csp_get_sticky_categories()- Get sticky categories for a postcsp_get_sticky_post_for_category()- Get sticky post for a categorycsp_update_sticky_categories()- Update sticky status for postcsp_enqueue_scripts()- Load admin CSS/JScsp_add_action_links()- Add documentation link to plugins page
activate()- Run on plugin activationdeactivate()- Run on plugin deactivationyt_category_sticky_posts_uninstall()- Run on plugin deletion
The plugin stores sticky post information in two places:
- Post Meta: Each sticky post stores its sticky category IDs in
_yt_category_stickymeta key - WordPress Option: A global mapping of category IDs to sticky post IDs in
yt_category_sticky_postsoption
This dual storage ensures:
- Fast lookups when rendering category archives
- Easy management when editing posts
- Clean data removal on uninstall
When a user visits a category archive:
csp_modify_query()detects the category being viewed- Retrieves the sticky post ID for that category
- Injects custom SQL ORDER BY clause via
csp_orderby_sticky() - Uses MySQL
FIELD()function to prioritize the sticky post
// Simplified SQL logic
ORDER BY FIELD(wp_posts.ID, [sticky_post_id]) DESC, [original_orderby]This ensures the sticky post appears first while maintaining the original sort order for other posts.
- ✅ Nonce verification on save
- ✅ Capability checks (
edit_post) - ✅ Input sanitization (
intval()for category IDs) - ✅ Output escaping (
esc_html(),esc_attr()) - ✅ Direct file access prevention
- ✅ SQL injection prevention (uses WP Query API)
// Before plugin initialization (none currently)// Filter sticky post IDs before display (future)
apply_filters( 'yt_category_sticky_posts', $all_sticky_posts );// Get plugin instance
$plugin = YT_Category_Sticky_Posts::get_instance();
// Get sticky categories for a post
$sticky_cats = get_post_meta( $post_id, '_yt_category_sticky', true );
// Get all sticky posts
$all_sticky_posts = get_option( 'yt_category_sticky_posts', array() );
// Get sticky post for specific category
$sticky_post_id = isset( $all_sticky_posts[ $category_id ] )
? $all_sticky_posts[ $category_id ]
: false;The plugin provides a global CSP_Admin object with AJAX methods (placeholders for future enhancements):
// Update sticky status
window.CSP_Admin.updateSticky(postId, [categoryIds], function (success, response) {
if (success) {
console.log("Updated successfully");
}
});
// Get sticky posts for a category
window.CSP_Admin.getStickyPosts(categoryId, function (success, response) {
if (success) {
console.log("Sticky posts:", response);
}
});The plugin includes comprehensive admin styles:
- Modern, clean WordPress admin UI design
- Hover effects and smooth transitions
- Responsive design for mobile devices
- Dark mode support
- Accessibility improvements (focus states, keyboard navigation)
- Loading state animations
Override plugin styles by targeting these CSS classes:
/* Meta box */
.csp-meta-box {
}
.csp-category-item {
}
.csp-category-name {
}
.csp-current-sticky {
}
/* Admin column */
.csp-sticky-indicator {
}
.csp-no-sticky {
}
/* Post state */
.post-state-category_sticky {
}- WordPress: 5.8 or higher
- PHP: 7.4 or higher
- Tested up to: WordPress 6.7
- Compatible with: Classic Editor, Gutenberg, Quick Edit, Bulk Edit
- Works with: Any post type that supports categories
The plugin is translation-ready with text domain yt-category-sticky-posts.
- Use the
.potfile in/languagesdirectory - Create
.poand.mofiles for your language - Place them in
/wp-content/languages/plugins/
- English (default)
Yes! Check multiple categories in the meta box to make the post sticky in all of them.
The plugin will replace the previous sticky post. You'll see a warning message showing which post will be replaced.
Currently, the plugin only supports the default category taxonomy. Custom taxonomy support may be added in future versions.
No, this plugin only affects category archives. Use WordPress's native sticky posts feature for the main blog page.
The sticky status data remains in your database. When you reactivate the plugin, all sticky posts will work again.
All plugin data (post meta and options) is automatically removed from your database during uninstall.
This plugin is designed for standard WordPress posts and categories. For WooCommerce products, you would need a modified version.
- Ensure the post is Published (not draft or pending)
- Verify the post is assigned to the category
- Check that the sticky status is saved (look for the sticky indicator in the posts list)
- Clear any caching plugins (W3 Total Cache, WP Super Cache, etc.)
- Check if theme is using custom queries that bypass the plugin
- Verify the post type supports categories
- Check Screen Options (top right) - make sure "Category Sticky" is checked
- Ensure the post is assigned to at least one category
- Clear browser cache
- Deactivate other plugins that modify quick edit
- Check browser console for JavaScript errors
This plugin follows WordPress Coding Standards (WPCS).
Run PHP_CodeSniffer:
phpcs --standard=WordPress class-yt-category-sticky-posts.phpThe JavaScript code follows WordPress JavaScript coding standards.
Run ESLint:
eslint assets/js/yt-category-sticky-posts-admin.jsContributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Follow WordPress coding standards
- Test thoroughly
- Submit a pull request
- Plugin activates without errors
- Meta box displays correctly
- Sticky status saves properly
- Category archives show sticky posts first
- Admin column displays correctly
- Quick edit works
- Bulk edit works
- Post state indicator appears
- Plugin deactivates cleanly
- Plugin uninstalls and removes all data
- No PHP warnings or notices
- Compatible with WordPress 5.8+
- Compatible with PHP 7.4+
The plugin is optimized for performance:
- Minimal database queries: Uses efficient lookups and caching
- Only loads on necessary pages: Scripts/styles only load on post edit and list screens
- Lightweight: ~640 lines of PHP, ~3.7KB CSS, ~7.8KB JS
- No frontend JavaScript: Zero impact on visitor page load times
- Smart query modification: Only runs on category archive pages
- Initial release
- Per-category sticky post functionality
- Admin meta box interface
- Quick edit and bulk edit support
- Admin column display
- Post state indicator
- Automatic query modification
- Translation ready
- Full admin assets (CSS/JS)
GPL v2 or later
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- Author: Krasen Slavov
- Author URI: https://krasenslavov.com
- Plugin URI: https://github.com/krasenslavov/yt-category-sticky-posts
Built following WordPress Plugin Handbook and WPCS guidelines.
For issues, questions, or feature requests:
- GitHub Issues: https://github.com/krasenslavov/yt-category-sticky-posts/issues
- Documentation: https://github.com/krasenslavov/yt-category-sticky-posts