How to Show Post Views Count in WordPress Without a Plugin
You can track and display post views using a few lines of code—no plugin needed. The idea is simple: store a counter in post meta, increment it on each single-post view (with basic bot/duplicate protection), and output the value in your theme.
Step 1: Count Views (functions.php or a small must-use plugin)
This snippet increments a post_views meta key when a single post is viewed. It skips obvious bots and avoids double-counting with a short-lived cookie per post.
<?php
/**
* Increment post views safely.
*/
function my_count_post_views() {
if ( is_admin() || is_feed() || is_preview() ) {
return;
}
if ( ! is_singular( 'post' ) ) {
return;
}
global $post;
if ( empty( $post ) || empty( $post->ID ) ) {
return;
}
// --- Basic bot check (very light) ---
$ua = isset($_SERVER['HTTP_USER_AGENT']) ? strtolower($_SERVER['HTTP_USER_AGENT']) : '';
$bots = array( 'bot', 'crawl', 'spider', 'slurp', 'facebookexternalhit', 'mediapartners-google' );
foreach ( $bots as $needle ) {
if ( strpos( $ua, $needle ) !== false ) {
return;
}
}
// --- Avoid double count for the same visitor (cookie per post) ---
$cookie_key = 'pv_' . $post->ID;
$already = isset($_COOKIE[$cookie_key]);
if ( $already ) {
return;
}
// Lifetime for "no double count": 12 hours (adjust)
$expire = time() + 12 * HOUR_IN_SECONDS;
setcookie( $cookie_key, '1', $expire, COOKIEPATH ?: '/', COOKIE_DOMAIN, is_ssl(), true );
// --- Increment meta counter ---
$count = (int) get_post_meta( $post->ID, 'post_views', true );
$count++;
update_post_meta( $post->ID, 'post_views', $count );
}
add_action( 'wp', 'my_count_post_views' );
Why this works: The wp action runs after WordPress resolves the main query, so is_singular('post') is reliable. The cookie prevents inflating counts from refreshes. The bot check is intentionally minimal—good enough for basic filtering without heavy CPU cost.
Step 2: Output the Views Anywhere (template tag + shortcode)
<?php
/**
* Get formatted post views.
*/
function my_get_post_views( $post_id = null ) {
$post_id = $post_id ?: get_the_ID();
$count = (int) get_post_meta( $post_id, 'post_views', true );
return number_format_i18n( $count );
}
/**
* Echo helper (for templates).
*/
function my_the_post_views( $label = 'Views' ) {
echo '<span class="post-views">' . esc_html( $label ) . ': ' . esc_html( my_get_post_views() ) . '</span>';
}
/**
* Shortcode: [post_views label="Views"]
*/
function my_post_views_shortcode( $atts ) {
$atts = shortcode_atts( array( 'label' => 'Views' ), $atts, 'post_views' );
return '<span class="post-views">' . esc_html( $atts['label'] ) . ': ' . esc_html( my_get_post_views() ) . '</span>';
}
add_shortcode( 'post_views', 'my_post_views_shortcode' );
Use it:
- In templates (e.g.,
single.php):<?php my_the_post_views(); ?> - In the editor:
[post_views label="Reads"]
Step 3: (Optional) Show a Views Column in Admin & Make It Sortable
<?php
// Add column.
function my_views_column_head( $columns ) {
$columns['post_views'] = 'Views';
return $columns;
}
add_filter( 'manage_post_posts_columns', 'my_views_column_head' );
// Fill column.
function my_views_column_content( $column, $post_id ) {
if ( $column === 'post_views' ) {
echo esc_html( my_get_post_views( $post_id ) );
}
}
add_action( 'manage_post_posts_custom_column', 'my_views_column_content', 10, 2 );
// Make sortable.
function my_views_column_sortable( $columns ) {
$columns['post_views'] = 'post_views';
return $columns;
}
add_filter( 'manage_edit-post_sortable_columns', 'my_views_column_sortable' );
// Sort logic.
function my_views_orderby( $query ) {
if ( ! is_admin() || ! $query->is_main_query() ) return;
if ( $query->get( 'orderby' ) === 'post_views' ) {
$query->set( 'meta_key', 'post_views' );
$query->set( 'orderby', 'meta_value_num' );
}
}
add_action( 'pre_get_posts', 'my_views_orderby' );
Step 4: (Optional) List “Most Viewed” Posts on the Frontend
<?php
$popular = new WP_Query( array(
'post_type' => 'post',
'posts_per_page' => 5,
'meta_key' => 'post_views',
'orderby' => 'meta_value_num',
'order' => 'DESC',
) );
if ( $popular->have_posts() ) :
echo '<ol class="popular-posts">';
while ( $popular->have_posts() ) : $popular->the_post();
echo '<li><a href="' . esc_url( get_permalink() ) . '">' . esc_html( get_the_title() ) . '</a> (' . esc_html( my_get_post_views() ) . ')</li>';
endwhile;
echo '</ol>';
wp_reset_postdata();
endif;
?>
Step 5: Quick Styles (Optional)
.post-views { color:#555; font-size:0.95rem; }
.popular-posts { margin:0; padding-left:1.2em; }
Tips & Notes
- Caching: If you use full-page caching, the counter may not run on cached hits. Consider AJAX or server-side bypasses for the counter if accuracy is critical.
- Privacy: This approach stores only an integer per post. It doesn’t track users.
- Performance: Post meta is fine for moderate traffic. For very high traffic, consider a transient or an external store (e.g., Redis) and batch-flush to post meta.
- Scope: The code counts only single post views (
is_singular('post')). Adjust if you need to count pages or CPTs.
Summary
- Add a counter that increments a
post_viewsmeta key on single-post views, with cookie & bot guard. - Display counts via a template tag or the
[post_views]shortcode. - (Optional) Surface a sortable “Views” column in the admin and build “Most Viewed” lists.
That’s it—lightweight, flexible, and no plugin required.
🎨 Want to learn more? Visit our WordPress Customization Hub for tips and advanced techniques.