Add Post View Counter
Tracking post views helps you understand which content resonates with your audience. This data is valuable for content strategy, identifying popular topics, and displaying social proof. This solution uses WordPress post meta to store view counts efficiently without external services or plugins.
Why Track Post Views?
- Content Strategy: Identify popular topics to create more relevant content
- Social Proof: Display view counts to encourage engagement
- Popular Posts Widget: Show most-viewed content automatically
- Analytics: Track trends without relying on Google Analytics
- Performance Insights: Compare post performance over time
- Lightweight: No external API calls or plugin overhead
Basic View Counter
Track views and store count in post meta:
/**
* Track post views
*/
function track_post_views($post_id) {
if (!is_single()) {
return;
}
if (empty($post_id)) {
global $post;
$post_id = $post->ID;
}
// Get current view count
$count = get_post_meta($post_id, 'post_views_count', true);
if ($count == '') {
$count = 0;
delete_post_meta($post_id, 'post_views_count');
add_post_meta($post_id, 'post_views_count', '0');
} else {
$count++;
update_post_meta($post_id, 'post_views_count', $count);
}
}
add_action('wp_head', 'track_post_views');
/**
* Display post views
*/
function get_post_views($post_id = null) {
if (!$post_id) {
$post_id = get_the_ID();
}
$count = get_post_meta($post_id, 'post_views_count', true);
if ($count == '') {
return '0 views';
}
return number_format($count) . ' views';
}Advanced: Exclude Logged-in Users & Bots
Track only real visitor views, excluding admins and bots:
/**
* Advanced post view tracking
*/
function advanced_track_post_views($post_id) {
if (!is_single()) {
return;
}
if (empty($post_id)) {
global $post;
$post_id = $post->ID;
}
// Exclude logged-in users
if (is_user_logged_in()) {
return;
}
// Exclude bots
$user_agent = $_SERVER['HTTP_USER_AGENT'];
$bot_patterns = array('bot', 'crawl', 'slurp', 'spider', 'mediapartners');
foreach ($bot_patterns as $pattern) {
if (stripos($user_agent, $pattern) !== false) {
return;
}
}
// Track unique views with cookie
$cookie_name = 'post_view_' . $post_id;
if (!isset($_COOKIE[$cookie_name])) {
// Set cookie for 24 hours
setcookie($cookie_name, '1', time() + 86400, COOKIEPATH, COOKIE_DOMAIN);
// Increment view count
$count = (int)get_post_meta($post_id, 'post_views_count', true);
$count++;
update_post_meta($post_id, 'post_views_count', $count);
}
}
add_action('wp_head', 'advanced_track_post_views');Display Views with Icon
Show formatted view count with visual icon:
/**
* Display views with icon
*/
function display_post_views_with_icon($post_id = null) {
if (!$post_id) {
$post_id = get_the_ID();
}
$count = (int)get_post_meta($post_id, 'post_views_count', true);
$output = '';
$output .= '👁 ';
if ($count >= 1000000) {
$output .= number_format($count / 1000000, 1) . 'M';
} elseif ($count >= 1000) {
$output .= number_format($count / 1000, 1) . 'K';
} else {
$output .= $count;
}
$output .= ' views';
return $output;
}
// Usage:
// echo display_post_views_with_icon();Most Viewed Posts Query
Retrieve and display most popular posts:
/**
* Get most viewed posts
*/
function get_most_viewed_posts($count = 5) {
$args = array(
'post_type' => 'post',
'posts_per_page' => $count,
'meta_key' => 'post_views_count',
'orderby' => 'meta_value_num',
'order' => 'DESC'
);
return new WP_Query($args);
}
/**
* Display most viewed posts widget
*/
function display_popular_posts() {
$popular_posts = get_most_viewed_posts(5);
if ($popular_posts->have_posts()) {
echo '';
echo 'Most Popular';
echo '';
while ($popular_posts->have_posts()) {
$popular_posts->the_post();
$views = get_post_meta(get_the_ID(), 'post_views_count', true);
echo '';
echo '' . get_the_title() . '';
echo '' . number_format($views) . ' views';
echo '';
}
echo '';
echo '';
wp_reset_postdata();
}
}REST API Integration
Add view count to REST API responses:
/**
* Add view count to REST API
*/
function add_views_to_rest_api() {
register_rest_field('post', 'views', array(
'get_callback' => function($post) {
$views = get_post_meta($post['id'], 'post_views_count', true);
return $views ? (int)$views : 0;
},
'schema' => array(
'description' => 'Post view count',
'type' => 'integer'
)
));
}
add_action('rest_api_init', 'add_views_to_rest_api');Admin Column for Views
Show view counts in WordPress admin post list:
/**
* Add views column to admin
*/
function add_views_column($columns) {
$columns['post_views'] = 'Views';
return $columns;
}
add_filter('manage_posts_columns', 'add_views_column');
/**
* Display view count in admin column
*/
function display_views_column($column, $post_id) {
if ($column === 'post_views') {
$views = get_post_meta($post_id, 'post_views_count', true);
echo $views ? number_format($views) : '0';
}
}
add_action('manage_posts_custom_column', 'display_views_column', 10, 2);
/**
* Make views column sortable
*/
function make_views_column_sortable($columns) {
$columns['post_views'] = 'post_views';
return $columns;
}
add_filter('manage_edit-post_sortable_columns', 'make_views_column_sortable');
/**
* Sort by views
*/
function sort_by_views($query) {
if (!is_admin()) {
return;
}
$orderby = $query->get('orderby');
if ('post_views' === $orderby) {
$query->set('meta_key', 'post_views_count');
$query->set('orderby', 'meta_value_num');
}
}
add_action('pre_get_posts', 'sort_by_views');Reset View Counts
Admin function to reset all view counts:
/**
* Reset all post view counts
*/
function reset_all_post_views() {
global $wpdb;
// Delete all view count meta
$wpdb->query("DELETE FROM $wpdb->postmeta WHERE meta_key = 'post_views_count'");
return true;
}
/**
* Reset views for specific post
*/
function reset_post_views($post_id) {
delete_post_meta($post_id, 'post_views_count');
add_post_meta($post_id, 'post_views_count', '0');
}Best Practices
| Practice | Why It Matters |
|---|---|
| Use Cookies for Unique Views | Prevents multiple counts from same visitor refreshing page |
| Exclude Logged-in Users | Authors checking their posts shouldn't inflate view counts |
| Filter Bot Traffic | Search engine crawlers and bots skew analytics |
| Use Efficient Queries | Index meta_key for fast sorting by view count |
| Consider Caching | High-traffic sites should cache popular posts queries |
| Track in wp_head | Ensures views are counted before any caching |
| Validate Post ID | Always check post exists before updating meta |
| Use Transients | Cache expensive "most viewed" queries for performance |
Performance Impact
Performance: Low impact (0.002-0.005s per page view). Single post meta update per view is lightweight. For high-traffic sites (10k+ daily views), consider: 1) Batch updates using cron, 2) Store counts in separate table, 3) Use Redis/Memcached for counting, 4) Implement AJAX-based counting to avoid blocking page load. Most sites under 100k monthly views won't notice any performance impact.
Styling View Counts
/* View count styling */
.post-views {
display: inline-flex;
align-items: center;
gap: 5px;
font-size: 14px;
color: #666;
}
.views-icon {
font-size: 16px;
opacity: 0.7;
}
/* Popular posts widget */
.popular-posts-widget {
background: #f9f9f9;
padding: 20px;
border-radius: 8px;
}
.popular-posts-widget li {
display: flex;
justify-content: space-between;
padding: 10px 0;
border-bottom: 1px solid #eee;
}
.view-count {
color: #999;
font-size: 12px;
}Frequently Asked Questions
Q: How accurate are post view counts using this method?
A: Basic implementation counts every page load, including bots and repeat views. For better accuracy, use cookies to track unique visitors (accurate within 24 hours), exclude logged-in users, and filter bot user agents. This gives 85-90% accuracy compared to Google Analytics. For exact analytics, integrate with Google Analytics API, but post meta is sufficient for displaying popular content and general trends.
Q: Will tracking views slow down my site?
A: Minimal impact for most sites. Each view adds one post meta update (~0.002s). Problems arise at very high traffic (100k+ daily views). Solutions: 1) Use object caching (Redis/Memcached), 2) Batch updates with cron instead of real-time, 3) Use AJAX to update counts after page load, 4) Store in custom table instead of postmeta. For 99% of WordPress sites, the basic method works fine without performance issues.
Q: How do I display most viewed posts from the last 30 days only?
A: Post meta only stores total count, not timestamps. For time-based popular posts, store daily counts in separate meta keys (format: views_2025_01_15) or use a custom table with timestamp column. Alternative: Query posts by date range, then sort by views: 'date_query' => array('after' => '30 days ago'), 'meta_key' => 'post_views_count', 'orderby' => 'meta_value_num'. This shows popular posts from recent content only.
Q: Can I track views for custom post types?
A: Yes, the code works for any post type. No modifications needed - it tracks whatever post type is displayed in is_single(). For post type specific tracking, add condition: if (get_post_type() !== 'your_cpt') return; before incrementing. You can also use separate meta keys per post type for independent counting: $meta_key = 'views_' . get_post_type($post_id);. Very useful for portfolios, products, or documentation sites.
Q: How do I prevent view count from increasing when I preview posts?
A: Add checks for preview and admin: if (is_preview() || is_admin()) return; at the start of your tracking function. Also exclude post revisions: if (wp_is_post_revision($post_id)) return;. For development, check environment: if (defined('WP_DEBUG') && WP_DEBUG) return;. This ensures only real public views are counted, not drafts, previews, or admin panel views.
Related Articles
Change WordPress Excerpt Length with Custom Code
Customize the default 55-word excerpt limit in WordPress to any length you prefer using this simple code snippet....
5 min readBeginnerCustomize Excerpt Length
Control the length of WordPress post excerpts by modifying word count and ending text using built-in filters....
7 min readBeginnerChange Read More Link Text
Customize the "Read More" link text and styling in WordPress using excerpt_more and the_content_more_link filters....
6 min readBeginner