diff --git a/Classes/Controller/AbstractController.php b/Classes/Controller/AbstractController.php
index d4d44b186b280961a27327a1d275649559436abb..d7fc77278262b39e3bdffa00738268ea33e73c24 100644
--- a/Classes/Controller/AbstractController.php
+++ b/Classes/Controller/AbstractController.php
@@ -27,32 +27,17 @@ namespace SGalinski\SgNews\Controller;
  ***************************************************************/
 
 use RuntimeException;
-use SGalinski\SgNews\Domain\Model\Category;
-use SGalinski\SgNews\Domain\Model\News;
-use SGalinski\SgNews\Service\ImageService;
-use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Extbase\Domain\Model\FileReference;
 use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
 
 /**
  * Abstract Controller
  */
 abstract class AbstractController extends ActionController {
-	/**
-	 * @var ImageService
-	 */
-	protected $imageService;
-
 	/**
 	 * @var array
 	 */
 	protected $extensionConfiguration = [];
 
-	/**
-	 * @var array
-	 */
-	protected $cachedSingleNews = [];
-
 	/**
 	 * Initializes any action
 	 *
@@ -65,13 +50,6 @@ abstract class AbstractController extends ActionController {
 		parent::initializeAction();
 	}
 
-	/**
-	 * @param ImageService $imageService
-	 */
-	public function injectImageService(ImageService $imageService) {
-		$this->imageService = $imageService;
-	}
-
 	/**
 	 * Error Handler
 	 *
@@ -82,129 +60,6 @@ abstract class AbstractController extends ActionController {
 		throw new RuntimeException(parent::errorAction());
 	}
 
-	/**
-	 * Returns the metadata of the given news.
-	 *
-	 * @param News $news
-	 * @param Category $category
-	 * @return array
-	 * @throws \InvalidArgumentException
-	 */
-	protected function getMetaDataForNews(News $news, Category $category): array {
-		$newsId = $news->getUid();
-		if (isset($this->cachedSingleNews[$newsId])) {
-			return $this->cachedSingleNews[$newsId];
-		}
-
-		$fileRepository = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\FileRepository::class);
-		$fileObjects = $fileRepository->findByRelation('pages', 'media', $news->getUid());
-
-		$singleNewsImageData = $this->getDataForSingleViewImage($news, $category);
-		$teaserImageData = $this->getDataForTeaserImage($news, $category);
-
-		// Use single news image data for teaser image data, if the teaser imaga data are empty.
-		$teaserIsEmpty = $teaserImageData['teaserImage'] === NULL && $teaserImageData['teaserImageObject'] === NULL;
-		$singleNewsIsEmpty = $singleNewsImageData['image'] === NULL && $singleNewsImageData['imageObject'] === NULL;
-		if ($teaserIsEmpty && !$singleNewsIsEmpty) {
-			$teaserImageData = [
-				'teaserImage' => $singleNewsImageData['image'],
-				'teaserImageObject' => $singleNewsImageData['imageObject'],
-			];
-		}
-
-		$newsRecord = array_merge(
-			[
-				'category' => $category,
-				'news' => $news,
-			],
-			$singleNewsImageData,
-			$teaserImageData,
-			['media' => $fileObjects]
-		);
-
-		$this->cachedSingleNews[$newsId] = $newsRecord;
-
-		return $newsRecord;
-	}
-
-	/**
-	 * Returns the single view image data as an array for the given news and category as fallback.
-	 *
-	 * @param News $news
-	 * @param Category $category
-	 * @return array
-	 * @throws \InvalidArgumentException
-	 */
-	protected function getDataForSingleViewImage(News $news, Category $category): array {
-		/** @var FileReference $singleNewsImage */
-		$singleNewsImage = $singleNewsImageObject = NULL;
-		$singleNewsImages = $news->getTeaser2Image();
-		if (count($singleNewsImages)) {
-			$singleNewsImage = $singleNewsImages->current();
-		} else {
-			$categoryImages = $category->getTeaser2Image();
-			if (count($categoryImages)) {
-				$singleNewsImage = $categoryImages->current();
-			}
-		}
-
-		if ($singleNewsImage) {
-			$singleNewsImageObject = $singleNewsImage;
-			$originalResource = $singleNewsImage->getOriginalResource();
-			if ($originalResource) {
-				$singleNewsImage = $originalResource->getPublicUrl();
-			}
-
-			if ($singleNewsImage) {
-				$singleNewsImage = $GLOBALS['TSFE']->absRefPrefix . $singleNewsImage;
-			}
-		}
-
-		return [
-			'image' => $singleNewsImage,
-			'imageObject' => $singleNewsImageObject,
-		];
-	}
-
-	/**
-	 * Returns the teaser image data as an array for the given news and category as fallback.
-	 *
-	 * @param News $news
-	 * @param Category $category
-	 * @return array
-	 * @throws \InvalidArgumentException
-	 */
-	protected function getDataForTeaserImage(News $news, Category $category): array {
-		/** @var FileReference $teaserImage */
-		$teaserImage = $teaserImageObject = NULL;
-		$teaserImages = $news->getTeaser1Image();
-		if (count($teaserImages)) {
-			$teaserImage = $teaserImages->current();
-		} else {
-			$categoryImages = $category->getTeaser1Image();
-			if (count($categoryImages)) {
-				$teaserImage = $categoryImages->current();
-			}
-		}
-
-		if ($teaserImage) {
-			$teaserImageObject = $teaserImage;
-			$originalResource = $teaserImage->getOriginalResource();
-			if ($originalResource) {
-				$teaserImage = $originalResource->getPublicUrl();
-			}
-
-			if ($teaserImage) {
-				$teaserImage = $GLOBALS['TSFE']->absRefPrefix . $teaserImage;
-			}
-		}
-
-		return [
-			'teaserImage' => $teaserImage,
-			'teaserImageObject' => $teaserImageObject,
-		];
-	}
-
 	/**
 	 * Calculate the pagination offset
 	 *
diff --git a/Classes/Controller/LatestController.php b/Classes/Controller/LatestController.php
index 393c956362a98045addca3de04b42f4d7db69029..e5eb3ea0cdeaa830bdf41fca500797a9a4bf9e68 100644
--- a/Classes/Controller/LatestController.php
+++ b/Classes/Controller/LatestController.php
@@ -31,6 +31,7 @@ use SGalinski\SgNews\Domain\Model\News;
 use SGalinski\SgNews\Domain\Repository\CategoryRepository;
 use SGalinski\SgNews\Domain\Repository\NewsRepository;
 use SGalinski\SgNews\Domain\Repository\TagRepository;
+use SGalinski\SgNews\Domain\Service\NewsService;
 use SGalinski\SgNews\Service\ConfigurationService;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
@@ -53,6 +54,11 @@ class LatestController extends AbstractController {
 	 */
 	protected $newsRepository;
 
+	/**
+	 * @var NewsService
+	 */
+	protected $newsService;
+
 	/**
 	 * Renders the news overview
 	 *
@@ -96,7 +102,7 @@ class LatestController extends AbstractController {
 				$categories[$categoryUid] = $category;
 			}
 
-			$newsMetaData[] = $this->getMetaDataForNews($latestNewsEntry, $category);
+			$newsMetaData[] = $this->newsService->getMetaDataForNews($latestNewsEntry, $category);
 			// Make sure, that the amount is the same as the configured limit.
 			if (count($newsMetaData) >= $limit) {
 				break;
@@ -127,4 +133,11 @@ class LatestController extends AbstractController {
 	public function injectTagRepository(TagRepository $tagRepository) {
 		$this->tagRepository = $tagRepository;
 	}
+
+	/**
+	 * @param NewsService $newsService
+	 */
+	public function injectNewsService(NewsService $newsService) {
+		$this->newsService = $newsService;
+	}
 }
diff --git a/Classes/Controller/ListByCategoryController.php b/Classes/Controller/ListByCategoryController.php
index 2f405c986df67aa5f2925f43c1ac84e697c36880..2a1c9619255b8ee6dee8a1e990475adf98afad87 100644
--- a/Classes/Controller/ListByCategoryController.php
+++ b/Classes/Controller/ListByCategoryController.php
@@ -30,6 +30,7 @@ use SGalinski\SgNews\Domain\Model\News;
 use SGalinski\SgNews\Domain\Repository\CategoryRepository;
 use SGalinski\SgNews\Domain\Repository\NewsRepository;
 use SGalinski\SgNews\Domain\Repository\TagRepository;
+use SGalinski\SgNews\Domain\Service\NewsService;
 use SGalinski\SgNews\Service\ConfigurationService;
 use SGalinski\SgNews\Service\HeaderMetaDataService;
 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
@@ -57,6 +58,11 @@ class ListByCategoryController extends AbstractController {
 	 */
 	protected $newsRepository;
 
+	/**
+	 * @var NewsService
+	 */
+	protected $newsService;
+
 	/**
 	 * @param CategoryRepository $categoryRepository
 	 */
@@ -79,6 +85,13 @@ class ListByCategoryController extends AbstractController {
 		$this->tagRepository = $tagRepository;
 	}
 
+	/**
+	 * @param NewsService $newsService
+	 */
+	public function injectNewsService(NewsService $newsService) {
+		$this->newsService = $newsService;
+	}
+
 	/**
 	 * Initialize the indexAction to set the currentPageBrowserPage parameter
 	 *
@@ -164,7 +177,7 @@ class ListByCategoryController extends AbstractController {
 
 		foreach ($news as $newsEntry) {
 			/** @var News $newsEntry */
-			$data = $this->getMetaDataForNews($newsEntry, $categories[$newsEntry->getPid()]);
+			$data = $this->newsService->getMetaDataForNews($newsEntry, $categories[$newsEntry->getPid()]);
 			$newsMetaData[] = $data;
 
 			if (!$headerSet) {
diff --git a/Classes/Controller/NewsByAuthorController.php b/Classes/Controller/NewsByAuthorController.php
index 4ace64d3cd03019ceb74e63fe1c9a6e1e63a5abd..217140fc9f465753eee084f7699e37f1d5186e67 100644
--- a/Classes/Controller/NewsByAuthorController.php
+++ b/Classes/Controller/NewsByAuthorController.php
@@ -32,6 +32,7 @@ use SGalinski\SgNews\Domain\Model\News;
 use SGalinski\SgNews\Domain\Repository\AuthorRepository;
 use SGalinski\SgNews\Domain\Repository\CategoryRepository;
 use SGalinski\SgNews\Domain\Repository\NewsRepository;
+use SGalinski\SgNews\Domain\Service\NewsService;
 use SGalinski\SgSeo\Service\HeadTagService;
 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
@@ -40,6 +41,18 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
  * News Author List Controller
  */
 class NewsByAuthorController extends AbstractController {
+	/**
+	 * @var NewsService
+	 */
+	protected $newsService;
+
+	/**
+	 * @param NewsService $newsService
+	 */
+	public function injectNewsService(NewsService $newsService) {
+		$this->newsService = $newsService;
+	}
+
 	/**
 	 * Renders the news author list.
 	 *
@@ -118,7 +131,7 @@ class NewsByAuthorController extends AbstractController {
 					$categories[$categoryId] = $category;
 				}
 
-				$newsMetaData[] = $this->getMetaDataForNews($newsEntry, $categories[$categoryId]);
+				$newsMetaData[] = $this->newsService->getMetaDataForNews($newsEntry, $categories[$categoryId]);
 			}
 		}
 
diff --git a/Classes/Controller/OverviewController.php b/Classes/Controller/OverviewController.php
index 0a524e79e7966522e1870240086221a28012e863..5b002811c71f32d22e80395f42762c173c3515bd 100644
--- a/Classes/Controller/OverviewController.php
+++ b/Classes/Controller/OverviewController.php
@@ -31,6 +31,7 @@ use SGalinski\SgNews\Domain\Model\News;
 use SGalinski\SgNews\Domain\Repository\CategoryRepository;
 use SGalinski\SgNews\Domain\Repository\NewsRepository;
 use SGalinski\SgNews\Domain\Repository\TagRepository;
+use SGalinski\SgNews\Domain\Service\NewsService;
 use SGalinski\SgNews\Service\ConfigurationService;
 use SGalinski\SgNews\Service\HeaderMetaDataService;
 use TYPO3\CMS\Core\Http\ImmediateResponseException;
@@ -60,6 +61,11 @@ class OverviewController extends AbstractController {
 	 */
 	protected $newsRepository;
 
+	/**
+	 * @var NewsService
+	 */
+	protected $newsService;
+
 	/**
 	 * @param CategoryRepository $categoryRepository
 	 */
@@ -82,6 +88,13 @@ class OverviewController extends AbstractController {
 		$this->tagRepository = $tagRepository;
 	}
 
+	/**
+	 * @param NewsService $newsService
+	 */
+	public function injectNewsService(NewsService $newsService) {
+		$this->newsService = $newsService;
+	}
+
 	/**
 	 * Initialize the overviewAction to set the currentPageBrowserPage parameter
 	 *
@@ -216,7 +229,7 @@ class OverviewController extends AbstractController {
 					$newsEntry->getPid()
 				) ?? $this->categoryRepository->findByUid($newsEntry->getPid());
 			$newsCategoryId = $newsCategory->getUid();
-			$newsMetaData = $this->getMetaDataForNews($newsEntry, $newsCategory);
+			$newsMetaData = $this->newsService->getMetaDataForNews($newsEntry, $newsCategory);
 
 			if ((int) $this->settings['groupBy'] === 1) {
 				if (!$areCategoriesCreated) {
@@ -353,7 +366,7 @@ class OverviewController extends AbstractController {
 		$category = $this->categoryRepository->findByUid($highlightedNews->getPid());
 		$highlightedNewsMetaData = NULL;
 		if ($category) {
-			$highlightedNewsMetaData = $this->getMetaDataForNews($highlightedNews, $category);
+			$highlightedNewsMetaData = $this->newsService->getMetaDataForNews($highlightedNews, $category);
 		}
 
 		if (!version_compare(ExtensionManagementUtility::getExtensionVersion('sg_seo'), '5.0.0', '>=')) {
@@ -470,7 +483,7 @@ class OverviewController extends AbstractController {
 		);
 		foreach ($news as $newsEntry) {
 			/** @var News $newsEntry */
-			$data = $this->getMetaDataForNews($newsEntry, $categoriesById[$newsEntry->getPid()]);
+			$data = $this->newsService->getMetaDataForNews($newsEntry, $categoriesById[$newsEntry->getPid()]);
 			$newsMetaData[] = $data;
 		}
 
diff --git a/Classes/Controller/SingleViewController.php b/Classes/Controller/SingleViewController.php
index 3890778233b62dd28c5da41b2cb48fdad8a87969..a7d929ff11fc51619c7593d661568217c2a553aa 100644
--- a/Classes/Controller/SingleViewController.php
+++ b/Classes/Controller/SingleViewController.php
@@ -31,6 +31,7 @@ use SGalinski\SgNews\Domain\Model\News;
 use SGalinski\SgNews\Domain\Repository\CategoryRepository;
 use SGalinski\SgNews\Domain\Repository\NewsRepository;
 use SGalinski\SgNews\Domain\Repository\TagRepository;
+use SGalinski\SgNews\Domain\Service\NewsService;
 use SGalinski\SgNews\Service\HeaderMetaDataService;
 use TYPO3\CMS\Core\Charset\CharsetConverter;
 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
@@ -59,6 +60,11 @@ class SingleViewController extends AbstractController {
 	 */
 	protected $characterSetConverter;
 
+	/**
+	 * @var NewsService
+	 */
+	protected $newsService;
+
 	/**
 	 * Renders the news single view
 	 *
@@ -92,7 +98,7 @@ class SingleViewController extends AbstractController {
 //			$similarNewsMetaData[] = $this->getMetaDataForNews($similarNewsEntry, $category);
 //		}
 
-		$newsMetaData = $this->getMetaDataForNews($news, $newsCategory);
+		$newsMetaData = $this->newsService->getMetaDataForNews($news, $newsCategory);
 		if (!version_compare(ExtensionManagementUtility::getExtensionVersion('sg_seo'), '5.0.0', '>=')) {
 			if ($newsMetaData['image']) {
 				HeaderMetaDataService::addOgImageToHeader($newsMetaData['image']);
@@ -145,4 +151,11 @@ class SingleViewController extends AbstractController {
 	public function injectTagRepository(TagRepository $tagRepository) {
 		$this->tagRepository = $tagRepository;
 	}
+
+	/**
+	 * @param NewsService $newsService
+	 */
+	public function injectNewsService(NewsService $newsService) {
+		$this->newsService = $newsService;
+	}
 }
diff --git a/Classes/Domain/Repository/NewsRepository.php b/Classes/Domain/Repository/NewsRepository.php
index 7d8011dcaf9a53d148a98e36023c90ed3b50d8f9..7b5f136564ed227a55505911e83fa3e85127a967 100644
--- a/Classes/Domain/Repository/NewsRepository.php
+++ b/Classes/Domain/Repository/NewsRepository.php
@@ -536,29 +536,40 @@ class NewsRepository extends AbstractRepository {
 			$constraints[] = $qb->expr()->eq('pid', $news->getPid());
 		}
 
+		if ($limit > 0) {
+			$qb->setMaxResults($limit);
+		}
+
 		// here we fetch the lastUpdated of the $limit amount of news with newer lastUpdated dates
 		$result = $qb->select('lastUpdated')
-			->from('pages')
+			->from('pages', 'pages')
 			->where(
 				$qb->expr()->eq('doktype', $qb->createNamedParameter(News::DOK_TYPE_NEWS, Connection::PARAM_INT)),
-				$qb->expr()->gt('lastUpdated', $news->getLastUpdated()->getTimestamp()),
-				$qb->expr()->andX(...$constraints)
-			)
-			->setMaxResults($limit)
+				$qb->expr()->gte('lastUpdated', $news->getLastUpdated()->getTimestamp())
+			)->andWhere(...$constraints)
 			->orderBy('lastUpdated', 'desc')
 			->execute();
 		$newest = $result->fetchOne();
 
 		// Here we fetch the lastUpdated of the $limit amount of news with older lastUpdated dates
-		$result = $qb->orderBy('lastUpdated', 'asc')->execute();
+		$result = $qb->where(
+				$qb->expr()->eq('doktype', $qb->createNamedParameter(News::DOK_TYPE_NEWS, Connection::PARAM_INT)),
+				$qb->expr()->lte('lastUpdated', $news->getLastUpdated()->getTimestamp())
+			)->andWhere(...$constraints)
+			->orderBy('lastUpdated', 'asc')
+			->execute();
 		$oldest = $result->fetchOne();
 
 		$query = $this->createQuery();
 		$query->getQuerySettings()->setRespectStoragePage(FALSE);
 		$query->setOrderings([
-			'crdate' => QueryInterface::ORDER_DESCENDING
+			'lastUpdated' => QueryInterface::ORDER_DESCENDING
 		]);
-		$constraints = [];
+		$constraints = [
+			$query->logicalNot(
+				$query->equals('uid', $news->getUid())
+			)
+		];
 		if ($newest) {
 			$constraints[] = $query->lessThanOrEqual('lastUpdated', $newest);
 		}
@@ -572,9 +583,12 @@ class NewsRepository extends AbstractRepository {
 		// "around" the given news, where newer news are preferred due to the ordering.
 		$tags = $news->getTags();
 		if ($tags->count() > 0) {
+			$tagConstraints = [];
 			foreach ($tags as $tag) {
-				$constraints[] = $query->contains('tags', $tag);
+				$tagConstraints[] = $query->contains('tags', $tag);
 			}
+
+			$constraints[] = $query->logicalOr($tagConstraints);
 		} else {
 			$constraints[] = $query->equals('pid', $news->getPid());
 		}
diff --git a/Classes/Domain/Service/NewsService.php b/Classes/Domain/Service/NewsService.php
new file mode 100644
index 0000000000000000000000000000000000000000..7ba3ad6d291e7a412b78cf1b3145c203321c4595
--- /dev/null
+++ b/Classes/Domain/Service/NewsService.php
@@ -0,0 +1,167 @@
+<?php
+
+namespace SGalinski\SgNews\Domain\Service;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) sgalinski Internet Services (https://www.sgalinski.de)
+ *
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project 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 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *
+ *  This script 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.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+use SGalinski\SgNews\Domain\Model\Category;
+use SGalinski\SgNews\Domain\Model\News;
+use TYPO3\CMS\Core\SingletonInterface;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Extbase\Domain\Model\FileReference;
+use TYPO3\CMS\Core\Resource\FileRepository;
+
+/**
+ * This service takes care of meta data generation for the news objects
+ */
+class NewsService implements SingletonInterface {
+	/**
+	 * @var array
+	 */
+	protected $cachedSingleNews = [];
+
+	/**
+	 * Returns the metadata of the given news.
+	 *
+	 * @param News $news
+	 * @param Category $category
+	 * @return array
+	 * @throws \InvalidArgumentException
+	 */
+	public function getMetaDataForNews(News $news, Category $category): array {
+		$newsId = $news->getUid();
+		if (isset($this->cachedSingleNews[$newsId])) {
+			return $this->cachedSingleNews[$newsId];
+		}
+
+		$fileRepository = GeneralUtility::makeInstance(FileRepository::class);
+		$fileObjects = $fileRepository->findByRelation('pages', 'media', $news->getUid());
+
+		$singleNewsImageData = $this->getDataForSingleViewImage($news, $category);
+		$teaserImageData = $this->getDataForTeaserImage($news, $category);
+
+		// Use single news image data for teaser image data, if the teaser imaga data are empty.
+		$teaserIsEmpty = $teaserImageData['teaserImage'] === NULL && $teaserImageData['teaserImageObject'] === NULL;
+		$singleNewsIsEmpty = $singleNewsImageData['image'] === NULL && $singleNewsImageData['imageObject'] === NULL;
+		if ($teaserIsEmpty && !$singleNewsIsEmpty) {
+			$teaserImageData = [
+				'teaserImage' => $singleNewsImageData['image'],
+				'teaserImageObject' => $singleNewsImageData['imageObject'],
+			];
+		}
+
+		$newsRecord = array_merge(
+			[
+				'category' => $category,
+				'news' => $news,
+			],
+			$singleNewsImageData,
+			$teaserImageData,
+			['media' => $fileObjects]
+		);
+
+		$this->cachedSingleNews[$newsId] = $newsRecord;
+
+		return $newsRecord;
+	}
+
+	/**
+	 * Returns the single view image data as an array for the given news and category as fallback.
+	 *
+	 * @param News $news
+	 * @param Category $category
+	 * @return array
+	 * @throws \InvalidArgumentException
+	 */
+	public function getDataForSingleViewImage(News $news, Category $category): array {
+		/** @var FileReference $singleNewsImage */
+		$singleNewsImage = $singleNewsImageObject = NULL;
+		$singleNewsImages = $news->getTeaser2Image();
+		if (count($singleNewsImages)) {
+			$singleNewsImage = $singleNewsImages->current();
+		} else {
+			$categoryImages = $category->getTeaser2Image();
+			if (count($categoryImages)) {
+				$singleNewsImage = $categoryImages->current();
+			}
+		}
+
+		if ($singleNewsImage) {
+			$singleNewsImageObject = $singleNewsImage;
+			$originalResource = $singleNewsImage->getOriginalResource();
+			if ($originalResource) {
+				$singleNewsImage = $originalResource->getPublicUrl();
+			}
+
+			if ($singleNewsImage) {
+				$singleNewsImage = $GLOBALS['TSFE']->absRefPrefix . $singleNewsImage;
+			}
+		}
+
+		return [
+			'image' => $singleNewsImage,
+			'imageObject' => $singleNewsImageObject,
+		];
+	}
+
+	/**
+	 * Returns the teaser image data as an array for the given news and category as fallback.
+	 *
+	 * @param News $news
+	 * @param Category $category
+	 * @return array
+	 * @throws \InvalidArgumentException
+	 */
+	public function getDataForTeaserImage(News $news, Category $category): array {
+		/** @var FileReference $teaserImage */
+		$teaserImage = $teaserImageObject = NULL;
+		$teaserImages = $news->getTeaser1Image();
+		if (count($teaserImages)) {
+			$teaserImage = $teaserImages->current();
+		} else {
+			$categoryImages = $category->getTeaser1Image();
+			if (count($categoryImages)) {
+				$teaserImage = $categoryImages->current();
+			}
+		}
+
+		if ($teaserImage) {
+			$teaserImageObject = $teaserImage;
+			$originalResource = $teaserImage->getOriginalResource();
+			if ($originalResource) {
+				$teaserImage = $originalResource->getPublicUrl();
+			}
+
+			if ($teaserImage) {
+				$teaserImage = $GLOBALS['TSFE']->absRefPrefix . $teaserImage;
+			}
+		}
+
+		return [
+			'teaserImage' => $teaserImage,
+			'teaserImageObject' => $teaserImageObject,
+		];
+	}
+}
diff --git a/Classes/ViewHelpers/RelatedViewHelper.php b/Classes/ViewHelpers/RelatedViewHelper.php
index 6a5ec43565fbde946b20321afd08819e3d957570..69b8e781d353728940d593b4788c082e934de247 100644
--- a/Classes/ViewHelpers/RelatedViewHelper.php
+++ b/Classes/ViewHelpers/RelatedViewHelper.php
@@ -2,8 +2,11 @@
 
 namespace SGalinski\SgNews\ViewHelpers;
 
+use SGalinski\SgNews\Domain\Repository\CategoryRepository;
 use SGalinski\SgNews\Domain\Repository\NewsRepository;
+use SGalinski\SgNews\Domain\Service\NewsService;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
 use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
 use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
 
@@ -13,6 +16,11 @@ use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
 class RelatedViewHelper extends AbstractViewHelper {
 	use CompileWithRenderStatic;
 
+	/**
+	 * @var bool
+	 */
+	protected $escapeOutput = false;
+
 	/**
 	 * Initialize the view helper arguments
 	 */
@@ -23,12 +31,30 @@ class RelatedViewHelper extends AbstractViewHelper {
 			'The news record from which to find related news',
 			TRUE
 		);
-		$this->registerArgument('limit', 'int', 'Limit the amount of related news to display', FALSE, 5);
-		$this->registerArgument('as', 'string', 'The name of the iteration variable', TRUE);
+		$this->registerArgument(
+			'limit',
+			'int',
+			'Limit the amount of related news to display',
+			FALSE,
+			5
+		);
+		$this->registerArgument(
+			'as',
+			'string',
+			'The name of the iteration variable',
+			TRUE
+		);
 		$this->registerArgument(
 			'iteration', 'string',
 			'The name of the variable to store iteration information (index, cycle, isFirst, isLast, isEven, isOdd)'
 		);
+		$this->registerArgument(
+			'relatedNews',
+			ObjectStorage::class,
+			'An optional list of related news to take instead of finding some via the news repository',
+			FALSE,
+			NULL
+		);
 	}
 
 	/**
@@ -44,21 +70,34 @@ class RelatedViewHelper extends AbstractViewHelper {
 	public static function renderStatic(
 		array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext
 	) {
+		$newsService = GeneralUtility::makeInstance(NewsService::class);
+		$categoryRepository = GeneralUtility::makeInstance(CategoryRepository::class);
 		$templateVariableContainer = $renderingContext->getVariableProvider();
 		$news = $arguments['news'];
 		$newsRepository = GeneralUtility::makeInstance(NewsRepository::class);
-		$related = $newsRepository->findRelated($news);
+		if ($arguments['relatedNews']) {
+			$related = $arguments['relatedNews'];
+		} else {
+			$related = $newsRepository->findRelated($news, (int) $arguments['limit']);
+		}
+
 		if (isset($arguments['iteration'])) {
 			$iterationData = [
 				'index' => 0,
 				'cycle' => 1,
-				'total' => count($arguments['each'])
+				'total' => $related->count()
 			];
 		}
 
 		$output = '';
+		$categories = [];
 		foreach ($related as $relatedNews) {
-			$templateVariableContainer->add($arguments['as'], $relatedNews);
+			if (!isset($categories[$relatedNews->getPid()])) {
+				$categories[$relatedNews->getPid()] = $categoryRepository->findByUid($relatedNews->getPid());
+			}
+
+			$newsMetaData = $newsService->getMetaDataForNews($relatedNews, $categories[$relatedNews->getPid()]);
+			$templateVariableContainer->add($arguments['as'], $newsMetaData);
 			if (isset($iterationData)) {
 				$iterationData['isFirst'] = $iterationData['cycle'] === 1;
 				$iterationData['isLast'] = $iterationData['cycle'] === $iterationData['total'];
diff --git a/Configuration/TypoScript/Frontend/constants.typoscript b/Configuration/TypoScript/Frontend/constants.typoscript
index 34a9c4da599228c1cc1b6f3069b7b9a49eab04db..58f8138af09e551c7aec4fbb1efd6f6b9db7ca00 100644
--- a/Configuration/TypoScript/Frontend/constants.typoscript
+++ b/Configuration/TypoScript/Frontend/constants.typoscript
@@ -19,5 +19,8 @@ plugin.tx_sgnews {
 
 		# sort direction (DESC / ASC)
 		sortDirection = DESC
+
+		# This enables the output of related news in regards to the news category or tags
+		enableAutomaticRelatedNews = 0
 	}
 }
diff --git a/Configuration/TypoScript/Frontend/setup.typoscript b/Configuration/TypoScript/Frontend/setup.typoscript
index 9a52fd4ef64e299d3c2fd12ce028ac75c9ebbf6a..fb32038793c9f09240980a30d131cdea7f83f512 100644
--- a/Configuration/TypoScript/Frontend/setup.typoscript
+++ b/Configuration/TypoScript/Frontend/setup.typoscript
@@ -109,6 +109,9 @@ plugin.tx_sgnews {
 
 		# The logo value for the structured data implementation (see single view template)
 		publisherLogo =
+
+		# This enables the output of related news in regards to the news category or tags
+		enableAutomaticRelatedNews = {$plugin.tx_sgnews.settings.enableAutomaticRelatedNews}
 	}
 
 	features {
diff --git a/Resources/Private/Partials/TeaserRelated.html b/Resources/Private/Partials/TeaserRelated.html
new file mode 100644
index 0000000000000000000000000000000000000000..2360faca6abc939c58cd947f821469b07d090b7b
--- /dev/null
+++ b/Resources/Private/Partials/TeaserRelated.html
@@ -0,0 +1,36 @@
+{namespace sg=SGalinski\SgNews\ViewHelpers}
+
+<f:comment>
+	<!--
+	Usage Example:
+
+	<f:render partial="Teaser" arguments="{
+		newsMetaData: newsMetaData,
+		headerTag: '<h3>',
+		closingHeaderTag: '</h3>',
+		showCategory: 1
+	}" />
+
+	newsMetaData -> news element
+	headerTag -> hierarchy type of the header tag
+	showCategory -> defines if the category may be shown
+
+	Use <f:debug>{_all}</f:debug> to see all parameters and fields.
+	-->
+</f:comment>
+
+<f:link.page pageUid="{news.uid}" class="tx-sgnews-teaser">
+	<f:if condition="{news.teaser1Image}">
+		<f:for each="{news.teaser1Image}" as="teaserImage">
+			<div class="tx-sgnews-teaser-image tx-sgnews-teaser-image-stretched"
+				 style="background-image: url({f:uri.image(image: teaserImage)});"></div>
+		</f:for>
+	</f:if>
+	<div class="tx-sgnews-teaser-inner">
+		<div class="tx-sgnews-teaser-title">
+			{headerTag -> f:format.raw()}
+			{news.subtitleWithFallbackToTitle}
+			{closingHeaderTag -> f:format.raw()}
+		</div>
+	</div>
+</f:link.page>
diff --git a/Resources/Private/Templates/SingleView/SingleView.html b/Resources/Private/Templates/SingleView/SingleView.html
index 7ee717d638ecc101d1777cbd7ec4f29ee8bfcd61..bbbb26abdbc10e04953aebdaedf7bfccd65ae54a 100644
--- a/Resources/Private/Templates/SingleView/SingleView.html
+++ b/Resources/Private/Templates/SingleView/SingleView.html
@@ -88,12 +88,12 @@
 					</div>
 				</div>
 			</section>
-
 		</div>
+
 		<section class="content">
 			<div class="container">
 				<div class="tx-sgnews-single">
-					<div class="tx-sgnews-single-container">
+					<ul class="tx-sgnews-single-container">
 						<f:alias
 							map="{content: '{f:cObject(typoscriptObjectPath: \'{f:if(condition: \\\'{newsMetaData.news.contentFromAnotherPage}\\\', then: \\\'lib.contentFromAnotherPage\\\', else: \\\'lib.mainContent\\\')}\')}'}">
 							<div class="tx-sgnews-single-content">
@@ -199,38 +199,50 @@
 
 						<f:if condition="{newsMetaData.news.relatedNews}">
 							<f:then>
-								<div class="tx-sgnews-single-related">
+								<div class="tx-sgnews-single-related tx-sgnews-categories">
 									<h3>
 										<f:translate key="frontend.singleview.relatedArticles"/>
 									</h3>
-
-									<ul>
-										<f:for each="{newsMetaData.news.relatedNews}" as="relatedNewsEntry">
-											<li>
-												<a href="{f:uri.page(pageUid: '{relatedNewsEntry.uid}')}">
-													{relatedNewsEntry.subtitleWithFallbackToTitle}
-												</a>
+									<ul class="tx-sgnews-list tx-sgnews-overview row">
+										<sg:related news="{newsMetaData.news}"
+													relatedNews="{newsMetaData.news.relatedNews}"
+													as="relatedNewsEntry">
+											<li class="col-md-4 col-sm-6 col-xs-12">
+												<f:render partial="Teaser" arguments="{
+													newsMetaData: relatedNewsEntry,
+													headerTag: '<h2>',
+													closingHeaderTag: '</h2>',
+													showCategory: '1'
+												}" />
 											</li>
-										</f:for>
+										</sg:related>
 									</ul>
 								</div>
 							</f:then>
 							<f:else>
-								<div class="tx-sgnews-single-related">
-									<h3>
-										<f:translate key="frontend.singleview.relatedArticles"/>
-									</h3>
-
-									<ul>
-										<sg:related news="{newsMetaData.news}" limit="5" as="relatedNewsEntry">
-											<li>
-												<a href="{f:uri.page(pageUid: '{relatedNewsEntry.uid}')}">
-													{relatedNewsEntry.subtitleWithFallbackToTitle}
-												</a>
-											</li>
-										</sg:related>
-									</ul>
-								</div>
+								<f:if condition="{settings.enableAutomaticRelatedNews}">
+									<sg:related news="{newsMetaData.news}" iteration="iterator" limit="3" as="relatedNewsEntry">
+										<f:if condition="{iterator.isFirst}">
+											<div class="tx-sgnews-single-related tx-sgnews-categories">
+												<h3>
+													<f:translate key="frontend.singleview.relatedArticles"/>
+												</h3>
+												<ul class="tx-sgnews-list tx-sgnews-overview row">
+										</f:if>
+													<li class="col-md-4 col-sm-6 col-xs-12">
+														<f:render partial="Teaser" arguments="{
+															newsMetaData: relatedNewsEntry,
+															headerTag: '<h2>',
+															closingHeaderTag: '</h2>',
+															showCategory: '1'
+														}" />
+													</li>
+										<f:if condition="{iterator.isLast}">
+												</ul>
+											</div>
+										</f:if>
+									</sg:related>
+								</f:if>
 							</f:else>
 						</f:if>
 					</div>