On your single.php template, you need to load the category term(s), and then run a query to get other posts. In my example, I load posts from a single category. We have WordPress SEO plugin installed which allows you to select a primary category if you decide to select several.
$related_articles = array();
$categories = get_the_category();
if($categories) {
$category_id = $categories[0]->term_id;
if(count($categories) > 1 && class_exists('WPSEO_Primary_Term')) {
$primary_term = new WPSEO_Primary_Term('category', get_the_id());
$category_id = $primary_term->get_primary_term();
}
$related_articles = get_posts(array(
'category' => $category_id,
'exclude' => $post->ID,
'numberposts' => 3,
'order' => 'DESC',
'orderby' => 'date',
'post_status' => 'published',
'post_type' => 'post'
));
}
You can then loop through the related posts, if any:
<?php if($related_articles): ?>
<section class="related-articles">
<?php foreach($related_articles as $post): ?>
<?php setup_postdata($post); ?>
<article>
<h2><?php the_title(); ?></h2>
</article>
<?php endforeach; ?>
<?php wp_reset_postdata(); ?>
</section>
<?php endif; ?>
Hope that helps