diff --git a/typo3/sysext/linkvalidator/Classes/LinkAnalyzer.php b/typo3/sysext/linkvalidator/Classes/LinkAnalyzer.php
index 23ac8bac22539e56f750f7bc879bbb56dac7e3d0..90c7ac12c26973cc94f5b4fe014015d882e10235 100644
--- a/typo3/sysext/linkvalidator/Classes/LinkAnalyzer.php
+++ b/typo3/sysext/linkvalidator/Classes/LinkAnalyzer.php
@@ -219,18 +219,6 @@ class LinkAnalyzer
         }
     }
 
-    /**
-     * Set that a recheck is necessary for recordUid / table combination in list
-     * of broken links.
-     *
-     * @param string $recordUid
-     * @param string $table
-     */
-    public function setNeedsRecheck(string $recordUid, string $table): void
-    {
-        $this->brokenLinkRepository->setNeedsRecheckForRecord($table, (int)$recordUid);
-    }
-
     /**
      * Recheck for broken links for one field in table for record.
      *
@@ -475,13 +463,12 @@ class LinkAnalyzer
     public function getLinkCounts()
     {
         $groupedResult = $this->brokenLinkRepository->getNumberOfBrokenLinksForRecordsOnPages($this->pids);
-
-        $markerArray = [];
+        $data = [];
         foreach ($groupedResult as $linkType => $amount) {
-            $markerArray[$linkType] = $amount;
-            $markerArray['brokenlinkCount'] += $amount;
+            $data[$linkType] = $amount;
+            $data['brokenlinkCount'] += $amount;
         }
-        return $markerArray;
+        return $data;
     }
 
     /**
diff --git a/typo3/sysext/linkvalidator/Classes/Report/LinkValidatorReport.php b/typo3/sysext/linkvalidator/Classes/Report/LinkValidatorReport.php
index b3622c40ce52f3269f860d6825c08972d1d0f6d0..7243128656149561d3d62de0e13e5c3426a33571 100644
--- a/typo3/sysext/linkvalidator/Classes/Report/LinkValidatorReport.php
+++ b/typo3/sysext/linkvalidator/Classes/Report/LinkValidatorReport.php
@@ -15,7 +15,6 @@ namespace TYPO3\CMS\Linkvalidator\Report;
  */
 
 use TYPO3\CMS\Backend\Routing\UriBuilder;
-use TYPO3\CMS\Backend\Template\DocumentTemplate;
 use TYPO3\CMS\Backend\Template\ModuleTemplate;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
@@ -27,10 +26,9 @@ use TYPO3\CMS\Core\Imaging\IconFactory;
 use TYPO3\CMS\Core\Localization\LanguageService;
 use TYPO3\CMS\Core\Messaging\FlashMessage;
 use TYPO3\CMS\Core\Messaging\FlashMessageService;
-use TYPO3\CMS\Core\Page\PageRenderer;
-use TYPO3\CMS\Core\Service\MarkerBasedTemplateService;
 use TYPO3\CMS\Core\Type\Bitmask\Permission;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Fluid\View\StandaloneView;
 use TYPO3\CMS\Info\Controller\InfoModuleController;
 use TYPO3\CMS\Linkvalidator\LinkAnalyzer;
 use TYPO3\CMS\Linkvalidator\Linktype\LinktypeInterface;
@@ -42,11 +40,6 @@ use TYPO3\CMS\Linkvalidator\Repository\BrokenLinkRepository;
  */
 class LinkValidatorReport
 {
-    /**
-     * @var DocumentTemplate
-     */
-    protected $doc;
-
     /**
      * Information about the current page record
      *
@@ -99,15 +92,6 @@ class LinkValidatorReport
      */
     protected $checkOpt = ['report' => [], 'check' => []];
 
-    /**
-     * Html for the statistics table with the checkboxes of the link types
-     * and the numbers of broken links
-     * For "Report" and "Check link" tab
-     *
-     * @var array
-     */
-    protected $checkOptionsHtml = ['report' => [], 'check' => []];
-
     /**
      * Information for last edited record
      * @var array
@@ -115,26 +99,15 @@ class LinkValidatorReport
     protected $lastEditedRecord = [
         'uid'   => 0,
         'table' => '',
-        'field' => ''
+        'field' => '',
+        'timestamp' => 0
     ];
 
-    /**
-     * Complete content (html) to be displayed
-     *
-     * @var string
-     */
-    protected $content;
-
     /**
      * @var LinktypeInterface[]
      */
     protected $hookObjectsArr = [];
 
-    /**
-     * @var MarkerBasedTemplateService
-     */
-    protected $templateService;
-
     /**
      * @var IconFactory
      */
@@ -153,13 +126,23 @@ class LinkValidatorReport
     /**
      * @var array
      */
-    protected $searchFields;
+    protected $searchFields = [];
 
     /**
      * @var BrokenLinkRepository
      */
     protected $brokenLinkRepository;
 
+    /**
+     * @var ModuleTemplate
+     */
+    protected $moduleTemplate;
+
+    /**
+     * @var StandaloneView
+     */
+    protected $view;
+
     /**
      * Init, called from parent object
      *
@@ -171,21 +154,29 @@ class LinkValidatorReport
         $this->id = (int)GeneralUtility::_GP('id');
         $this->brokenLinkRepository = GeneralUtility::makeInstance(BrokenLinkRepository::class);
         $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
+        $this->moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
+        $this->view = $this->createView('InfoModule');
+    }
+
+    protected function createView(string $templateName): StandaloneView
+    {
+        $view = GeneralUtility::makeInstance(StandaloneView::class);
+        $view->setLayoutRootPaths(['EXT:linkvalidator/Resources/Private/Layouts']);
+        $view->setPartialRootPaths(['EXT:linkvalidator/Resources/Private/Partials']);
+        $view->setTemplateRootPaths(['EXT:linkvalidator/Resources/Private/Templates/Backend']);
+        $view->setTemplate($templateName);
+        $view->assign('pageId', $this->id);
+        return $view;
     }
 
     /**
-     * Main, called from parent object
-     *
-     * @return string Module content
+     * Checks for incoming GET/POST parameters to update the module settings
      */
-    public function main()
+    protected function validateSettings()
     {
-        $this->getLanguageService()->includeLLFile('EXT:linkvalidator/Resources/Private/Language/Module/locallang.xlf');
-        $update = GeneralUtility::_GP('updateLinkList');
         $prefix = 'check';
         $other = 'report';
-
-        if (empty($update)) {
+        if (empty(GeneralUtility::_GP('updateLinkList'))) {
             $prefix = 'report';
             $other = 'check';
         }
@@ -194,13 +185,10 @@ class LinkValidatorReport
         $this->lastEditedRecord['uid'] = GeneralUtility::_GP('last_edited_record_uid') ?? 0;
         $this->lastEditedRecord['table'] = GeneralUtility::_GP('last_edited_record_table') ?? '';
         $this->lastEditedRecord['field'] = GeneralUtility::_GP('last_edited_record_field') ?? '';
-        $this->lastEditedRecord['timestamp'] = GeneralUtility::_GP('last_edited_record_timestamp') ?? '';
+        $this->lastEditedRecord['timestamp'] = GeneralUtility::_GP('last_edited_record_timestamp') ?? 0;
 
         // get searchLevel (number of levels of pages to check / show results)
         $this->searchLevel[$prefix] = GeneralUtility::_GP($prefix . '_search_levels');
-        if (isset($this->id)) {
-            $this->modTS = BackendUtility::getPagesTSconfig($this->id)['mod.']['linkvalidator.'] ?? [];
-        }
         if (isset($this->searchLevel[$prefix])) {
             $this->pObj->MOD_SETTINGS[$prefix . '_searchlevel'] = $this->searchLevel[$prefix];
         } else {
@@ -244,73 +232,86 @@ class LinkValidatorReport
 
         // save settings
         $this->getBackendUser()->pushModuleData('web_info', $this->pObj->MOD_SETTINGS);
+    }
+    /**
+     * Main, called from parent object
+     *
+     * @return string Module content
+     */
+    public function main()
+    {
+        $this->getLanguageService()->includeLLFile('EXT:linkvalidator/Resources/Private/Language/Module/locallang.xlf');
+        if (isset($this->id)) {
+            $this->modTS = BackendUtility::getPagesTSconfig($this->id)['mod.']['linkvalidator.'] ?? [];
+        }
+        $this->validateSettings();
         $this->initialize();
 
-        $this->initializeLinkAnalyzer();
-        $updateLinkList = GeneralUtility::_GP('updateLinkList') ?? '';
-        if ($updateLinkList) {
+        if (GeneralUtility::_GP('updateLinkList')) {
             $this->updateBrokenLinks();
-        } else {
-            if ($this->lastEditedRecord['uid']) {
-                if ($this->modTS['actionAfterEditRecord'] === 'recheck') {
-                    // recheck broken links for last edited reccord
-                    $this->linkAnalyzer->recheckLinks(
-                        $this->checkOpt['check'],
-                        $this->lastEditedRecord['uid'],
-                        $this->lastEditedRecord['table'],
-                        $this->lastEditedRecord['field'],
-                        (int)($this->lastEditedRecord['timestamp']),
-                        true
-                    );
-                } else {
-                    // mark broken links for last edited record as needing a recheck
-                    $this->linkAnalyzer->setNeedsRecheck(
-                        $this->lastEditedRecord['uid'],
-                        $this->lastEditedRecord['table']
-                    );
-                }
+        } elseif ($this->lastEditedRecord['uid']) {
+            if ($this->modTS['actionAfterEditRecord'] === 'recheck') {
+                // recheck broken links for last edited reccord
+                $this->linkAnalyzer->recheckLinks(
+                    $this->checkOpt['check'],
+                    $this->lastEditedRecord['uid'],
+                    $this->lastEditedRecord['table'],
+                    $this->lastEditedRecord['field'],
+                    (int)$this->lastEditedRecord['timestamp']
+                );
+            } else {
+                // mark broken links for last edited record as needing a recheck
+                $this->brokenLinkRepository->setNeedsRecheckForRecord(
+                    $this->lastEditedRecord['table'],
+                    (int)$this->lastEditedRecord['uid']
+                );
             }
         }
 
-        $brokenLinkOverView = $this->linkAnalyzer->getLinkCounts();
-
-        $this->checkOptionsHtml['report'] = $this->getCheckOptions($brokenLinkOverView, 'report');
-        $this->checkOptionsHtml['check'] = $this->getCheckOptions($brokenLinkOverView, 'check');
-        $this->render();
-
-        $pageTile = '';
-        if ($this->id) {
-            $pageRecord = BackendUtility::getRecord('pages', $this->id);
-            $pageTile = '<h1>' . htmlspecialchars(BackendUtility::getRecordTitle('pages', $pageRecord)) . '</h1>';
-        }
-
-        return '<div id="linkvalidator-modfuncreport">' . $pageTile . $this->createTabs() . '</div>';
+        $pageTitle = $this->pageRecord ? BackendUtility::getRecordTitle('pages', $this->pageRecord) : '';
+        $this->view->assign('title', $pageTitle);
+        $this->view->assign('content', $this->renderContent());
+        return $this->view->render();
     }
 
     /**
      * Create tabs to split the report and the checkLink functions
-     *
-     * @return string
      */
-    protected function createTabs()
+    protected function renderContent(): string
     {
-        $languageService = $this->getLanguageService();
+        if (!$this->isAccessibleForCurrentUser) {
+            // If no access or if ID == zero
+            $this->moduleTemplate->addFlashMessage(
+                $this->getLanguageService()->getLL('no.access'),
+                $this->getLanguageService()->getLL('no.access.title'),
+                FlashMessage::ERROR
+            );
+            return '';
+        }
+
+        $groupedBrokenLinkCounts = $this->linkAnalyzer->getLinkCounts();
+
+        $reportsTabView = $this->createViewForBrokenLinksTab($groupedBrokenLinkCounts);
         $menuItems = [
             0 => [
-                'label' => $languageService->getLL('Report'),
-                'content' => $this->flush(true)
+                'label' => $this->getLanguageService()->getLL('Report'),
+                'content' => $reportsTabView->render()
             ],
         ];
 
         if ((bool)$this->modTS['showCheckLinkTab']) {
+            $reportsTabView = $this->createView('CheckLinksTab');
+            $reportsTabView->assignMultiple([
+                'prefix' => 'check',
+                'selectedLevel' => $this->searchLevel['check'],
+                'options' => $this->getCheckOptions($groupedBrokenLinkCounts, 'check'),
+            ]);
             $menuItems[1] = [
-                'label' => $languageService->getLL('CheckLink'),
-                'content' => $this->flush()
+                'label' => $this->getLanguageService()->getLL('CheckLink'),
+                'content' => $reportsTabView->render()
             ];
         }
-
-        $moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
-        return $moduleTemplate->getDynamicTabMenu($menuItems, 'report-linkvalidator');
+        return $this->moduleTemplate->getDynamicTabMenu($menuItems, 'report-linkvalidator');
     }
 
     /**
@@ -318,8 +319,6 @@ class LinkValidatorReport
      */
     protected function initialize()
     {
-        $this->doc = GeneralUtility::makeInstance(DocumentTemplate::class);
-        $this->doc->setModuleTemplate('EXT:linkvalidator/Resources/Private/Templates/mod_template.html');
         foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'] ?? [] as $linkType => $className) {
             $this->hookObjectsArr[$linkType] = GeneralUtility::makeInstance($className);
         }
@@ -333,14 +332,12 @@ class LinkValidatorReport
             $this->isAccessibleForCurrentUser = false;
         }
 
-        $pageRenderer = $this->getPageRenderer();
+        $pageRenderer = $this->moduleTemplate->getPageRenderer();
         $pageRenderer->addCssFile('EXT:linkvalidator/Resources/Public/Css/linkvalidator.css', 'stylesheet', 'screen');
         $pageRenderer->loadRequireJsModule('TYPO3/CMS/Linkvalidator/Linkvalidator');
+        $pageRenderer->addInlineLanguageLabelFile('EXT:linkvalidator/Resources/Private/Language/Module/locallang.xlf');
 
-        $this->linkAnalyzer = GeneralUtility::makeInstance(LinkAnalyzer::class);
-        $this->templateService = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
-        // Localization
-        $this->getPageRenderer()->addInlineLanguageLabelFile('EXT:linkvalidator/Resources/Private/Language/Module/locallang.xlf');
+        $this->initializeLinkAnalyzer();
     }
 
     /**
@@ -348,7 +345,7 @@ class LinkValidatorReport
      */
     protected function initializeLinkAnalyzer()
     {
-        $this->searchFields = [];
+        $this->linkAnalyzer = GeneralUtility::makeInstance(LinkAnalyzer::class);
         // Get the searchFields from TSconfig
         foreach ($this->modTS['searchFields.'] as $table => $fieldList) {
             $fields = GeneralUtility::trimExplode(',', $fieldList, true);
@@ -381,125 +378,40 @@ class LinkValidatorReport
         $this->linkAnalyzer->getLinkStatistics($this->checkOpt['check'], $this->modTS['checkhidden']);
     }
 
-    /**
-     * Renders the content of the module
-     */
-    protected function render()
-    {
-        if ($this->isAccessibleForCurrentUser) {
-            $this->content = $this->renderBrokenLinksTable();
-        } else {
-            // If no access or if ID == zero
-            $message = GeneralUtility::makeInstance(
-                FlashMessage::class,
-                $this->getLanguageService()->getLL('no.access'),
-                $this->getLanguageService()->getLL('no.access.title'),
-                FlashMessage::ERROR
-            );
-            $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
-            $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
-            $defaultFlashMessageQueue->enqueue($message);
-        }
-    }
-
-    /**
-     * Flushes the rendered content to the browser
-     *
-     * @param bool $form
-     * @return string $content
-     */
-    protected function flush($form = false)
-    {
-        return $this->doc->moduleBody(
-            $this->pageRecord,
-            $this->getDocHeaderButtons(),
-            $form ? $this->getTemplateMarkers() : $this->getTemplateMarkersCheck()
-        );
-    }
-
-    /**
-     * Builds the selector for the level of pages to search
-     *
-     * @param string $prefix Indicating if the selector is build for the "report" or "check" tab
-     *
-     * @return string Html code of that selector
-     */
-    protected function getLevelSelector($prefix = 'report')
-    {
-        $languageService = $this->getLanguageService();
-        // Build level selector
-        $options = [];
-        $availableOptions = [
-            0 => $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_0'),
-            1 => $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_1'),
-            2 => $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_2'),
-            3 => $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_3'),
-            4 => $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_4'),
-            999 => $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_infi')
-        ];
-        foreach ($availableOptions as $optionValue => $optionLabel) {
-            $options[] = '<option value="' . $optionValue . '"' . ($optionValue === (int)$this->searchLevel[$prefix] ? ' selected="selected"' : '') . '>' . htmlspecialchars($optionLabel) . '</option>';
-        }
-        return '<select name="' . $prefix . '_search_levels" class="form-control">' . implode('', $options) . '</select>';
-    }
-
     /**
      * Displays the table of broken links or a note if there were no broken links
      *
-     * @return string Content of the table or of the note
+     * @param array $amountOfBrokenLinks
+     * @return StandaloneView
      */
-    protected function renderBrokenLinksTable()
+    protected function createViewForBrokenLinksTab(array $amountOfBrokenLinks)
     {
-        $brokenLinkItems = '';
-        $brokenLinksTemplate = $this->templateService->getSubpart(
-            $this->doc->moduleTemplate,
-            '###NOBROKENLINKS_CONTENT###'
-        );
+        $view = $this->createView('ReportTab');
+        $view->assignMultiple([
+            'prefix' => 'report',
+            'selectedLevel' => $this->searchLevel['report'],
+            'options' => $this->getCheckOptions($amountOfBrokenLinks, 'report'),
+        ]);
+        // Table header
+        $view->assignMultiple($this->getVariablesForTableHeader());
 
         $linkTypes = [];
         if (is_array($this->checkOpt['report'])) {
             $linkTypes = array_keys($this->checkOpt['report'], '1');
         }
-
-        // Table header
-        $brokenLinksMarker = $this->startTable();
-
         $items = [];
         $rootLineHidden = $this->linkAnalyzer->getRootLineIsHidden($this->pObj->pageinfo);
         if (!$rootLineHidden || (bool)$this->modTS['checkhidden'] && !empty($linkTypes)) {
-            $pageList = $this->getPageList();
-            $brokenLinks = $this->brokenLinkRepository->getAllBrokenLinksForPages($pageList, $linkTypes);
-            // Display table with broken links
-            $brokenLinksItemTemplate = $this->templateService->getSubpart(
-                $this->doc->moduleTemplate,
-                '###BROKENLINKS_ITEM###'
-            );
-
-            // Table rows containing the broken links
+            $brokenLinks = $this->brokenLinkRepository->getAllBrokenLinksForPages($this->getPageList(), $linkTypes);
             foreach ($brokenLinks as $row) {
-                $items[] = $this->renderTableRow($row['table_name'], $row, $brokenLinksItemTemplate);
-            }
-
-            if (!empty($items)) {
-                $brokenLinksTemplate = $this->templateService->getSubpart(
-                    $this->doc->moduleTemplate,
-                    '###BROKENLINKS_CONTENT###'
-                );
-                $brokenLinkItems = implode(LF, $items);
+                $items[] = $this->renderTableRow($row['table_name'], $row);
             }
         }
         if (empty($items)) {
-            $brokenLinksMarker = $this->getNoBrokenLinkMessage($brokenLinksMarker);
+            $this->createFlashMessagesForNoBrokenLinks();
         }
-
-        $brokenLinksTemplate = $this->templateService->substituteMarkerArray(
-            $brokenLinksTemplate,
-            $brokenLinksMarker,
-            '###|###',
-            true
-        );
-
-        return $this->templateService->substituteSubpart($brokenLinksTemplate, '###BROKENLINKS_ITEM', $brokenLinkItems);
+        $view->assign('brokenLinks', $items);
+        return $view;
     }
 
     /**
@@ -521,59 +433,47 @@ class LinkValidatorReport
         // Always add the current page, because we are just displaying the results
         $pageList .= $this->id;
         $pageList = $this->addPageTranslationsToPageList($pageList, $permsClause);
-
         return GeneralUtility::intExplode(',', $pageList, true);
     }
 
     /**
-     * Replace $brokenLinksMarker['NO_BROKEN_LINKS] with localized flashmessage
-     *
-     * @param array $brokenLinksMarker
-     * @return array $brokenLinksMarker['NO_BROKEN_LINKS] replaced with flashmessage
+     * Used when there are no broken links found.
      */
-    protected function getNoBrokenLinkMessage(array $brokenLinksMarker)
+    protected function createFlashMessagesForNoBrokenLinks(): void
     {
-        $languageService = $this->getLanguageService();
-        $brokenLinksMarker['LIST_HEADER'] = '<h3>' . htmlspecialchars($languageService->getLL('list.header')) . '</h3>';
-        /** @var FlashMessage $message */
         $message = GeneralUtility::makeInstance(
             FlashMessage::class,
-            $languageService->getLL('list.no.broken.links'),
-            $languageService->getLL('list.no.broken.links.title'),
-            FlashMessage::OK
+            $this->getLanguageService()->getLL('list.no.broken.links'),
+            $this->getLanguageService()->getLL('list.no.broken.links.title'),
+            FlashMessage::OK,
+            false
         );
         $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
-        $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
+        $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier('linkvalidator');
         $defaultFlashMessageQueue->enqueue($message);
-        $brokenLinksMarker['NO_BROKEN_LINKS'] = $defaultFlashMessageQueue->renderFlashMessages();
-        return $brokenLinksMarker;
     }
 
     /**
-     * Displays the table header of the table with the broken links
-     *
-     * @return array Code of content
+     * Sets variables for the Fluid Template of the table with the broken links
+     * @return array variables
      */
-    protected function startTable()
+    protected function getVariablesForTableHeader(): array
     {
         $languageService = $this->getLanguageService();
-        // Listing head
-        $makerTableHead = [
-            'tablehead_path' => $languageService->getLL('list.tableHead.path'),
-            'tablehead_element' => $languageService->getLL('list.tableHead.element'),
-            'tablehead_headlink' => $languageService->getLL('list.tableHead.headlink'),
-            'tablehead_linktarget' => $languageService->getLL('list.tableHead.linktarget'),
-            'tablehead_linkmessage' => $languageService->getLL('list.tableHead.linkmessage'),
-            'tablehead_lastcheck' => $languageService->getLL('list.tableHead.lastCheck'),
+        $variables = [
+            'tableheadPath' => $languageService->getLL('list.tableHead.path'),
+            'tableheadElement' => $languageService->getLL('list.tableHead.element'),
+            'tableheadHeadlink' => $languageService->getLL('list.tableHead.headlink'),
+            'tableheadLinktarget' => $languageService->getLL('list.tableHead.linktarget'),
+            'tableheadLinkmessage' => $languageService->getLL('list.tableHead.linkmessage'),
+            'tableheadLastcheck' => $languageService->getLL('list.tableHead.lastCheck'),
         ];
 
         // Add CSH to the header of each column
-        foreach ($makerTableHead as $column => $label) {
-            $makerTableHead[$column] = BackendUtility::wrapInHelp('linkvalidator', $column, $label);
+        foreach ($variables as $column => $label) {
+            $variables[$column] = BackendUtility::wrapInHelp('linkvalidator', $column, $label);
         }
-        // Add section header
-        $makerTableHead['list_header'] = '<h3>' . htmlspecialchars($languageService->getLL('list.header')) . '</h3>';
-        return $makerTableHead;
+        return $variables;
     }
 
     /**
@@ -581,13 +481,12 @@ class LinkValidatorReport
      *
      * @param string $table Name of database table
      * @param array $row Record row to be processed
-     * @param string $brokenLinksItemTemplate Markup of the template to be used
-     * @return string HTML of the rendered row
+     * @return array HTML of the rendered row
      */
-    protected function renderTableRow($table, array $row, $brokenLinksItemTemplate)
+    protected function renderTableRow($table, array $row)
     {
         $languageService = $this->getLanguageService();
-        $markerArray = [];
+        $variables = [];
         $fieldName = '';
         // Restore the linktype object
         $hookObj = $this->hookObjectsArr[$row['link_type']];
@@ -612,9 +511,7 @@ class LinkValidatorReport
             'columnsOnly' => $row['field'],
             'returnUrl' => $requestUri
         ]);
-        $actionLinkOpen = '<a href="' . htmlspecialchars($url);
-        $actionLinkOpen .= '" title="' . htmlspecialchars($languageService->getLL('list.edit')) . '">';
-        $actionLinkClose = '</a>';
+        $variables['editUrl'] = $url;
         $elementHeadline = $row['headline'];
         // Get the language label for the field from TCA
         if ($GLOBALS['TCA'][$table]['columns'][$row['field']]['label']) {
@@ -634,13 +531,10 @@ class LinkValidatorReport
             $element .= htmlspecialchars($elementHeadline);
         }
         $element .= ' ' . htmlspecialchars(sprintf($languageService->getLL('list.field'), $fieldName));
-        $markerArray['actionlinkOpen'] = $actionLinkOpen;
-        $markerArray['actionlinkClose'] = $actionLinkClose;
-        $markerArray['actionlinkIcon'] = $this->iconFactory->getIcon('actions-open', Icon::SIZE_SMALL)->render();
-        $markerArray['path'] = BackendUtility::getRecordPath($row['record_pid'], '', 0, 0);
-        $markerArray['element'] = $element;
-        $markerArray['headlink'] = htmlspecialchars($row['link_title']);
-        $markerArray['linktarget'] = htmlspecialchars($hookObj->getBrokenUrl($row));
+        $variables['element'] = $element;
+        $variables['path'] = BackendUtility::getRecordPath($row['record_pid'], '', 0, 0);
+        $variables['link_title'] = $row['link_title'];
+        $variables['linktarget'] = $hookObj->getBrokenUrl($row);
         $response = $row['url_response'];
         if ($response['valid']) {
             $linkMessage = '<span class="valid">' . htmlspecialchars($languageService->getLL('list.msg.ok')) . '</span>';
@@ -657,162 +551,44 @@ class LinkValidatorReport
                 )
                 . '</span>';
         }
-        $markerArray['linkmessage'] = $linkMessage;
-
+        $variables['linkmessage'] = $linkMessage;
         $lastRunDate = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'], $row['last_check']);
         $lastRunTime = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'], $row['last_check']);
-        $markerArray['lastcheck'] = htmlspecialchars(sprintf($languageService->getLL('list.msg.lastRun'), $lastRunDate, $lastRunTime));
-        if ($row['needs_recheck']) {
-            $markerArray['lastcheck'] .= '<br/><span class="error"> (' . htmlspecialchars($languageService->getLL('needs-recheck')) . ')</span>';
-        }
-
-        // Return the table html code as string
-        return $this->templateService->substituteMarkerArray($brokenLinksItemTemplate, $markerArray, '###|###', true, true);
+        $variables['lastcheck'] = htmlspecialchars(sprintf($languageService->getLL('list.msg.lastRun'), $lastRunDate, $lastRunTime));
+        $variables['needs_recheck'] = (bool)$row['needs_recheck'];
+        return $variables;
     }
 
     /**
-     * Builds the checkboxes out of the hooks array
+     * Builds the checkboxes to show which types of links are available
      *
      * @param array $brokenLinkOverView Array of broken links information
      * @param string $prefix "report" or "check" for "Report" and "Check links" tab
-     * @return string code content
+     * @return array
      */
     protected function getCheckOptions(array $brokenLinkOverView, $prefix)
     {
-        $languageService = $this->getLanguageService();
-        $markerArray = [];
-        if (!empty($prefix)) {
-            $additionalAttr = ' class="' . $prefix . '"';
-        } else {
-            $additionalAttr = ' class="refresh"';
-        }
-        $checkOptionsTemplate = $this->templateService->getSubpart($this->doc->moduleTemplate, '###CHECKOPTIONS_SECTION###');
-        $hookSectionTemplate = $this->templateService->getSubpart($checkOptionsTemplate, '###HOOK_SECTION###');
-        $markerArray['statistics_header'] = '<h3>' . htmlspecialchars($languageService->getLL('report.statistics.header')) . '</h3>';
-        $markerArray['total_count_label'] = BackendUtility::wrapInHelp('linkvalidator', 'checkboxes', $languageService->getLL('overviews.nbtotal'));
-        $markerArray['total_count'] = $brokenLinkOverView['brokenlinkCount'] ?: '0';
-
-        $linktypes = GeneralUtility::trimExplode(',', $this->modTS['linktypes'], true);
-        $hookSectionContent = '';
-        if (is_array($linktypes)) {
-            if (
-                !empty($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'])
-                && is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'])
-            ) {
-                foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'] as $type => $value) {
-                    if (in_array($type, $linktypes)) {
-                        $hookSectionMarker = [
-                            'count' => $brokenLinkOverView[$type] ?: '0',
-                        ];
-
-                        $translation = $languageService->getLL('hooks.' . $type) ?: $type;
-
-                        $checked = $this->checkOpt[$prefix][$type] ? 'checked="checked"' : '';
-
-                        $hookSectionMarker['option'] = '<input type="checkbox"' . $additionalAttr
-                            . ' id="' . $prefix . '_SET_' . $type
-                            . '" name="' . $prefix . '_SET[' . $type . ']" value="1"'
-                            . ' ' . $checked . '/><label for="'
-                            . $prefix . '_SET_' . $type . '">&nbsp;' . htmlspecialchars($translation) . '</label>';
-
-                        $hookSectionContent .= $this->templateService->substituteMarkerArray(
-                            $hookSectionTemplate,
-                            $hookSectionMarker,
-                            '###|###',
-                            true,
-                            true
-                        );
-                    }
-                }
+        $variables = [];
+        $variables['totalCountLabel'] = BackendUtility::wrapInHelp('linkvalidator', 'checkboxes', $this->getLanguageService()->getLL('overviews.nbtotal'));
+        $variables['totalCount'] = $brokenLinkOverView['brokenlinkCount'] ?: '0';
+        $variables['optionsByType'] = [];
+        $linkTypes = GeneralUtility::trimExplode(',', $this->modTS['linktypes'] ?? '', true);
+        $availableLinkTypes = array_keys($this->hookObjectsArr);
+        foreach ($availableLinkTypes as $type) {
+            if (!in_array($type, $linkTypes, true)) {
+                continue;
             }
+            $label = $this->getLanguageService()->getLL('hooks.' . $type) ?: $type;
+            $variables['optionsByType'][$type] = [
+                'count' => $brokenLinkOverView[$type] ?: '0',
+                'checkbox' => '<input type="checkbox" class="' . $prefix . '"'
+                    . ' id="' . $prefix . '_SET_' . $type
+                    . '" name="' . $prefix . '_SET[' . $type . ']" value="1"'
+                    . ' ' . ($this->checkOpt[$prefix][$type] ? 'checked="checked"' : '') . '/>',
+                'label' => '<label for="' . $prefix . '_SET_' . $type . '">&nbsp;' . htmlspecialchars($label) . '</label>'
+            ];
         }
-        $checkOptionsTemplate = $this->templateService->substituteSubpart(
-            $checkOptionsTemplate,
-            '###HOOK_SECTION###',
-            $hookSectionContent
-        );
-
-        // set this to signal that $prefix_SET variables should be used
-        $checkOptionsTemplate .= '<input type="hidden" name="' . $prefix . '_values" value="1">';
-
-        return $this->templateService->substituteMarkerArray($checkOptionsTemplate, $markerArray, '###|###', true, true);
-    }
-
-    /**
-     * Gets the buttons that shall be rendered in the docHeader
-     *
-     * @return array Available buttons for the docHeader
-     */
-    protected function getDocHeaderButtons()
-    {
-        return [
-            'csh' => BackendUtility::cshItem('_MOD_web_func', ''),
-            'shortcut' => $this->getShortcutButton(),
-            'save' => ''
-        ];
-    }
-
-    /**
-     * Gets the button to set a new shortcut in the backend (if current user is allowed to).
-     *
-     * @return string HTML representation of the shortcut button
-     */
-    protected function getShortcutButton()
-    {
-        $result = '';
-        if ($this->getBackendUser()->mayMakeShortcut()) {
-            $result = $this->doc->makeShortcutIcon('', 'function', 'web_info');
-        }
-        return $result;
-    }
-
-    /**
-     * Gets the filled markers that are used in the HTML template
-     * Reports tab
-     *
-     * @return array The filled marker array
-     */
-    protected function getTemplateMarkers()
-    {
-        $languageService = $this->getLanguageService();
-        return [
-            'FUNC_TITLE' => $languageService->getLL('report.func.title'),
-            'CHECKOPTIONS_TITLE' => $languageService->getLL('report.statistics.header'),
-            'FUNC_MENU' => $this->getLevelSelector('report'),
-            'CONTENT' => $this->content,
-            'CHECKOPTIONS' => $this->checkOptionsHtml['report'],
-            'ID' => '<input type="hidden" name="id" value="' . $this->id . '" />',
-            'REFRESH' => '<input type="submit" class="btn btn-default t3js-update-button" name="refreshLinkList" id="refreshLinkList" value="'
-                . htmlspecialchars($languageService->getLL('label_refresh'))
-                . '" data-notification-message="'
-                . htmlspecialchars($languageService->getLL('label_refresh-link-list')) . '" />',
-            'UPDATE' => '',
-        ];
-    }
-
-    /**
-     * Gets the filled markers that are used in the HTML template
-     * Check Links tab
-     *
-     * @return array The filled marker array
-     */
-    protected function getTemplateMarkersCheck()
-    {
-        $languageService = $this->getLanguageService();
-        return [
-            'FUNC_TITLE' => $languageService->getLL('checklinks.func.title'),
-            'CHECKOPTIONS_TITLE' => $languageService->getLL('checklinks.statistics.header'),
-            'FUNC_MENU' => $this->getLevelSelector('check'),
-            'CONTENT' => '',
-            'CHECKOPTIONS' => $this->checkOptionsHtml['check'],
-            'ID' => '<input type="hidden" name="id" value="' . $this->id . '" />',
-            'REFRESH' => '',
-            'UPDATE' => '<input type="submit" class="btn btn-default t3js-update-button" name="updateLinkList" id="updateLinkList" value="'
-                . htmlspecialchars($languageService->getLL('label_update'))
-                . '" data-notification-message="'
-                . htmlspecialchars($languageService->getLL('label_update-link-list'))
-                . '"/>',
-        ];
+        return $variables;
     }
 
     /**
@@ -831,19 +607,6 @@ class LinkValidatorReport
         return $GLOBALS['BE_USER'];
     }
 
-    /**
-     * @return PageRenderer
-     */
-    protected function getPageRenderer(): PageRenderer
-    {
-        return GeneralUtility::makeInstance(PageRenderer::class);
-    }
-
-    /**
-     * @param string $theList
-     * @param string $permsClause
-     * @return string
-     */
     protected function addPageTranslationsToPageList(string $theList, string $permsClause): string
     {
         $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
diff --git a/typo3/sysext/linkvalidator/Resources/Private/Layouts/TabContent.html b/typo3/sysext/linkvalidator/Resources/Private/Layouts/TabContent.html
new file mode 100644
index 0000000000000000000000000000000000000000..222e05b9bd5d7f44af542a867b8bd0e582ab82e9
--- /dev/null
+++ b/typo3/sysext/linkvalidator/Resources/Private/Layouts/TabContent.html
@@ -0,0 +1,39 @@
+<div class="linkvalidator-check-options">
+    <input type="hidden" name="id" value="{pageId}" />
+    <h4><f:render section="OptionsTitle" /></h4>
+    <div>
+        <table class="table table-striped table-hover" id="linkvalidator-statistics-table">
+            <thead>
+            <tr>
+                <th>{options.totalCountLabel -> f:format.raw()}</th>
+                <th class="number"><span class="badge">{options.totalCount}</span></th>
+            </tr>
+            </thead>
+            <tbody>
+            <f:for each="{options.optionsByType}" as="optionByType">
+                <tr>
+                    <td>{optionByType.checkbox -> f:format.raw()}{optionByType.label -> f:format.raw()}</td>
+                    <td class="number"><span class="badge">{optionByType.count}</span></td>
+                </tr>
+            </f:for>
+            </tbody>
+        </table>
+        <input type="hidden" name="{prefix}_values" value="1">
+    </div>
+</div>
+<div class="linkvalidator-function-menu">
+    <h4><f:render section="levelTitle" /></h4>
+    <div>
+        <select name="{prefix}_search_levels" class="form-control">
+            <option value="0"{f:if(condition: '{selectedLevel} == 0', then: 'selected')}><f:translate key="LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_0" /></option>
+            <option value="1"{f:if(condition: '{selectedLevel} == 1', then: 'selected')}><f:translate key="LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_0" /></option>
+            <option value="2"{f:if(condition: '{selectedLevel} == 2', then: 'selected')}><f:translate key="LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_0" /></option>
+            <option value="3"{f:if(condition: '{selectedLevel} == 3', then: 'selected')}><f:translate key="LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_0" /></option>
+            <option value="4"{f:if(condition: '{selectedLevel} == 4', then: 'selected')}><f:translate key="LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_0" /></option>
+            <option value="999"{f:if(condition: '{selectedLevel} == 999', then: 'selected')}><f:translate key="LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_infi" /></option>
+        </select>
+    </div>
+</div>
+<div class="linkvalidator-button"><f:render section="Button" /></div>
+
+<f:render section="Main" optional="true" />
diff --git a/typo3/sysext/linkvalidator/Resources/Private/Templates/Backend/CheckLinksTab.html b/typo3/sysext/linkvalidator/Resources/Private/Templates/Backend/CheckLinksTab.html
new file mode 100644
index 0000000000000000000000000000000000000000..34bbfe4a39afa44709ae12be923e9f4b80ebee8d
--- /dev/null
+++ b/typo3/sysext/linkvalidator/Resources/Private/Templates/Backend/CheckLinksTab.html
@@ -0,0 +1,12 @@
+<f:layout name="TabContent" />
+
+<f:section name="OptionsTitle">
+    <f:translate key="LLL:EXT:linkvalidator/Resources/Private/Language/Module/locallang.xlf:checklinks.statistics.header" />
+</f:section>
+<f:section name="levelTitle">
+    <f:translate key="LLL:EXT:linkvalidator/Resources/Private/Language/Module/locallang.xlf:checklinks.func.title" />
+</f:section>
+<f:section name="Button">
+    <input type="submit" class="btn btn-default t3js-update-button" name="updateLinkList" id="updateLinkList" value="{f:translate(key: 'LLL:EXT:linkvalidator/Resources/Private/Language/Module/locallang.xlf:label_update')}"
+           data-notification-message="{f:translate(key: 'LLL:EXT:linkvalidator/Resources/Private/Language/Module/locallang.xlf:label_update-link-list')}" />
+</f:section>
diff --git a/typo3/sysext/linkvalidator/Resources/Private/Templates/Backend/InfoModule.html b/typo3/sysext/linkvalidator/Resources/Private/Templates/Backend/InfoModule.html
new file mode 100644
index 0000000000000000000000000000000000000000..26c690bc2c175e8930608c3ade26b324f21234bd
--- /dev/null
+++ b/typo3/sysext/linkvalidator/Resources/Private/Templates/Backend/InfoModule.html
@@ -0,0 +1,6 @@
+<div id="linkvalidator-modfuncreport">
+    <f:if condition="{title}">
+        <h1>{title}</h1>
+    </f:if>
+    {content -> f:format.raw()}
+</div>
diff --git a/typo3/sysext/linkvalidator/Resources/Private/Templates/Backend/ReportTab.html b/typo3/sysext/linkvalidator/Resources/Private/Templates/Backend/ReportTab.html
new file mode 100644
index 0000000000000000000000000000000000000000..047b3ac5ed810fd931b8899dbf2bd2531069d9f0
--- /dev/null
+++ b/typo3/sysext/linkvalidator/Resources/Private/Templates/Backend/ReportTab.html
@@ -0,0 +1,49 @@
+<f:layout name="TabContent" />
+
+<f:section name="OptionsTitle">
+    <f:translate key="LLL:EXT:linkvalidator/Resources/Private/Language/Module/locallang.xlf:report.statistics.header" />
+</f:section>
+<f:section name="levelTitle">
+    <f:translate key="LLL:EXT:linkvalidator/Resources/Private/Language/Module/locallang.xlf:report.func.title" />
+</f:section>
+<f:section name="Main">
+    <h3><f:translate key="LLL:EXT:linkvalidator/Resources/Private/Language/Module/locallang.xlf:list.header" /></h3>
+    <f:if condition="{brokenLinks -> f:count()}">
+        <f:then>
+            <table class="table table-striped table-hover" id="linkvalidator-broken-links-table">
+                <thead>
+                <tr>
+                    <th>{tableheadElement -> f:format.raw()}</th>
+                    <th>{tableheadPath -> f:format.raw()}</th>
+                    <th>{tableheadHeadlink -> f:format.raw()}</th>
+                    <th>{tableheadLinktarget -> f:format.raw()}</th>
+                    <th>{tableheadLinkmessage -> f:format.raw()}</th>
+                    <th>{tableheadLastcheck -> f:format.raw()}</th>
+                    <th></th>
+                </tr>
+                </thead>
+                <tbody>
+                <f:for each="{brokenLinks}" as="item">
+                    <tr>
+                        <td><a href="{item.editUrl}" title="{f:translate(key: 'LLL:EXT:linkvalidator/Resources/Private/Language/Module/locallang.xlf:list.edit')}">{item.element -> f:format.raw()}</a></td>
+                        <td>{item.path -> f:format.raw()}</td>
+                        <td>{item.link_title -> f:format.raw()}</td>
+                        <td><a href="{item.linktarget}" target="_blank" rel="noreferrer">{item.linktarget}</a></td>
+                        <td>{item.linkmessage -> f:format.raw()}</td>
+                        <td>{item.lastcheck -> f:format.raw()}<f:if condition="{item.needsRecheck}"><br /><span class="error"> ({f:translate(key: 'LLL:EXT:linkvalidator/Resources/Private/Language/Module/locallang.xlf:needs-recheck')})</span></f:if>
+                        </td>
+                        <td><a href="{item.editUrl}" title="{f:translate(key: 'LLL:EXT:linkvalidator/Resources/Private/Language/Module/locallang.xlf:list.edit')}"><core:icon identifier="actions-open" size="small" /></a></td>
+                    </tr>
+                </f:for>
+                </tbody>
+            </table>
+        </f:then>
+        <f:else>
+            <f:flashMessages queueIdentifier="linkvalidator" />
+        </f:else>
+    </f:if>
+</f:section>
+<f:section name="Button">
+    <input type="submit" class="btn btn-default t3js-update-button" name="refreshLinkList" id="refreshLinkList" value="{f:translate(key: 'LLL:EXT:linkvalidator/Resources/Private/Language/Module/locallang.xlf:label_refresh')}"
+           data-notification-message="{f:translate(key: 'LLL:EXT:linkvalidator/Resources/Private/Language/Module/locallang.xlf:label_refresh-link-list')}" />
+</f:section>
diff --git a/typo3/sysext/linkvalidator/Resources/Private/Templates/mod_template.html b/typo3/sysext/linkvalidator/Resources/Private/Templates/mod_template.html
deleted file mode 100644
index f722a3ada2d2ca842d17a5043afdb92e29f276fd..0000000000000000000000000000000000000000
--- a/typo3/sysext/linkvalidator/Resources/Private/Templates/mod_template.html
+++ /dev/null
@@ -1,65 +0,0 @@
-<!-- ###FULLDOC### begin -->
-<div class="linkvalidator-check-options">
-    ###ID###
-    <h4>###CHECKOPTIONS_TITLE###</h4>
-    <div>###CHECKOPTIONS###</div>
-</div>
-<div class="linkvalidator-function-menu"><h4>###FUNC_TITLE###</h4><div>###FUNC_MENU###</div></div>
-<div class="linkvalidator-button">###REFRESH######UPDATE###</div>
-
-###CONTENT###
-<!-- ###FULLDOC### end -->
-
-<!-- ###CHECKOPTIONS_SECTION### begin -->
-<table class="table table-striped table-hover" id="linkvalidator-statistics-table">
-    <thead>
-        <tr>
-            <th>###TOTAL_COUNT_LABEL###</th>
-            <th class="number"><span class="badge">###TOTAL_COUNT###</span></th>
-        </tr>
-    </thead>
-    <tbody>
-        <!-- ###HOOK_SECTION### begin -->
-        <tr>
-            <td>###OPTION###</td>
-            <td class="number"><span class="badge">###COUNT###</span></td>
-        </tr>
-        <!-- ###HOOK_SECTION### end -->
-    </tbody>
-</table>
-<!-- ###CHECKOPTIONS_SECTION### end -->
-
-<!-- ###BROKENLINKS_CONTENT### begin -->
-###LIST_HEADER###
-<table class="table table-striped table-hover" id="linkvalidator-broken-links-table">
-    <thead>
-        <tr>
-            <th>###TABLEHEAD_ELEMENT###</th>
-            <th>###TABLEHEAD_PATH###</th>
-            <th>###TABLEHEAD_HEADLINK###</th>
-            <th>###TABLEHEAD_LINKTARGET###</th>
-            <th>###TABLEHEAD_LINKMESSAGE###</th>
-            <th>###TABLEHEAD_LASTCHECK###</th>
-            <th></th>
-        </tr>
-    </thead>
-    <tbody>
-        <!-- ###BROKENLINKS_ITEM### begin -->
-        <tr>
-            <td>###ACTIONLINKOPEN######ELEMENT######ACTIONLINKCLOSE###</td>
-            <td>###PATH###</td>
-            <td>###HEADLINK###</td>
-            <td><a href="###LINKTARGET###" target="_blank" rel="noreferrer">###LINKTARGET###</a></td>
-            <td>###LINKMESSAGE###</td>
-            <td>###LASTCHECK###</td>
-            <td>###ACTIONLINKOPEN######ACTIONLINKICON######ACTIONLINKCLOSE###</td>
-        </tr>
-        <!-- ###BROKENLINKS_ITEM### end -->
-    </tbody>
-</table>
-<!-- ###BROKENLINKS_CONTENT### end -->
-
-<!-- ###NOBROKENLINKS_CONTENT### begin -->
-###LIST_HEADER###
-###NO_BROKEN_LINKS###
-<!-- ###NOBROKENLINKS_CONTENT### end -->