diff --git a/CHANGES.rst b/CHANGES.rst
index 0908a02d24..9a5f7498da 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -6,6 +6,8 @@ Unreleased
 -   The default ``hashlib.sha1`` may not be available in FIPS builds. Don't
     access it at import time so the developer has time to change the default.
     :issue:`5448`
+-   Don't initialize the ``cli`` attribute in the sansio scaffold, but rather in
+    the ``Flask`` concrete class. :pr:`5270`
 
 
 Version 3.0.2
diff --git a/src/flask/app.py b/src/flask/app.py
index 12ac50d49b..7622b5e838 100644
--- a/src/flask/app.py
+++ b/src/flask/app.py
@@ -241,6 +241,16 @@ def __init__(
             root_path=root_path,
         )
 
+        #: The Click command group for registering CLI commands for this
+        #: object. The commands are available from the ``flask`` command
+        #: once the application has been discovered and blueprints have
+        #: been registered.
+        self.cli = cli.AppGroup()
+
+        # Set the name of the Click group in case someone wants to add
+        # the app's commands to another CLI tool.
+        self.cli.name = self.name
+
         # Add a static route using the provided static_url_path, static_host,
         # and static_folder if there is a configured static_folder.
         # Note we do this without checking if static_folder exists.
diff --git a/src/flask/blueprints.py b/src/flask/blueprints.py
index 52859b855a..aa9eacf21b 100644
--- a/src/flask/blueprints.py
+++ b/src/flask/blueprints.py
@@ -4,16 +4,54 @@
 import typing as t
 from datetime import timedelta
 
+from .cli import AppGroup
 from .globals import current_app
 from .helpers import send_from_directory
 from .sansio.blueprints import Blueprint as SansioBlueprint
 from .sansio.blueprints import BlueprintSetupState as BlueprintSetupState  # noqa
+from .sansio.scaffold import _sentinel
 
 if t.TYPE_CHECKING:  # pragma: no cover
     from .wrappers import Response
 
 
 class Blueprint(SansioBlueprint):
+    def __init__(
+        self,
+        name: str,
+        import_name: str,
+        static_folder: str | os.PathLike[str] | None = None,
+        static_url_path: str | None = None,
+        template_folder: str | os.PathLike[str] | None = None,
+        url_prefix: str | None = None,
+        subdomain: str | None = None,
+        url_defaults: dict[str, t.Any] | None = None,
+        root_path: str | None = None,
+        cli_group: str | None = _sentinel,  # type: ignore
+    ) -> None:
+        super().__init__(
+            name,
+            import_name,
+            static_folder,
+            static_url_path,
+            template_folder,
+            url_prefix,
+            subdomain,
+            url_defaults,
+            root_path,
+            cli_group,
+        )
+
+        #: The Click command group for registering CLI commands for this
+        #: object. The commands are available from the ``flask`` command
+        #: once the application has been discovered and blueprints have
+        #: been registered.
+        self.cli = AppGroup()
+
+        # Set the name of the Click group in case someone wants to add
+        # the app's commands to another CLI tool.
+        self.cli.name = self.name
+
     def get_send_file_max_age(self, filename: str | None) -> int | None:
         """Used by :func:`send_file` to determine the ``max_age`` cache
         value for a given file path if it wasn't passed.
diff --git a/src/flask/sansio/app.py b/src/flask/sansio/app.py
index 21a79ba460..01fd5dbfae 100644
--- a/src/flask/sansio/app.py
+++ b/src/flask/sansio/app.py
@@ -410,10 +410,6 @@ def __init__(
         # request.
         self._got_first_request = False
 
-        # Set the name of the Click group in case someone wants to add
-        # the app's commands to another CLI tool.
-        self.cli.name = self.name
-
     def _check_setup_finished(self, f_name: str) -> None:
         if self._got_first_request:
             raise AssertionError(
diff --git a/src/flask/sansio/scaffold.py b/src/flask/sansio/scaffold.py
index 5355700899..69e33a095b 100644
--- a/src/flask/sansio/scaffold.py
+++ b/src/flask/sansio/scaffold.py
@@ -8,7 +8,6 @@
 from collections import defaultdict
 from functools import update_wrapper
 
-import click
 from jinja2 import BaseLoader
 from jinja2 import FileSystemLoader
 from werkzeug.exceptions import default_exceptions
@@ -16,10 +15,12 @@
 from werkzeug.utils import cached_property
 
 from .. import typing as ft
-from ..cli import AppGroup
 from ..helpers import get_root_path
 from ..templating import _default_template_ctx_processor
 
+if t.TYPE_CHECKING:  # pragma: no cover
+    from click import Group
+
 # a singleton sentinel value for parameter defaults
 _sentinel = object()
 
@@ -66,6 +67,7 @@ class Scaffold:
     .. versionadded:: 2.0
     """
 
+    cli: Group
     name: str
     _static_folder: str | None = None
     _static_url_path: str | None = None
@@ -97,12 +99,6 @@ def __init__(
         #: up resources contained in the package.
         self.root_path = root_path
 
-        #: The Click command group for registering CLI commands for this
-        #: object. The commands are available from the ``flask`` command
-        #: once the application has been discovered and blueprints have
-        #: been registered.
-        self.cli: click.Group = AppGroup()
-
         #: A dictionary mapping endpoint names to view functions.
         #:
         #: To register a view function, use the :meth:`route` decorator.