diff --git a/typo3/sysext/fluid/Classes/ViewHelpers/Uri/ResourceViewHelper.php b/typo3/sysext/fluid/Classes/ViewHelpers/Uri/ResourceViewHelper.php
index bef5da513cd423022883eb941391274860cb2be3..d4116a6a74aa89e6b68ac0d419d33a19f503c291 100644
--- a/typo3/sysext/fluid/Classes/ViewHelpers/Uri/ResourceViewHelper.php
+++ b/typo3/sysext/fluid/Classes/ViewHelpers/Uri/ResourceViewHelper.php
@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 /*
  * This file is part of the TYPO3 CMS project.
  *
@@ -15,8 +17,11 @@
 
 namespace TYPO3\CMS\Fluid\ViewHelpers\Uri;
 
+use TYPO3\CMS\Core\Resource\Exception\InvalidFileException;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Core\Utility\PathUtility;
+use TYPO3\CMS\Extbase\Mvc\RequestInterface;
+use TYPO3\CMS\Fluid\Core\Rendering\RenderingContext;
 use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
 use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
 use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
@@ -27,38 +32,50 @@ use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
  * Examples
  * ========
  *
+ * Best practice with EXT: syntax
+ * ------------------------------
+ *
+ * ::
+ *
+ *    <link href="{f:uri.resource(path:'EXT:indexed_search/Resources/Public/Css/Stylesheet.css')}" rel="stylesheet" />
+ *
+ * Output::
+ *
+ *    <link href="typo3/sysext/indexed_search/Resources/Public/Css/Stylesheet.css" rel="stylesheet" />
+ *
+ * Preferred syntax that works in both extbase and non-extbase context.
+ *
  * Defaults
  * --------
  *
  * ::
  *
- *    <link href="{f:uri.resource(path:'css/stylesheet.css')}" rel="stylesheet" />
+ *    <link href="{f:uri.resource(path:'Css/Stylesheet.css')}" rel="stylesheet" />
  *
  * Output::
  *
- *    <link href="typo3conf/ext/example_extension/Resources/Public/css/stylesheet.css" rel="stylesheet" />
+ *    <link href="typo3conf/ext/example_extension/Resources/Public/Css/Stylesheet.css" rel="stylesheet" />
  *
- * Depending on current extension.
+ * Works only in extbase context since it uses the extbase request to find current extension, magically adds 'Resources/Public' to path.
  *
  * With extension name
  * -------------------
  *
  * ::
  *
- *    <link href="{f:uri.resource(path:'css/stylesheet.css', extensionName: 'AnotherExtension')}" rel="stylesheet" />
+ *    <link href="{f:uri.resource(path:'Css/Stylesheet.css', extensionName: 'AnotherExtension')}" rel="stylesheet" />
  *
  * Output::
  *
- *    <link href="typo3conf/ext/another_extension/Resources/Public/css/stylesheet.css" rel="stylesheet" />
+ *    <link href="typo3conf/ext/another_extension/Resources/Public/Css/Stylesheet.css" rel="stylesheet" />
+ *
+ * Magically adds 'Resources/Public' to path.
  */
 final class ResourceViewHelper extends AbstractViewHelper
 {
     use CompileWithRenderStatic;
 
-    /**
-     * Initialize arguments
-     */
-    public function initializeArguments()
+    public function initializeArguments(): void
     {
         $this->registerArgument('path', 'string', 'The path and filename of the resource (relative to Public resource directory of the extension).', true);
         $this->registerArgument('extensionName', 'string', 'Target extension name. If not set, the current extension name will be used');
@@ -68,24 +85,101 @@ final class ResourceViewHelper extends AbstractViewHelper
     /**
      * Render the URI to the resource. The filename is used from child content.
      *
+     * @return string The URI to the resource
+     * @throws InvalidFileException
+     * @throws \RuntimeException
+     */
+    public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext): string
+    {
+        $uri = PathUtility::getPublicResourceWebPath(self::resolveExtensionPath($arguments, $renderingContext));
+        if ($arguments['absolute']) {
+            $uri = GeneralUtility::locationHeaderUrl($uri);
+        }
+
+        return $uri;
+    }
+
+    /**
+     * Resolves the extension path, either directly when possible, or from extension name and request
+     *
      * @param array $arguments
-     * @param \Closure $renderChildrenClosure
      * @param RenderingContextInterface $renderingContext
-     * @return string The URI to the resource
+     * @return string
      */
-    public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
+    private static function resolveExtensionPath(array $arguments, RenderingContextInterface $renderingContext): string
     {
         $path = $arguments['path'];
-        $extensionName = $arguments['extensionName'];
-        $absolute = $arguments['absolute'];
+        if (PathUtility::isExtensionPath($path)) {
+            return $path;
+        }
+
+        return sprintf(
+            'EXT:%s/Resources/Public/%s',
+            self::resolveExtensionKey($arguments, $renderingContext),
+            ltrim($path, '/')
+        );
+    }
 
+    /**
+     * Resolves extension key either from given extension name argument or from request
+     *
+     * @param array $arguments
+     * @param RenderingContextInterface $renderingContext
+     * @return string
+     */
+    private static function resolveExtensionKey(array $arguments, RenderingContextInterface $renderingContext): string
+    {
+        $extensionName = $arguments['extensionName'];
         if ($extensionName === null) {
-            $extensionName = $renderingContext->getRequest()->getControllerExtensionName();
+            return self::resolveValidatedRequest($arguments, $renderingContext)->getControllerExtensionKey();
         }
-        $uri = PathUtility::getPublicResourceWebPath('EXT:' . GeneralUtility::camelCaseToLowerCaseUnderscored($extensionName) . '/Resources/Public/' . $path);
-        if ($absolute === true) {
-            $uri = GeneralUtility::locationHeaderUrl($uri);
+
+        return GeneralUtility::camelCaseToLowerCaseUnderscored($extensionName);
+    }
+
+    /**
+     * Resolves and validates the request from rendering context
+     *
+     * @param array $arguments
+     * @param RenderingContextInterface $renderingContext
+     * @return RequestInterface
+     */
+    private static function resolveValidatedRequest(array $arguments, RenderingContextInterface $renderingContext): RequestInterface
+    {
+        if (!$renderingContext instanceof RenderingContext) {
+            throw new \RuntimeException(
+                sprintf(
+                    'RenderingContext must be instance of "%s", but is instance of "%s"',
+                    RenderingContext::class,
+                    get_class($renderingContext)
+                ),
+                1640095993
+            );
         }
-        return $uri;
+        $request = $renderingContext->getRequest();
+        if (!$request instanceof RequestInterface) {
+            throw new \RuntimeException(
+                sprintf(
+                    'ViewHelper f:uri.resource needs an Extbase Request object to resolve extension name for given path "%s".'
+                    . ' If not in Extbase context, either set argument "extensionName",'
+                    . ' or (better) use the standard EXT: syntax for path attribute like \'path="EXT:indexed_search/Resources/Public/Icons/Extension.svg"\'.',
+                    $arguments['path']
+                ),
+                1639672666
+            );
+        }
+        if ($request->getControllerExtensionKey() === '') {
+            throw new \RuntimeException(
+                sprintf(
+                    'Can not resolve extension key for given path "%s".'
+                    . ' If not in Extbase context, either set argument "extensionName",'
+                    . ' or (better) use the standard EXT: syntax for path attribute like \'path="EXT:indexed_search/Resources/Public/Icons/Extension.svg"\'.',
+                    $arguments['path']
+                ),
+                1640097205
+            );
+        }
+
+        return $request;
     }
 }
diff --git a/typo3/sysext/fluid/Tests/Functional/ViewHelpers/Uri/ResourceViewHelperTest.php b/typo3/sysext/fluid/Tests/Functional/ViewHelpers/Uri/ResourceViewHelperTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..0d1099a8e4af54017e16081a05037abf8404787e
--- /dev/null
+++ b/typo3/sysext/fluid/Tests/Functional/ViewHelpers/Uri/ResourceViewHelperTest.php
@@ -0,0 +1,87 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+namespace TYPO3\CMS\Fluid\Tests\Functional\ViewHelpers\Uri;
+
+use TYPO3\CMS\Extbase\Mvc\ExtbaseRequestParameters;
+use TYPO3\CMS\Extbase\Mvc\Request;
+use TYPO3\CMS\Fluid\View\StandaloneView;
+use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
+
+class ResourceViewHelperTest extends FunctionalTestCase
+{
+    /**
+     * @var bool Speed up this test case, it needs no database
+     */
+    protected $initializeDatabase = false;
+
+    public function renderWithExtbaseRequestDataProvider(): \Generator
+    {
+        yield 'render returns URI using extensionName from Extbase Request' => [
+            '<f:uri.resource path="Icons/Extension.svg" />',
+            'typo3/sysext/core/Resources/Public/Icons/Extension.svg',
+        ];
+
+        yield 'render gracefully trims leading slashes from path' => [
+            '<f:uri.resource path="/Icons/Extension.svg" />',
+            'typo3/sysext/core/Resources/Public/Icons/Extension.svg',
+        ];
+
+        yield 'render returns URI using UpperCamelCase extensionName' => [
+            '<f:uri.resource path="Icons/Extension.svg" extensionName="Core" />',
+            'typo3/sysext/core/Resources/Public/Icons/Extension.svg',
+        ];
+
+        yield 'render returns URI using extension key as extensionName' => [
+            '<f:uri.resource path="Icons/Extension.svg" extensionName="core" />',
+            'typo3/sysext/core/Resources/Public/Icons/Extension.svg',
+        ];
+
+        yield 'render returns URI using EXT: syntax' => [
+            '<f:uri.resource path="EXT:core/Resources/Public/Icons/Extension.svg" />',
+            'typo3/sysext/core/Resources/Public/Icons/Extension.svg',
+        ];
+    }
+
+    /**
+     * @test
+     * @dataProvider renderWithExtbaseRequestDataProvider
+     */
+    public function renderWithExtbaseRequest(string $template, string $expected): void
+    {
+        $view = new StandaloneView();
+        $view->setTemplateSource($template);
+        $extbaseRequestParameters = new ExtbaseRequestParameters();
+        $extbaseRequestParameters->setControllerExtensionName('Core');
+        $extbaseRequest = (new Request())->withAttribute('extbase', $extbaseRequestParameters);
+        $view->getRenderingContext()->setRequest($extbaseRequest);
+        self::assertEquals($expected, $view->render());
+    }
+
+    /**
+     * @test
+     */
+    public function renderingFailsWhenExtensionNameNotSetInRequest(): void
+    {
+        $view = new StandaloneView();
+        $view->setTemplateSource('<f:uri.resource path="Icons/Extension.svg" />');
+
+        $this->expectException(\RuntimeException::class);
+        $this->expectExceptionCode(1640097205);
+        $view->render();
+    }
+}