diff --git a/Classes/Controller/YoutubeController.php b/Classes/Controller/YoutubeController.php
index c521d0c23bf019ffc356ceb7dc0d9ddf1eca2dbd..513de3a052fc4b8eb770a3f8e1075aba1d6dc543 100644
--- a/Classes/Controller/YoutubeController.php
+++ b/Classes/Controller/YoutubeController.php
@@ -60,7 +60,7 @@ class YoutubeController extends ActionController {
 		$apiKey = $this->settings['apiKey'];
 		$thumbnailType = $this->settings['thumbnailType'];
 		$aspectRatio = $this->settings['aspectRatio'];
-		$showApiResult = (boolean) $this->settings['showApiResult'];
+		$showApiResult = (bool) $this->settings['showApiResult'];
 		$debugOutput = '';
 
 		try {
@@ -71,7 +71,11 @@ class YoutubeController extends ActionController {
 			}
 
 			$jsonArray['items'] = $this->youtubeService->mapArray(
-				$jsonArray['items'], $id, $aspectRatio, $thumbnailType, $apiKey
+				$jsonArray['items'],
+				$id,
+				$aspectRatio,
+				$thumbnailType,
+				$apiKey
 			);
 
 			$jsonArray['items'] = $this->mapJsonArrayWithPossibleCustomThumbnails($jsonArray['items']);
@@ -113,7 +117,9 @@ class YoutubeController extends ActionController {
 
 		$fileRepository = GeneralUtility::makeInstance(FileRepository::class);
 		$fileObjects = $fileRepository->findByRelation(
-			'tt_content', 'tx_sgyoutube_thumbnail_image', $contentElementUid
+			'tt_content',
+			'tx_sgyoutube_thumbnail_image',
+			$contentElementUid
 		);
 		if (count($fileObjects) <= 0) {
 			return $jsonArray;
diff --git a/Classes/Hooks/PageLayoutView/PluginRenderer.php b/Classes/Hooks/PageLayoutView/PluginRenderer.php
index 9b7e3da292dd8980d501b03e07feb11724450f91..6780e1878bf8c25f4d094c6c4b26755ca101e683 100644
--- a/Classes/Hooks/PageLayoutView/PluginRenderer.php
+++ b/Classes/Hooks/PageLayoutView/PluginRenderer.php
@@ -20,20 +20,28 @@ namespace SGalinski\SgYoutube\Hooks\PageLayoutView;
  *  This copyright notice MUST APPEAR in all copies of the script!
  ***************************************************************/
 
+use SGalinski\SgYoutube\Preview\PreviewService;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Backend\View\PageLayoutView;
 use TYPO3\CMS\Backend\View\PageLayoutViewDrawItemHookInterface;
+use TYPO3\CMS\Core\Localization\LanguageService;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Fluid\View\StandaloneView;
-use \TYPO3\CMS\Core\Localization\LanguageService;
 
 /**
  * Class PluginRenderer
  * Renders back-end preview for the SG YouTube Videos plugin
  *
  * @package SGalinski\SgYoutube\Hooks
+ * @deprecated All of this class will be removed when TYPO3 10 support is dropped
  */
 class PluginRenderer implements PageLayoutViewDrawItemHookInterface {
+	protected PreviewService $previewService;
+
+	public function init() {
+		$this->previewService = GeneralUtility::makeInstance(PreviewService::class);
+	}
+
 	/**
 	 * Preprocesses the preview rendering of a content element of type "sg_youtube"
 	 *
@@ -44,41 +52,27 @@ class PluginRenderer implements PageLayoutViewDrawItemHookInterface {
 	 * @param array $row Record row of tt_content
 	 * @return void
 	 * @noinspection ReferencingObjectsInspection
+	 * @deprecated All of this class will be removed when TYPO3 10 support is dropped
 	 */
 	public function preProcess(
-		PageLayoutView &$parentObject, &$drawItem, &$headerContent, &$itemContent, array &$row
+		PageLayoutView &$parentObject,
+		&$drawItem,
+		&$headerContent,
+		&$itemContent,
+		array &$row
 	): void {
+		$this->init();
+		trigger_error(
+			'Using the old style of rendering backend previews is deprecated and will not longer work when TYPO3 10'
+			. ' support is dropped! Please switch to using the Fluid Based Page Module instead!',
+			E_USER_DEPRECATED
+		);
 		if ($row['list_type'] === 'sgyoutube_youtube') {
 			$drawItem = FALSE;
 
 			$this->adaptPluginHeaderContent($headerContent, $row);
 
-			$view = GeneralUtility::makeInstance(StandaloneView::class);
-			$view->setPartialRootPaths(['EXT:sg_youtube/Resources/Private/Partials/Backend']);
-			$view->setTemplateRootPaths(['EXT:sg_youtube/Resources/Private/Templates/Youtube']);
-			$view->setTemplate('Backend.html');
-			$view->assign('uid', $row['uid']);
-
-			// Get available plugin settings and their values from flexform
-			$pluginConfiguration = GeneralUtility::xml2array(
-				$row['pi_flexform'], 'T3DataStructure'
-			)['data']['sDEF']['lDEF'];
-
-			$templateData = [
-				'youtubeId' => $pluginConfiguration['settings.id']['vDEF'],
-				'maxResults' => $pluginConfiguration['settings.maxResults']['vDEF'],
-				'showTitle' => (int) ($pluginConfiguration['settings.showTitle']['vDEF'] ?? 1),
-				'showDescription' => (int) ($pluginConfiguration['settings.showDescription']['vDEF'] ?? 1),
-				'disableLightbox' => $pluginConfiguration['settings.disableLightbox']['vDEF'],
-				'disableLightboxMobile' => $pluginConfiguration['settings.disableLightboxMobile']['vDEF'],
-				'aspectRatio' => $pluginConfiguration['settings.aspectRatio']['vDEF'],
-				'thumbnailType' => $pluginConfiguration['settings.thumbnailType']['vDEF'],
-				'thumbnailImagesCount' => $pluginConfiguration['settings.thumbnailImages']['vDEF'],
-				'showApiResult' => $pluginConfiguration['settings.showApiResult']['vDEF'],
-			];
-
-			$view->assign('data', $templateData);
-
+			$view = $this->previewService->getPluginPreview($row);
 			$itemContent .= $view->render();
 		}
 	}
@@ -87,13 +81,14 @@ class PluginRenderer implements PageLayoutViewDrawItemHookInterface {
 	 * Adapts the given $headerContent.
 	 * To be used in all plugin previews so the Header Contents appear similarly.
 	 *
-	 * @param $headerContent
-	 * @param $row
+	 * @param string $headerContent
+	 * @param array $row
 	 */
 	protected function adaptPluginHeaderContent(&$headerContent, $row): void {
 		$headerContent = '<h4>' . $this->getPluginNameForHeaderContent(
-				(int) $row['pid'], $row['list_type']
-			) . $headerContent . '</h4>';
+			(int) $row['pid'],
+			$row['list_type']
+		) . $headerContent . '</h4>';
 	}
 
 	/**
@@ -109,7 +104,10 @@ class PluginRenderer implements PageLayoutViewDrawItemHookInterface {
 
 		$pluginName = $languageService->sL(
 			BackendUtility::getLabelFromItemListMerged(
-				$pid, 'tt_content', 'list_type', $listType
+				$pid,
+				'tt_content',
+				'list_type',
+				$listType
 			)
 		);
 		return '<span class="label label-primary">' . $pluginName . '</span>&nbsp;';
diff --git a/Classes/Preview/PreviewRenderer.php b/Classes/Preview/PreviewRenderer.php
new file mode 100644
index 0000000000000000000000000000000000000000..73e7b165cd453ce2d6a6dfd5c713838ae2a38180
--- /dev/null
+++ b/Classes/Preview/PreviewRenderer.php
@@ -0,0 +1,95 @@
+<?php
+
+/***************************************************************
+ *  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!
+ ***************************************************************/
+
+namespace SGalinski\SgYoutube\Preview;
+
+use TYPO3\CMS\Backend\Preview\PreviewRendererInterface;
+use TYPO3\CMS\Backend\Utility\BackendUtility;
+use TYPO3\CMS\Backend\View\BackendLayout\Grid\GridColumnItem;
+use TYPO3\CMS\Core\Localization\LanguageService;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Fluid\View\StandaloneView;
+
+class PreviewRenderer implements PreviewRendererInterface {
+	/**
+	 * @var LanguageService $languageService
+	 */
+	protected LanguageService $languageService;
+
+	protected PreviewService $previewService;
+
+	public function __construct(LanguageService $languageService, PreviewService $previewService) {
+		$this->languageService = $languageService;
+		$this->previewService = $previewService;
+	}
+
+	/**
+	 * Dedicated method for rendering preview header HTML for
+	 * the page module only. Receives $item which is an instance of
+	 * GridColumnItem which has a getter method to return the record.
+	 *
+	 * @param GridColumnItem $item
+	 * @return string
+	 */
+	public function renderPageModulePreviewHeader(GridColumnItem $item): string {
+		$label = BackendUtility::getLabelFromItemListMerged(
+			$item->getRecord()['pid'],
+			'tt_content',
+			'list_type',
+			$item->getRecord()['list_type']
+		);
+		return '<h4><span class="label label-primary">' . $this->languageService->sL($label) . '</span></h4>';
+	}
+
+	/**
+	 * Dedicated method for rendering preview body HTML for
+	 * the page module only.
+	 *
+	 * @param GridColumnItem $item
+	 * @return string
+	 */
+	public function renderPageModulePreviewContent(GridColumnItem $item): string {
+		$view = $this->previewService->getPluginPreview($item->getRecord());
+		return $view->render();
+	}
+
+	/**
+	 * Render a footer for the record to display in page module below
+	 * the body of the item's preview.
+	 *
+	 * @param GridColumnItem $item
+	 * @return string
+	 */
+	public function renderPageModulePreviewFooter(GridColumnItem $item): string {
+		return '';
+	}
+
+	/**
+	 * Dedicated method for wrapping a preview header and body HTML.
+	 *
+	 * @param string $previewHeader
+	 * @param string $previewContent
+	 * @param GridColumnItem $item
+	 * @return string
+	 */
+	public function wrapPageModulePreview(string $previewHeader, string $previewContent, GridColumnItem $item): string {
+		return $previewHeader . $previewContent;
+	}
+}
diff --git a/Classes/Preview/PreviewService.php b/Classes/Preview/PreviewService.php
new file mode 100644
index 0000000000000000000000000000000000000000..1d88d9fba92d615e731b993714117f4ae9ba35b6
--- /dev/null
+++ b/Classes/Preview/PreviewService.php
@@ -0,0 +1,85 @@
+<?php
+
+/***************************************************************
+ *  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!
+ ***************************************************************/
+namespace SGalinski\SgYoutube\Preview;
+
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Fluid\View\StandaloneView;
+
+/**
+ * PreviewService, to get Views while we have to duplicate previewCode
+ */
+class PreviewService {
+	public const RETURNTYPE_ARR = 'array';
+
+	/**
+	 * returns the usable PluginView
+	 *
+	 * @param array $row
+	 * @return StandaloneView
+	 */
+	public function getPluginPreview(array $row): StandaloneView {
+		$view = GeneralUtility::makeInstance(StandaloneView::class);
+		$view->setPartialRootPaths(['EXT:sg_youtube/Resources/Private/Partials/Backend']);
+		$view->setTemplateRootPaths(['EXT:sg_youtube/Resources/Private/Templates/Youtube']);
+		$view->setTemplate('Backend.html');
+		$view->assign('uid', $row['uid']);
+
+		// Get available plugin settings and their values from flexform
+		$pluginConfiguration = GeneralUtility::xml2array(
+			$row['pi_flexform'],
+			'T3DataStructure'
+		)['data']['sDEF']['lDEF'];
+
+		$templateData = [
+			'youtubeId' => $this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.id'),
+			'maxResults' => $this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.maxResults'),
+			'showTitle' => (int) ($this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.showTitle') ?? 1),
+			'showDescription' => (int) ($this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.showDescription') ?? 1),
+			'disableLightbox' => $this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.disableLightbox'),
+			'disableLightboxMobile' => $this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.disableLightboxMobile'),
+			'aspectRatio' => $this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.aspectRatio'),
+			'thumbnailType' => $this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.thumbnailType'),
+			'thumbnailImagesCount' => $this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.thumbnailImages'),
+			'showApiResult' => $this->passVDefOnKeyToTemplate($pluginConfiguration, 'settings.showApiResult'),
+		];
+
+		$view->assign('data', $templateData);
+		return $view;
+	}
+
+	/**
+	 * @param array $conf
+	 * @param string $key
+	 * @param string $returnType
+	 * @return array|mixed|string
+	 */
+	private function passVDefOnKeyToTemplate(array $conf, string $key, string $returnType = '') {
+		if (isset($conf[$key])) {
+			return $conf[$key]['vDEF'];
+		}
+
+		// check if we got a possible returntype:
+		if ($returnType === self::RETURNTYPE_ARR) {
+			return [];
+		}
+
+		return '';
+	}
+}
diff --git a/Classes/Service/CachedImageService.php b/Classes/Service/CachedImageService.php
index b45774f9ab6eae136308ea1828f1fb200b7371cc..b6e7470da1547cd0e6a38d51d3939ecac1296ca7 100644
--- a/Classes/Service/CachedImageService.php
+++ b/Classes/Service/CachedImageService.php
@@ -74,7 +74,7 @@ class CachedImageService {
 	 *
 	 * @param string $cacheDirectory
 	 */
-	public function __construct($cacheDirectory) {
+	public function __construct(string $cacheDirectory = '') {
 		$this->cacheDirectory = ltrim($cacheDirectory, '/');
 	}
 
diff --git a/Classes/Service/YoutubeService.php b/Classes/Service/YoutubeService.php
index b406514cc9fc1b8b00eb511de07fe96a8b21f376..c21799b4ccc895349929dc54534a99aae480ce2b 100644
--- a/Classes/Service/YoutubeService.php
+++ b/Classes/Service/YoutubeService.php
@@ -26,27 +26,29 @@ namespace SGalinski\SgYoutube\Service;
  *  This copyright notice MUST APPEAR in all copies of the script!
  ***************************************************************/
 
+use Exception;
+use GuzzleHttp\Exception\ClientException;
 use InvalidArgumentException;
 use TYPO3\CMS\Core\Context\Context;
 use TYPO3\CMS\Core\Context\Exception\AspectNotFoundException;
 use TYPO3\CMS\Core\Context\LanguageAspect;
+use TYPO3\CMS\Core\Http\RequestFactory;
 use TYPO3\CMS\Core\Http\ServerRequest;
 use TYPO3\CMS\Core\Registry;
 use TYPO3\CMS\Core\Site\Entity\Site;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Core\Utility\VersionNumberUtility;
 
 /**
  * YouTube Helper Service
  */
 class YoutubeService {
-	const API_URL = 'https://www.googleapis.com/youtube/v3/';
-	const API_CHANNEL = 'search';
-	const API_PLAYLIST = 'playlistItems';
-	const API_VIDEO = 'videos';
-	const API_PART = 'snippet';
-	const API_PART_LOCALIZATIONS = 'localizations';
-	const API_ORDER_BY = 'date';
+	public const API_URL = 'https://www.googleapis.com/youtube/v3/';
+	public const API_CHANNEL = 'search';
+	public const API_PLAYLIST = 'playlistItems';
+	public const API_VIDEO = 'videos';
+	public const API_PART = 'snippet';
+	public const API_PART_LOCALIZATIONS = 'localizations';
+	public const API_ORDER_BY = 'date';
 
 	/**
 	 * Maps the json array from the YouTube call to return some unified value. The output from YouTube is pretty
@@ -60,7 +62,11 @@ class YoutubeService {
 	 * @return array
 	 */
 	public function mapArray(
-		$jsonArray = [], $youtubeId = '', $aspectRatio = '16:9', $thumbnailType = 'byAspectRatio', $apiKey = ''
+		$jsonArray = [],
+		$youtubeId = '',
+		$aspectRatio = '16:9',
+		$thumbnailType = 'byAspectRatio',
+		$apiKey = ''
 	): array {
 		if (count($jsonArray) <= 0) {
 			return $jsonArray;
@@ -84,22 +90,18 @@ class YoutubeService {
 			$aspectRatio = '16:9';
 		}
 
-		// Localization is just available from TYPO3 9.X.X
-		if (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '9.0.0', '>=')) {
-			$context = GeneralUtility::makeInstance(Context::class);
-
-			try {
-				/** @var LanguageAspect $languageAspect */
-				$languageAspect = $context->getAspect('language');
-				$currentLanguageUid = $languageAspect->getId();
-			} catch (AspectNotFoundException $e) {
-				// Can't be possible to land here, otherwise the whole frontend would be weird as hell..
-				$currentLanguageUid = 0;
-			}
+		$context = GeneralUtility::makeInstance(Context::class);
+		try {
+			/** @var LanguageAspect $languageAspect */
+			$languageAspect = $context->getAspect('language');
+			$currentLanguageUid = $languageAspect->getId();
+		} catch (AspectNotFoundException $e) {
+			// Can't be possible to land here, otherwise the whole frontend would be weird as hell..
+			$currentLanguageUid = 0;
+		}
 
-			if ($currentLanguageUid > 0 && $youtubeId && $apiKey) {
-				$jsonArray = $this->addLocalizationData($jsonArray, $apiKey, $currentLanguageUid);
-			}
+		if ($currentLanguageUid > 0 && $youtubeId && $apiKey) {
+			$jsonArray = $this->addLocalizationData($jsonArray, $apiKey, $currentLanguageUid);
 		}
 
 		$result = [];
@@ -156,21 +158,19 @@ class YoutubeService {
 		}
 
 		$localizationData = $this->getDetailedVideoInformationForJsonArray(
-			$jsonArray, $apiKey, self::API_PART_LOCALIZATIONS
+			$jsonArray,
+			$apiKey,
+			self::API_PART_LOCALIZATIONS
 		);
-		if (!isset($localizationData['items']) || count($localizationData['items']) <= 0) {
+		if (!isset($localizationData['items']) || (is_countable($localizationData['items']) ? count($localizationData['items']) : 0) <= 0) {
 			return $jsonArray;
 		}
 
-		/** @var ServerRequest $request */
-		$request = $GLOBALS['TYPO3_REQUEST'];
-		$attributes = $request->getAttributes();
-		if (!isset($attributes['site'])) {
+		$site = $this->getSite();
+		if ($site === NULL) {
 			return $jsonArray;
 		}
 
-		/** @var Site $site */
-		$site = $attributes['site'];
 		$languages = $site->getLanguages();
 		$currentSiteLanguage = $languages[$currentLanguageUid];
 		if (!$currentSiteLanguage) {
@@ -190,7 +190,7 @@ class YoutubeService {
 		}
 
 		foreach ($localizationData['items'] as $index => $localizationEntry) {
-			if (!isset($localizationEntry['localizations']) || count($localizationEntry['localizations']) <= 0) {
+			if (!isset($localizationEntry['localizations']) || (is_countable($localizationEntry['localizations']) ? count($localizationEntry['localizations']) : 0) <= 0) {
 				continue;
 			}
 
@@ -202,11 +202,11 @@ class YoutubeService {
 					break;
 				}
 
-				if (!$title &&  isset($localizations[$languageIsoCode]['title'])) {
+				if (!$title && isset($localizations[$languageIsoCode]['title'])) {
 					$title = $localizations[$languageIsoCode]['title'];
 				}
 
-				if (!$description &&  isset($localizations[$languageIsoCode]['description'])) {
+				if (!$description && isset($localizations[$languageIsoCode]['description'])) {
 					$description = $localizations[$languageIsoCode]['description'];
 				}
 			}
@@ -266,7 +266,7 @@ class YoutubeService {
 		}
 
 		$result = $this->getJsonAsArray('', '10', $apiKey, $apiUrl . '?' . $query);
-		if (!isset($result['items']) || count($result['items']) <= 0) {
+		if (!isset($result['items']) || (is_countable($result['items']) ? count($result['items']) : 0) <= 0) {
 			return $jsonArray;
 		}
 
@@ -302,26 +302,27 @@ class YoutubeService {
 			}
 		}
 
-		if (function_exists('curl_init')) {
-			$ch = curl_init();
-			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
-			curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
-			curl_setopt($ch, CURLOPT_URL, $url);
-			$jsonString = curl_exec($ch);
-			curl_close($ch);
-
-		} elseif (ini_get('allow_url_fopen')) {
-			$jsonString = file_get_contents($url);
-		} else {
-			throw new InvalidArgumentException(
-				'The system doesn\'t have CURL enabled and "allow_url_fopen" is disabled.', 403
-			);
+		$requestFactory = GeneralUtility::makeInstance(RequestFactory::class);
+		try {
+			$site = $this->getSite();
+			if ($site === NULL) {
+				throw new Exception('No site object found!');
+			}
+			$response = $requestFactory->request($url, 'GET', [
+				'headers' => [
+					'Referer' => $site->getBase()->getHost()
+				]
+			]);
+			$jsonString = (string) $response->getBody();
+		} catch (ClientException $exception) {
+			$jsonString = (string) $exception->getResponse()->getBody();
 		}
-
+		
 		$jsonArray = ($jsonString !== '' ? json_decode($jsonString, TRUE) : []);
 		if ($jsonArray === NULL) {
 			throw new InvalidArgumentException(
-				'There is something wrong with loaded JSON or encoded data is deeper than the recursion limit.', 403
+				'There is something wrong with loaded JSON or encoded data is deeper than the recursion limit.',
+				403
 			);
 		}
 
@@ -339,7 +340,7 @@ class YoutubeService {
 			throw new InvalidArgumentException('No items array.', 403);
 		}
 
-		if (count($jsonArray['items']) < 1) {
+		if ((is_countable($jsonArray['items']) ? count($jsonArray['items']) : 0) < 1) {
 			throw new InvalidArgumentException('No items found.', 403);
 		}
 
@@ -396,4 +397,20 @@ class YoutubeService {
 
 		return $youtubeId;
 	}
+
+	/**
+	 * Get the current site of the request
+	 *
+	 * @return Site|null
+	 */
+	protected function getSite(): ?Site {
+		/** @var ServerRequest $request */
+		$request = $GLOBALS['TYPO3_REQUEST'];
+		$attributes = $request->getAttributes();
+		if (!isset($attributes['site'])) {
+			return NULL;
+		}
+
+		return $attributes['site'];
+	}
 }
diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..53ba19598e5d49e5db0e15371b80e19bcdd768cb
--- /dev/null
+++ b/Configuration/Services.yaml
@@ -0,0 +1,15 @@
+services:
+  _defaults:
+    autowire: true
+    autoconfigure: true
+    public: true
+
+  SGalinski\SgYoutube\:
+    resource: '../Classes/*'
+
+  SGalinski\SgYoutube\Preview\PreviewRenderer:
+    public: true
+
+  SGalinski\SgYoutube\Service\YoutubeService:
+    public: true
+    autowire: false
diff --git a/Configuration/TCA/Overrides/sys_template.php b/Configuration/TCA/Overrides/sys_template.php
index 2940744c5dd5ec35a4bf5e362bb6580ecca53546..3749ff006deafa710c4b986ce2fcac6b8d193758 100644
--- a/Configuration/TCA/Overrides/sys_template.php
+++ b/Configuration/TCA/Overrides/sys_template.php
@@ -1,5 +1,6 @@
 <?php
 
+defined('TYPO3') or die();
 \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addStaticFile(
 	'sg_youtube',
 	'Configuration/TypoScript',
diff --git a/Configuration/TCA/Overrides/tt_content.php b/Configuration/TCA/Overrides/tt_content.php
index c12eb263102d20dd0b4d942104183164def494d9..76259ba94cea5f2a45349c09555f58e5a777f89a 100644
--- a/Configuration/TCA/Overrides/tt_content.php
+++ b/Configuration/TCA/Overrides/tt_content.php
@@ -1,12 +1,18 @@
 <?php
 
+defined('TYPO3') or die();
 $GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['sgyoutube_youtube'] = 'pi_flexform';
 \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPiFlexFormValue(
-	'sgyoutube_youtube', 'FILE:EXT:sg_youtube/Configuration/FlexForms/flexform_sgyoutube_youtube.xml'
+	'sgyoutube_youtube',
+	'FILE:EXT:sg_youtube/Configuration/FlexForms/flexform_sgyoutube_youtube.xml'
 );
 
 \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin(
-	'SGalinski.sg_youtube',
+	'SgYoutube',
 	'Youtube',
 	'YouTube Videos'
 );
+
+// add the backend preview for the youtube plugin
+$GLOBALS['TCA']['tt_content']['types']['list']['previewRenderer']['sgyoutube_youtube'] =
+	\SGalinski\SgYoutube\Preview\PreviewRenderer::class;
diff --git a/Configuration/TypoScript/setup.typoscript b/Configuration/TypoScript/setup.typoscript
index 32b5bbdb22e78bb405f50a00053754b718cfa3f4..381ab31b1763fcb6ee4c405353b143e65e247a3f 100644
--- a/Configuration/TypoScript/setup.typoscript
+++ b/Configuration/TypoScript/setup.typoscript
@@ -20,3 +20,4 @@ plugin.tx_sgyoutube {
 		}
 	}
 }
+plugin.tx_sgyoutube_youtube < plugin.tx_sgyoutube
diff --git a/README.md b/README.md
index 9d28075c3cecfa432364106f0d31073351340201..fcedb8308d1f36c392f87e0a48db6176d12f243e 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@ Repository: https://gitlab.sgalinski.de/typo3/sg_youtube
 
 Please report bugs here: https://gitlab.sgalinski.de/typo3/sg_youtube
 
-TYPO3 version: >9.5
+TYPO3 version: >10.4
 
 ## Installation / Integration
 
diff --git a/Resources/Public/Icons/Extension.png b/Resources/Public/Icons/Extension.png
new file mode 100644
index 0000000000000000000000000000000000000000..67fda8fec210ccf660363ff30c10f19513b4bfb2
Binary files /dev/null and b/Resources/Public/Icons/Extension.png differ
diff --git a/Resources/Public/JavaScript/youtubeLightbox.js b/Resources/Public/JavaScript/youtubeLightbox.js
deleted file mode 100644
index 92d787888b5470574da929c2a0dbd678c49c46c4..0000000000000000000000000000000000000000
--- a/Resources/Public/JavaScript/youtubeLightbox.js
+++ /dev/null
@@ -1,93 +0,0 @@
-/**
- * JavaScript module for the sg-youtube plugin
- *
- * @deprecated since version 4.4.0 and will be removed in 5.0.0
- */
-module.exports = function() {
-	'use strict';
-
-	const $ = require('jquery');
-	require('magnific-popup');
-
-	/**
-	 * Initialize the whole Popup setup
-	 */
-	function init(section) {
-		$(`${section || ''} .sg-youtube-item`).each(function (index, item) {
-			let $youtubeItem = $(item),
-				viewportWidth = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
-			if (($youtubeItem.data('disable-lightbox') && viewportWidth >= 680) ||
-				($youtubeItem.data('disable-lightbox-mobile') && viewportWidth < 680)
-			) {
-				$youtubeItem.on('click', replaceThumbnailWithVideo);
-			} else {
-				$youtubeItem.magnificPopup({
-					type: 'iframe',
-					iframe: {
-						patterns: {
-							youtube: {
-								index: 'youtube.com/',
-								id: function(url) {
-									return prepareVideoUrl(url);
-								},
-								src: '//www.youtube-nocookie.com/embed/%id%'
-							}
-						}
-					}
-				});
-			}
-		});
-	}
-
-	/**
-	 * Replaces the given element behind the event with a youtube video.
-	 *
-	 * @param {Event} event
-	 */
-	function replaceThumbnailWithVideo(event) {
-		event.preventDefault();
-		let $youtubeItem = $(event.currentTarget);
-
-		// This needs to be done, because the height of inline elements is always 0, if on auto...
-		$youtubeItem.css('display', 'block');
-
-		let queryString = prepareVideoUrl($youtubeItem.attr('href')),
-			$thumbnailElement = $youtubeItem.find('.sg-youtube-image'),
-			width = $thumbnailElement.outerWidth() + 'px',
-			height = $thumbnailElement.outerHeight() + 'px';
-		$thumbnailElement.replaceWith('<div class="sg-youtube-item sg-card-shadow" ' +
-			'style="height: ' + height + '; width: ' + width + ';"><iframe ' +
-			'width="' + width + '" height="' + height + '" ' +
-			'src="https://www.youtube-nocookie.com/embed/' + queryString + '" frameborder="0" ' +
-			'allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" ' +
-			'allowfullscreen></iframe></div>');
-	}
-
-	/**
-	 * Prepares the given url, or returns null.
-	 *
-	 * @param {string} url
-	 * @return {string|null}
-	 */
-	function prepareVideoUrl(url) {
-		let matches = url.match(/watch\?v=(.*)&list=(.*)/);
-		if (!matches) {
-			// check if the list parameter is missing
-			matches = url.match(/watch\?v=([^?&]*)/);
-			if (!matches) {
-				return null;
-			}
-		}
-		let [, videoString] = matches,
-			queryParameterSeparator = '?';
-		if (matches[2]) {
-			videoString += '?list=' + matches[2];
-			queryParameterSeparator = '&';
-
-		}
-
-		return videoString + queryParameterSeparator + 'autoplay=1&rel=0';
-	}
-
-	init();
-};
diff --git a/UPGRADE.md b/UPGRADE.md
index b669916711875659dfae2452deea0f1370fed234..ac1134cf4f91ab17e002eaa8d3974f0609c590c2 100644
--- a/UPGRADE.md
+++ b/UPGRADE.md
@@ -1,6 +1,8 @@
 ## Version 5
 - `youtubeLightbox.js` (deprecated since 4.4.0) using magnific-popup and jQuery removed in favor of `sgYoutubeLightbox.js` (vanilla JS).
 - Extension `project_theme_lightbox` required starting with version 5.0.0.
+- Dropped TYPO3 9 Support
+- Dropped php 7.3 Support
 
 ## Version 4.4  ```project_theme_lightbox``` integration
 - The magnific popup integration is deprecated and will be removed in later versions.
diff --git a/composer.json b/composer.json
index a8f158fc97026517545bd7e81ddc2f0073215945..2e912f6999f570efe4fc84e2f41632974cd151cd 100644
--- a/composer.json
+++ b/composer.json
@@ -6,11 +6,8 @@
     "license": "GPL-2.0-or-later",
     "version": "4.8.2",
     "require": {
-        "typo3/cms-core": "^9.5.4 || ^10.4.0",
-        "sgalinski/project-theme-lightbox": "^1.0.0"
-    },
-    "require-dev": {
-        "roave/security-advisories": "dev-master"
+        "typo3/cms-core": "^10.4.0 || ^11.5.0",
+        "sgalinski/project-theme-lightbox": "^2.0.0"
     },
     "replace": {
         "sgalinski/sg_youtube": "self.version"
diff --git a/ext_emconf.php b/ext_emconf.php
index ddcd5dd44b820af3c672dbc560e79f69320c1d71..d89aa145ba4ad079705b5f938efee7b68d246793 100644
--- a/ext_emconf.php
+++ b/ext_emconf.php
@@ -39,8 +39,9 @@ $EM_CONF['sg_youtube'] = [
 	'version' => '4.8.2',
 	'constraints' => [
 		'depends' => [
-			'typo3' => '9.5.0-10.4.99',
-			'project_theme_lightbox' => '^1.0.0',
+			'typo3' => '10.4.0-11.5.99',
+			'project_theme_lightbox' => '1.0.0-2.9.99',
+			'php' => '7.4.0-8.1.99',
 		],
 		'conflicts' => [],
 		'suggests' => [],
diff --git a/ext_localconf.php b/ext_localconf.php
index 71c8207270ad838aef688283229dc4721620aaaf..d4fb86d3af8cb75c487226f2eb1f4fdbdda9098e 100644
--- a/ext_localconf.php
+++ b/ext_localconf.php
@@ -24,15 +24,13 @@
  *  This copyright notice MUST APPEAR in all copies of the script!
  ***************************************************************/
 
-if (!defined('TYPO3_MODE')) {
-	die('Access denied.');
-}
+defined('TYPO3') or die();
 
 \TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
-	'SGalinski.sg_youtube',
+	'SgYoutube',
 	'Youtube',
 	[
-		'Youtube' => 'index',
+		\SGalinski\SgYoutube\Controller\YoutubeController::class => 'index',
 
 	],
 	// non-cacheable actions