How to Display Related Posts in WordPress Without Slowing Your Site: A Practical Guide
Related posts have a reputation for slowing sites down. Of everything people raise with me about them, speed comes up first and foremost.
The worry has a basis. A naive implementation runs a heavy query on every page load, scans the entire posts table, and adds weight to your time to first byte.
Whether it is deserved in any particular case is another matter. The fear arrives before anyone measures anything, and it keeps related posts off sites that would benefit from them.
I have been building Contextual Related Posts since 2009, and speed is the part I have worked hardest at. What follows is the practical side of that work.
This guide covers installation, content matching, thumbnails, caching, and the lazy loading added in the latest pro release. I will also explain why matching on content holds up better than matching on tags.

Installing Contextual Related Posts
The free plugin is hosted on WordPress.org, so installation is the same as any other plugin.
Go to Plugins → Add New, search for “Contextual Related Posts”, then click Install Now and Activate. From the command line:
Activation adds FULLTEXT indices to the wp_posts table, covering the title, the content, and the two together. Those indices are what let MySQL match text quickly instead of reading every row. On multisite, each site gets its own set.
No template edits are needed. Once active, the plugin appends a related posts list to the end of every single post. That default is enough for a lot of sites.
Settings live at Settings → Related Posts.
Configuring content matching
Open the List tuning tab. This controls what counts as related.
The setting that matters most is Related posts based on title and content. It is on by default. Leave it checked and the plugin matches on both the title and the body. Uncheck it, and only titles are used. In Contextual Related Posts Pro, this setting is replaced with individual weights.
Test both on your own archive. Long, detailed articles usually match better on full content, while sites built from short posts with descriptive headlines sometimes do better on titles by themselves. There is no universal answer, which is why the setting exists.
Under it sits Limit the content to be compared, which caps how many body words are matched. It defaults to 0, meaning no limit, and the maximum is 100 words. On sites with long posts, capping it can tighten results, because the opening words usually carry the topic.
Post types to include and Number of posts to display are on the same tab. Both are worth revisiting once you have seen what the list actually returns on your own content.
One setting to note under Order posts: choosing Randomly, or ticking Randomize posts, will not work alongside cached HTML. A shuffled list and a saved copy of the output cannot both be true.
Setting up thumbnails
Images earn more clicks than a bare text list. The Thumbnail tab handles this.
Location of the post thumbnail sets the layout: before the title, after the title, image only, or text alone.
The plugin works through a sequence of sources and stops at the first hit. It starts with the field named in the Thumbnail meta field name, then ACF and Featured Image from URL if you use either of those, and only then the post’s own featured image. After that, it scans the post body when Get the first image is enabled, and falls back to whatever you set under Use default thumbnail?.
That order catches people out. If you set a meta field, it overrides the featured image rather than backing it up.
Set Thumbnail width and Thumbnail height, and turn on Hard crop thumbnails so every image fills the same box. Changing these values does not resize images already in your library. You will need to regenerate them.
How caching keeps it fast
Matching against full post content on every request would be expensive. The plugin caches the output instead, which is why it stays fast on busy sites.
Both cache settings sit on the Performance tab, and both are on by default. Cache HTML output stores the generated markup the first time a post is visited. Cache posts stores only the matched post IDs, without the surrounding markup, which gives more flexibility at slightly lower performance.
The heavy query runs once per post, not once per visitor. Later readers are served the saved copy.
Saving the settings page clears the cache, so a configuration change takes effect immediately. You can also clear it from Tools → Related Posts Tools, which is where the option to recreate the indices lives.
If your site is quiet, none of this will be visible to you. If it gets traffic, it is the difference between a query per pageview and a query per post.
Pro users can lazy load the list
Caching removes the query cost. It does not remove the cost of rendering the list into the initial HTML on every single pageview.
Version 4.3.0 added Lazy load related posts to the Performance tab in CRP Pro. Turn it on, and the list is fetched over JavaScript only when it is about to enter the viewport, which keeps the initial HTML smaller and stops the related posts competing with the content above them.
It covers the content, shortcode, widget, and Related Posts block methods. WordPress core renders the Query Loop block variation, so that one is left alone. Feeds and AMP pages are skipped.
There is a tradeoff here, and I would rather say it outright than bury it. Search engines may not index the related posts links while this is on, because those links are no longer part of the HTML that gets crawled. If you added related posts mainly for internal linking, leave it off.
You can switch it off for a single instance in the shortcode:
The crp_lazy_loadOpens in a new window filter does the same job when you need the decision made conditionally in code.
Why content matching beats tags and categories
Many related posts plugins match on taxonomy alone. They pull whatever shares a category or tag. It is quick, and it is not what Contextual Related Posts leads with.
Taxonomy matching is only ever as good as your tagging, which means a tag missed on an old post makes it invisible to the list no matter how relevant it is. Tag two articles inconsistently, and the link never forms. Anyone who has inherited a site with years of uneven tagging knows how patchy this gets.
Content matching reads what is on the page. A post about caching surfaces other writing about caching, whether or not anyone remembered to tag it. New posts are matched the day they publish.
You can still bring taxonomy in. Restrict results to the same category, exclude specific terms, or in CRP Pro, weight titles, content, and excerpts against each other. The default leads with content because content is the one signal that is always there.
Closing
Relevance comes from the matching. Speed comes from caching. Set both once, and the list runs quietly in the background while you get on with writing.
Install Contextual Related PostsOpens in a new window for free from WordPress.org and let it index your archive. If you want weighted matching, cache lifetimes you can tune, and the lazy loading covered above, CRP Pro goes further.
