Repository: django-components/django-components
Pull request #1093: docs: move extension command- and url-related API to own API pages
Author: login: JuroOravec
Created: 2025-04-06T12:03:28Z | Merged: 2025-04-06T12:12:15Z

A bit of maintenance in the auto-generated section of the docs, because the [main API page](https://django-components.github.io/django-components/latest/reference/api/) felt too crowded:

- Moved the API reference that's related to defining extension commands and extension URLs to their own pages.
- Move the API reference for data available within extension hooks to the "Extension hooks" section

<img width="1284" alt="Screenshot 2025-04-06 at 13 43 21" src="https://github.com/user-attachments/assets/00e16f73-9457-49ec-81a2-eca59a60f78b" />

<img width="267" alt="Screenshot 2025-04-06 at 12 59 50" src="https://github.com/user-attachments/assets/cffd4f86-6192-43d7-bb65-9268753a558b" />



Diff:
diff --git a/docs/concepts/advanced/extensions.md b/docs/concepts/advanced/extensions.md
index f81882377..3fd5d8a3b 100644
--- a/docs/concepts/advanced/extensions.md
+++ b/docs/concepts/advanced/extensions.md
@@ -394,7 +394,7 @@ Where:
 
 ### Defining Commands
 
-To define a command, subclass from [`ComponentCommand`](../../../reference/api#django_components.ComponentCommand).
+To define a command, subclass from [`ComponentCommand`](../../../reference/extension_commands#django_components.ComponentCommand).
 This subclass should define:
 
 - `name` - the command's name
@@ -419,12 +419,12 @@ class MyExt(ComponentExtension):
 ### Defining Command Arguments and Options
 
 Commands can accept positional arguments and options (e.g. `--foo`), which are defined using the
-[`arguments`](../../../reference/api#django_components.ComponentCommand.arguments)
-attribute of the [`ComponentCommand`](../../../reference/api#django_components.ComponentCommand) class.
+[`arguments`](../../../reference/extension_commands#django_components.ComponentCommand.arguments)
+attribute of the [`ComponentCommand`](../../../reference/extension_commands#django_components.ComponentCommand) class.
 
 The arguments are parsed with [`argparse`](https://docs.python.org/3/library/argparse.html)
 into a dictionary of arguments and options. These are then available
-as keyword arguments to the [`handle`](../../../reference/api#django_components.ComponentCommand.handle)
+as keyword arguments to the [`handle`](../../../reference/extension_commands#django_components.ComponentCommand.handle)
 method of the command.
 
 ```python
@@ -470,20 +470,20 @@ python manage.py components ext run my_ext hello John --shout
     See the [argparse documentation](https://docs.python.org/3/library/argparse.html) for more information.
 
     django-components defines types as
-    [`CommandArg`](../../../reference/api#django_components.CommandArg),
-    [`CommandArgGroup`](../../../reference/api#django_components.CommandArgGroup),
-    [`CommandSubcommand`](../../../reference/api#django_components.CommandSubcommand),
-    and [`CommandParserInput`](../../../reference/api#django_components.CommandParserInput)
+    [`CommandArg`](../../../reference/extension_commands#django_components.CommandArg),
+    [`CommandArgGroup`](../../../reference/extension_commands#django_components.CommandArgGroup),
+    [`CommandSubcommand`](../../../reference/extension_commands#django_components.CommandSubcommand),
+    and [`CommandParserInput`](../../../reference/extension_commands#django_components.CommandParserInput)
     to help with type checking.
 
 !!! note
 
-    If a command doesn't have the [`handle`](../../../reference/api#django_components.ComponentCommand.handle)
+    If a command doesn't have the [`handle`](../../../reference/extension_commands#django_components.ComponentCommand.handle)
     method defined, the command will print a help message and exit.
 
 ### Grouping Arguments
 
-Arguments can be grouped using [`CommandArgGroup`](../../../reference/api#django_components.CommandArgGroup)
+Arguments can be grouped using [`CommandArgGroup`](../../../reference/extension_commands#django_components.CommandArgGroup)
 to provide better organization and help messages.
 
 Read more on [argparse argument groups](https://docs.python.org/3/library/argparse.html#argument-groups).
@@ -539,12 +539,12 @@ class HelloCommand(ComponentCommand):
 Extensions can define subcommands, allowing for more complex command structures.
 
 Subcommands are defined similarly to root commands, as subclasses of
-[`ComponentCommand`](../../../reference/api#django_components.ComponentCommand) class.
+[`ComponentCommand`](../../../reference/extension_commands#django_components.ComponentCommand) class.
 
 However, instead of defining the subcommands in the
-[`commands`](../../../reference/api#django_components.ComponentExtension.commands)
+[`commands`](../../../reference/extension_commands#django_components.ComponentExtension.commands)
 attribute of the extension, you define them in the
-[`subcommands`](../../../reference/api#django_components.ComponentCommand.subcommands)
+[`subcommands`](../../../reference/extension_commands#django_components.ComponentCommand.subcommands)
 attribute of the parent command:
 
 ```python
@@ -663,7 +663,7 @@ def test_hello_command(self):
 
 Extensions can define custom views and endpoints that can be accessed through the Django application.
 
-To define URLs for an extension, set them in the [`urls`](../../../reference/api#django_components.ComponentExtension.urls) attribute of your [`ComponentExtension`](../../../reference/api#django_components.ComponentExtension) class. Each URL is defined using the [`URLRoute`](../../../reference/api#django_components.URLRoute) class, which specifies the path, handler, and optional name for the route.
+To define URLs for an extension, set them in the [`urls`](../../../reference/api#django_components.ComponentExtension.urls) attribute of your [`ComponentExtension`](../../../reference/api#django_components.ComponentExtension) class. Each URL is defined using the [`URLRoute`](../../../reference/extension_urls#django_components.URLRoute) class, which specifies the path, handler, and optional name for the route.
 
 Here's an example of how to define URLs within an extension:
 
@@ -685,13 +685,13 @@ class MyExtension(ComponentExtension):
 
 !!! warning
 
-    The [`URLRoute`](../../../reference/api#django_components.URLRoute) objects
+    The [`URLRoute`](../../../reference/extension_urls#django_components.URLRoute) objects
     are different from objects created with Django's
     [`django.urls.path()`](https://docs.djangoproject.com/en/5.1/ref/urls/#path).
     Do NOT use `URLRoute` objects in Django's [`urlpatterns`](https://docs.djangoproject.com/en/5.1/topics/http/urls/#example)
     and vice versa!
 
-    django-components uses a custom [`URLRoute`](../../../reference/api#django_components.URLRoute) class to define framework-agnostic routing rules.
+    django-components uses a custom [`URLRoute`](../../../reference/extension_urls#django_components.URLRoute) class to define framework-agnostic routing rules.
 
     As of v0.131, `URLRoute` objects are directly converted to Django's `URLPattern` and `URLResolver` objects.
 
@@ -713,9 +713,9 @@ For example, if you have defined a URL with the path `my-view/<str:name>/` in an
 
 Extensions can also define nested URLs to allow for more complex routing structures.
 
-To define nested URLs, set the [`children`](../../../reference/api#django_components.URLRoute.children)
-attribute of the [`URLRoute`](../../../reference/api#django_components.URLRoute) object to
-a list of child [`URLRoute`](../../../reference/api#django_components.URLRoute) objects:
+To define nested URLs, set the [`children`](../../../reference/extension_urls#django_components.URLRoute.children)
+attribute of the [`URLRoute`](../../../reference/extension_urls#django_components.URLRoute) object to
+a list of child [`URLRoute`](../../../reference/extension_urls#django_components.URLRoute) objects:
 
 ```python
 class MyExtension(ComponentExtension):
@@ -742,15 +742,15 @@ would call the `my_view` handler with the parameter `name` set to `"John"`.
 
 ### Passing kwargs and other extra fields to URL routes
 
-The [`URLRoute`](../../../reference/api#django_components.URLRoute) class is framework-agnostic,
+The [`URLRoute`](../../../reference/extension_urls#django_components.URLRoute) class is framework-agnostic,
 so that extensions could be used with non-Django frameworks in the future.
 
 However, that means that there may be some extra fields that Django's
 [`django.urls.path()`](https://docs.djangoproject.com/en/5.1/ref/urls/#path)
 accepts, but which are not defined on the `URLRoute` object.
 
-To address this, the [`URLRoute`](../../../reference/api#django_components.URLRoute) object has
-an [`extra`](../../../reference/api#django_components.URLRoute.extra) attribute,
+To address this, the [`URLRoute`](../../../reference/extension_urls#django_components.URLRoute) object has
+an [`extra`](../../../reference/extension_urls#django_components.URLRoute.extra) attribute,
 which is a dictionary that can be used to pass any extra kwargs to `django.urls.path()`:
 
 ```python
diff --git a/docs/reference/.nav.yml b/docs/reference/.nav.yml
index 9780be307..5de9011b2 100644
--- a/docs/reference/.nav.yml
+++ b/docs/reference/.nav.yml
@@ -4,7 +4,9 @@ nav:
   - Commands: commands.md
   - Components: components.md
   - Exceptions: exceptions.md
+  - Extension commands: extension_commands.md
   - Extension hooks: extension_hooks.md
+  - Extension URLs: extension_urls.md
   - Middlewares: middlewares.md
   - Settings: settings.md
   - Signals: signals.md
diff --git a/docs/reference/api.md b/docs/reference/api.md
index fbda4fcac..7d8f47e38 100644
--- a/docs/reference/api.md
+++ b/docs/reference/api.md
@@ -7,38 +7,14 @@
     options:
       show_if_no_docstring: true
 
-::: django_components.CommandArg
-    options:
-      show_if_no_docstring: true
-
-::: django_components.CommandArgGroup
-    options:
-      show_if_no_docstring: true
-
-::: django_components.CommandHandler
-    options:
-      show_if_no_docstring: true
-
 ::: django_components.CommandLiteralAction
     options:
       show_if_no_docstring: true
 
-::: django_components.CommandParserInput
-    options:
-      show_if_no_docstring: true
-
-::: django_components.CommandSubcommand
-    options:
-      show_if_no_docstring: true
-
 ::: django_components.Component
     options:
       show_if_no_docstring: true
 
-::: django_components.ComponentCommand
-    options:
-      show_if_no_docstring: true
-
 ::: django_components.ComponentExtension
     options:
       show_if_no_docstring: true
@@ -87,38 +63,6 @@
     options:
       show_if_no_docstring: true
 
-::: django_components.OnComponentClassCreatedContext
-    options:
-      show_if_no_docstring: true
-
-::: django_components.OnComponentClassDeletedContext
-    options:
-      show_if_no_docstring: true
-
-::: django_components.OnComponentDataContext
-    options:
-      show_if_no_docstring: true
-
-::: django_components.OnComponentInputContext
-    options:
-      show_if_no_docstring: true
-
-::: django_components.OnComponentRegisteredContext
-    options:
-      show_if_no_docstring: true
-
-::: django_components.OnComponentUnregisteredContext
-    options:
-      show_if_no_docstring: true
-
-::: django_components.OnRegistryCreatedContext
-    options:
-      show_if_no_docstring: true
-
-::: django_components.OnRegistryDeletedContext
-    options:
-      show_if_no_docstring: true
-
 ::: django_components.RegistrySettings
     options:
       show_if_no_docstring: true
@@ -151,14 +95,6 @@
     options:
       show_if_no_docstring: true
 
-::: django_components.URLRoute
-    options:
-      show_if_no_docstring: true
-
-::: django_components.URLRouteHandler
-    options:
-      show_if_no_docstring: true
-
 ::: django_components.all_components
     options:
       show_if_no_docstring: true
diff --git a/docs/reference/commands.md b/docs/reference/commands.md
index d75e9cccd..c27716707 100644
--- a/docs/reference/commands.md
+++ b/docs/reference/commands.md
@@ -54,7 +54,8 @@ python manage.py components ext run <extension> <command>
 ## `components create`
 
 ```txt
-usage: python manage.py components create [-h] [--path PATH] [--js JS] [--css CSS] [--template TEMPLATE] [--force] [--verbose] [--dry-run] name
+usage: python manage.py components create [-h] [--path PATH] [--js JS] [--css CSS] [--template TEMPLATE] [--force] [--verbose] [--dry-run]
+              name
 
 ```
 
@@ -461,8 +462,8 @@ ProjectDashboardAction    project.components.dashboard_action.ProjectDashboardAc
 ## `upgradecomponent`
 
 ```txt
-usage: upgradecomponent [-h] [--path PATH] [--version] [-v {0,1,2,3}] [--settings SETTINGS] [--pythonpath PYTHONPATH] [--traceback] [--no-color] [--force-color]
-                        [--skip-checks]
+usage: upgradecomponent [-h] [--path PATH] [--version] [-v {0,1,2,3}] [--settings SETTINGS]
+                        [--pythonpath PYTHONPATH] [--traceback] [--no-color] [--force-color] [--skip-checks]
 
 ```
 
@@ -506,8 +507,9 @@ Deprecated. Use `components upgrade` instead.
 ## `startcomponent`
 
 ```txt
-usage: startcomponent [-h] [--path PATH] [--js JS] [--css CSS] [--template TEMPLATE] [--force] [--verbose] [--dry-run] [--version] [-v {0,1,2,3}]
-                      [--settings SETTINGS] [--pythonpath PYTHONPATH] [--traceback] [--no-color] [--force-color] [--skip-checks]
+usage: startcomponent [-h] [--path PATH] [--js JS] [--css CSS] [--template TEMPLATE] [--force] [--verbose]
+                      [--dry-run] [--version] [-v {0,1,2,3}] [--settings SETTINGS] [--pythonpath PYTHONPATH]
+                      [--traceback] [--no-color] [--force-color] [--skip-checks]
                       name
 
 ```
diff --git a/docs/reference/extension_commands.md b/docs/reference/extension_commands.md
new file mode 100644
index 000000000..ba29d401d
--- /dev/null
+++ b/docs/reference/extension_commands.md
@@ -0,0 +1,39 @@
+<!-- Autogenerated by reference.py -->
+
+# Extension commands
+
+Overview of all classes, functions, and other objects related to defining extension commands.
+
+Read more on [Extensions](../../concepts/advanced/extensions).
+
+
+::: django_components.CommandArg
+    options:
+      heading_level: 3
+      show_if_no_docstring: true
+
+::: django_components.CommandArgGroup
+    options:
+      heading_level: 3
+      show_if_no_docstring: true
+
+::: django_components.CommandHandler
+    options:
+      heading_level: 3
+      show_if_no_docstring: true
+
+::: django_components.CommandParserInput
+    options:
+      heading_level: 3
+      show_if_no_docstring: true
+
+::: django_components.CommandSubcommand
+    options:
+      heading_level: 3
+      show_if_no_docstring: true
+
+::: django_components.ComponentCommand
+    options:
+      heading_level: 3
+      show_if_no_docstring: true
+
diff --git a/docs/reference/extension_hooks.md b/docs/reference/extension_hooks.md
index c8eb44a63..cd8a14ce1 100644
--- a/docs/reference/extension_hooks.md
+++ b/docs/reference/extension_hooks.md
@@ -1,14 +1,17 @@
 <!-- Autogenerated by reference.py -->
 
-# Extension Hooks
+# Extension hooks
 
 Overview of all the extension hooks available in Django Components.
 
 Read more on [Extensions](../../concepts/advanced/extensions).
 
 
+## Hooks
+
 ::: django_components.extension.ComponentExtension.on_component_class_created
     options:
+      heading_level: 3
       show_root_heading: true
       show_signature: true
       separate_signature: true
@@ -25,6 +28,7 @@ name | type | description
 
 ::: django_components.extension.ComponentExtension.on_component_class_deleted
     options:
+      heading_level: 3
       show_root_heading: true
       show_signature: true
       separate_signature: true
@@ -41,6 +45,7 @@ name | type | description
 
 ::: django_components.extension.ComponentExtension.on_component_data
     options:
+      heading_level: 3
       show_root_heading: true
       show_signature: true
       separate_signature: true
@@ -62,6 +67,7 @@ name | type | description
 
 ::: django_components.extension.ComponentExtension.on_component_input
     options:
+      heading_level: 3
       show_root_heading: true
       show_signature: true
       separate_signature: true
@@ -84,6 +90,7 @@ name | type | description
 
 ::: django_components.extension.ComponentExtension.on_component_registered
     options:
+      heading_level: 3
       show_root_heading: true
       show_signature: true
       separate_signature: true
@@ -102,6 +109,7 @@ name | type | description
 
 ::: django_components.extension.ComponentExtension.on_component_unregistered
     options:
+      heading_level: 3
       show_root_heading: true
       show_signature: true
       separate_signature: true
@@ -120,6 +128,7 @@ name | type | description
 
 ::: django_components.extension.ComponentExtension.on_registry_created
     options:
+      heading_level: 3
       show_root_heading: true
       show_signature: true
       separate_signature: true
@@ -136,6 +145,7 @@ name | type | description
 
 ::: django_components.extension.ComponentExtension.on_registry_deleted
     options:
+      heading_level: 3
       show_root_heading: true
       show_signature: true
       separate_signature: true
@@ -150,3 +160,45 @@ name | type | description
 --|--|--
 `registry` | [`ComponentRegistry`](../api#django_components.ComponentRegistry) | The to-be-deleted ComponentRegistry instance
 
+## Objects
+
+::: django_components.extension.OnComponentClassCreatedContext
+    options:
+      heading_level: 3
+      show_if_no_docstring: true
+
+::: django_components.extension.OnComponentClassDeletedContext
+    options:
+      heading_level: 3
+      show_if_no_docstring: true
+
+::: django_components.extension.OnComponentDataContext
+    options:
+      heading_level: 3
+      show_if_no_docstring: true
+
+::: django_components.extension.OnComponentInputContext
+    options:
+      heading_level: 3
+      show_if_no_docstring: true
+
+::: django_components.extension.OnComponentRegisteredContext
+    options:
+      heading_level: 3
+      show_if_no_docstring: true
+
+::: django_components.extension.OnComponentUnregisteredContext
+    options:
+      heading_level: 3
+      show_if_no_docstring: true
+
+::: django_components.extension.OnRegistryCreatedContext
+    options:
+      heading_level: 3
+      show_if_no_docstring: true
+
+::: django_components.extension.OnRegistryDeletedContext
+    options:
+      heading_level: 3
+      show_if_no_docstring: true
+
diff --git a/docs/reference/extension_urls.md b/docs/reference/extension_urls.md
new file mode 100644
index 000000000..f651ceb78
--- /dev/null
+++ b/docs/reference/extension_urls.md
@@ -0,0 +1,19 @@
+<!-- Autogenerated by reference.py -->
+
+# Extension URLs
+
+Overview of all classes, functions, and other objects related to defining extension URLs.
+
+Read more on [Extensions](../../concepts/advanced/extensions).
+
+
+::: django_components.URLRoute
+    options:
+      heading_level: 3
+      show_if_no_docstring: true
+
+::: django_components.URLRouteHandler
+    options:
+      heading_level: 3
+      show_if_no_docstring: true
+
diff --git a/docs/reference/template_tags.md b/docs/reference/template_tags.md
index 357d2b95b..1ec60bab4 100644
--- a/docs/reference/template_tags.md
+++ b/docs/reference/template_tags.md
@@ -20,7 +20,7 @@ Import as
 
 
 
-<a href="https://github.com/django-components/django-components/tree/master/src/django_components/templatetags/component_tags.py#L1053" target="_blank">See source code</a>
+<a href="https://github.com/django-components/django-components/tree/master/src/django_components/templatetags/component_tags.py#L1037" target="_blank">See source code</a>
 
 
 
@@ -43,7 +43,7 @@ If you insert this tag multiple times, ALL CSS links will be duplicately inserte
 
 
 
-<a href="https://github.com/django-components/django-components/tree/master/src/django_components/templatetags/component_tags.py#L1075" target="_blank">See source code</a>
+<a href="https://github.com/django-components/django-components/tree/master/src/django_components/templatetags/component_tags.py#L1059" target="_blank">See source code</a>
 
 
 
@@ -67,7 +67,7 @@ If you insert this tag multiple times, ALL JS scripts will be duplicately insert
 
 
 
-<a href="https://github.com/django-components/django-components/tree/master/src/django_components/templatetags/component_tags.py#L1494" target="_blank">See source code</a>
+<a href="https://github.com/django-components/django-components/tree/master/src/django_components/templatetags/component_tags.py#L1564" target="_blank">See source code</a>
 
 
 
diff --git a/docs/scripts/reference.py b/docs/scripts/reference.py
index 31893a460..7929e013a 100644
--- a/docs/scripts/reference.py
+++ b/docs/scripts/reference.py
@@ -95,6 +95,9 @@ def gen_reference_api():
                 or _is_error_cls(obj)
                 or _is_tag_formatter_instance(obj)
                 or _is_tag_formatter_cls(obj)
+                or _is_extension_command_api(obj)
+                or _is_extension_hook_api(obj)
+                or _is_extension_url_api(obj)
             ):
                 continue
 
@@ -660,6 +663,7 @@ def gen_reference_extension_hooks():
         # All hooks start with `on_`, so filter out the rest
         unique_methods = [name for name in unique_methods if name.startswith("on_")]
 
+        f.write("## Hooks\n\n")
         for name in sorted(unique_methods):
             # Programmatically get the data available inside the hook, so we can generate
             # a table of available data.
@@ -691,6 +695,7 @@ def gen_reference_extension_hooks():
             f.write(
                 f"::: {class_name}.{name}\n"
                 f"    options:\n"
+                f"      heading_level: 3\n"
                 f"      show_root_heading: true\n"
                 f"      show_signature: true\n"
                 f"      separate_signature: true\n"
@@ -703,6 +708,102 @@ def gen_reference_extension_hooks():
             f.write(available_data)
             f.write("\n")
 
+        # 3. Print the context objects for each hook
+        f.write("## Objects\n\n")
+        main_module = import_module("django_components")
+        for name, obj in inspect.getmembers(main_module):
+            if not _is_extension_hook_api(obj):
+                continue
+
+            # For each entry, generate a mkdocstrings entry, e.g.
+            # ```
+            # ::: django_components.extension.OnComponentClassCreatedContext
+            #     options:
+            #       show_if_no_docstring: true
+            # ```
+            f.write(
+                f"::: {module.__name__}.{name}\n"
+                f"    options:\n"
+                f"      heading_level: 3\n"
+                f"      show_if_no_docstring: true\n"
+            )
+
+            f.write("\n")
+
+
+def gen_reference_extension_commands():
+    """
+    Generate documentation for the objects related to defining extension commands.
+    """
+    module = import_module("django_components")
+
+    preface = "<!-- Autogenerated by reference.py -->\n\n"
+    preface += (root / "docs/templates/reference_extension_commands.md").read_text()
+    out_file = root / "docs/reference/extension_commands.md"
+
+    out_file.parent.mkdir(parents=True, exist_ok=True)
+    with out_file.open("w", encoding="utf-8") as f:
+        # 1. Insert section from `reference_extension_commands.md`
+        f.write(preface + "\n\n")
+
+        # 2. Print the context objects for each hook
+        main_module = import_module("django_components")
+        for name, obj in inspect.getmembers(main_module):
+            if not _is_extension_command_api(obj):
+                continue
+
+            # For each entry, generate a mkdocstrings entry, e.g.
+            # ```
+            # ::: django_components.util.command.CommandLiteralAction
+            #     options:
+            #       show_if_no_docstring: true
+            # ```
+            f.write(
+                f"::: {module.__name__}.{name}\n"
+                f"    options:\n"
+                f"      heading_level: 3\n"
+                f"      show_if_no_docstring: true\n"
+            )
+
+            f.write("\n")
+
+
+def gen_reference_extension_urls():
+    """
+    Generate documentation for the objects related to defining extension URLs.
+    """
+    module = import_module("django_components")
+
+    preface = "<!-- Autogenerated by reference.py -->\n\n"
+    preface += (root / "docs/templates/reference_extension_urls.md").read_text()
+    out_file = root / "docs/reference/extension_urls.md"
+
+    out_file.parent.mkdir(parents=True, exist_ok=True)
+    with out_file.open("w", encoding="utf-8") as f:
+        # 1. Insert section from `reference_extension_urls.md`
+        f.write(preface + "\n\n")
+
+        # 2. Print the context objects for each hook
+        main_module = import_module("django_components")
+        for name, obj in inspect.getmembers(main_module):
+            if not _is_extension_url_api(obj):
+                continue
+
+            # For each entry, generate a mkdocstrings entry, e.g.
+            # ```
+            # ::: django_components.util.routing.URLRoute
+            #     options:
+            #       show_if_no_docstring: true
+            # ```
+            f.write(
+                f"::: {module.__name__}.{name}\n"
+                f"    options:\n"
+                f"      heading_level: 3\n"
+                f"      show_if_no_docstring: true\n"
+            )
+
+            f.write("\n")
+
 
 forward_ref_pattern = re.compile(r"ForwardRef\('(.+?)'\)")
 class_repr_pattern = re.compile(r"<class '(.+?)'>")
@@ -1018,6 +1119,18 @@ def _is_template_tag(obj: Any) -> bool:
     return inspect.isclass(obj) and issubclass(obj, BaseNode)
 
 
+def _is_extension_hook_api(obj: Any) -> bool:
+    return inspect.isclass(obj) and getattr(obj, "_extension_hook_api", False)
+
+
+def _is_extension_command_api(obj: Any) -> bool:
+    return inspect.isclass(obj) and getattr(obj, "_extension_command_api", False)
+
+
+def _is_extension_url_api(obj: Any) -> bool:
+    return inspect.isclass(obj) and getattr(obj, "_extension_url_api", False)
+
+
 def gen_reference():
     """The entrypoint to generate all the reference documentation."""
     gen_reference_api()
@@ -1033,6 +1146,8 @@ def gen_reference():
     gen_reference_signals()
     gen_reference_testing_api()
     gen_reference_extension_hooks()
+    gen_reference_extension_commands()
+    gen_reference_extension_urls()
 
 
 # This is run when `gen-files` plugin is run in mkdocs.yml
diff --git a/docs/templates/reference_extension_commands.md b/docs/templates/reference_extension_commands.md
new file mode 100644
index 000000000..0df906451
--- /dev/null
+++ b/docs/templates/reference_extension_commands.md
@@ -0,0 +1,5 @@
+# Extension commands
+
+Overview of all classes, functions, and other objects related to defining extension commands.
+
+Read more on [Extensions](../../concepts/advanced/extensions).
diff --git a/docs/templates/reference_extension_hooks.md b/docs/templates/reference_extension_hooks.md
index b3c3f6fd0..e43104e2c 100644
--- a/docs/templates/reference_extension_hooks.md
+++ b/docs/templates/reference_extension_hooks.md
@@ -1,4 +1,4 @@
-# Extension Hooks
+# Extension hooks
 
 Overview of all the extension hooks available in Django Components.
 
diff --git a/docs/templates/reference_extension_urls.md b/docs/templates/reference_extension_urls.md
new file mode 100644
index 000000000..10692b23c
--- /dev/null
+++ b/docs/templates/reference_extension_urls.md
@@ -0,0 +1,5 @@
+# Extension URLs
+
+Overview of all classes, functions, and other objects related to defining extension URLs.
+
+Read more on [Extensions](../../concepts/advanced/extensions).
diff --git a/src/django_components/extension.py b/src/django_components/extension.py
index ec1d4b5e5..1571108ea 100644
--- a/src/django_components/extension.py
+++ b/src/django_components/extension.py
@@ -27,26 +27,38 @@
 ################################################
 
 
+# Mark a class as an extension hook context so we can place these in
+# a separate documentation section
+def mark_extension_hook_api(cls: Type[Any]) -> Type[Any]:
+    cls._extension_hook_api = True
+    return cls
+
+
+@mark_extension_hook_api
 class OnComponentClassCreatedContext(NamedTuple):
     component_cls: Type["Component"]
     """The created Component class"""
 
 
+@mark_extension_hook_api
 class OnComponentClassDeletedContext(NamedTuple):
     component_cls: Type["Component"]
     """The to-be-deleted Component class"""
 
 
+@mark_extension_hook_api
 class OnRegistryCreatedContext(NamedTuple):
     registry: "ComponentRegistry"
     """The created ComponentRegistry instance"""
 
 
+@mark_extension_hook_api
 class OnRegistryDeletedContext(NamedTuple):
     registry: "ComponentRegistry"
     """The to-be-deleted ComponentRegistry instance"""
 
 
+@mark_extension_hook_api
 class OnComponentRegisteredContext(NamedTuple):
     registry: "ComponentRegistry"
     """The registry the component was registered to"""
@@ -56,6 +68,7 @@ class OnComponentRegisteredContext(NamedTuple):
     """The registered Component class"""
 
 
+@mark_extension_hook_api
 class OnComponentUnregisteredContext(NamedTuple):
     registry: "ComponentRegistry"
     """The registry the component was unregistered from"""
@@ -65,6 +78,7 @@ class OnComponentUnregisteredContext(NamedTuple):
     """The unregistered Component class"""
 
 
+@mark_extension_hook_api
 class OnComponentInputContext(NamedTuple):
     component: "Component"
     """The Component instance that received the input and is being rendered"""
@@ -82,6 +96,7 @@ class OnComponentInputContext(NamedTuple):
     """The Django template Context object"""
 
 
+@mark_extension_hook_api
 class OnComponentDataContext(NamedTuple):
     component: "Component"
     """The Component instance that is being rendered"""
@@ -209,7 +224,8 @@ class MyExtension(ComponentExtension.ExtensionClass):
 
     These commands will be available to the user as `components ext run <extension> <command>`.
 
-    Commands are defined as subclasses of [`ComponentCommand`](../api#django_components.ComponentCommand).
+    Commands are defined as subclasses of
+    [`ComponentCommand`](../extension_commands#django_components.ComponentCommand).
 
     **Example:**
 
diff --git a/src/django_components/util/command.py b/src/django_components/util/command.py
index e04f8ac0b..644a122c2 100644
--- a/src/django_components/util/command.py
+++ b/src/django_components/util/command.py
@@ -6,6 +6,14 @@
 if TYPE_CHECKING:
     from argparse import _ArgumentGroup, _FormatterClass
 
+
+# Mark object as related to extension commands so we can place these in
+# a separate documentation section
+def mark_extension_command_api(obj: Any) -> Any:
+    obj._extension_command_api = True
+    return obj
+
+
 #############################
 # Argparse typing
 #############################
@@ -19,8 +27,10 @@
 This is a subset of the values for `action` in
 [`ArgumentParser.add_argument()`](https://docs.python.org/3/library/argparse.html#the-add-argument-method).
 """
+mark_extension_command_api(CommandLiteralAction)
 
 
+@mark_extension_command_api
 @dataclass
 class CommandArg:
     """
@@ -77,6 +87,7 @@ def asdict(self) -> dict:
         return _remove_none_values(asdict(self))
 
 
+@mark_extension_command_api
 @dataclass
 class CommandArgGroup:
     """
@@ -102,6 +113,7 @@ def asdict(self) -> dict:
         return _remove_none_values(asdict(self))
 
 
+@mark_extension_command_api
 @dataclass
 class CommandSubcommand:
     """
@@ -158,6 +170,7 @@ def asdict(self) -> dict:
         return _remove_none_values(asdict(self))
 
 
+@mark_extension_command_api
 @dataclass
 class CommandParserInput:
     """
@@ -203,10 +216,12 @@ def asdict(self) -> dict:
 #############################
 
 
+@mark_extension_command_api
 class CommandHandler(Protocol):
     def __call__(self, *args: Any, **kwargs: Any) -> None: ...  # noqa: E704
 
 
+@mark_extension_command_api
 class ComponentCommand:
     """
     Definition of a CLI command.
diff --git a/src/django_components/util/routing.py b/src/django_components/util/routing.py
index 470018d32..69ace6d83 100644
--- a/src/django_components/util/routing.py
+++ b/src/django_components/util/routing.py
@@ -2,12 +2,21 @@
 from typing import Any, Dict, List, Optional, Protocol
 
 
+# Mark object as related to extension URLs so we can place these in
+# a separate documentation section
+def mark_extension_url_api(obj: Any) -> Any:
+    obj._extension_url_api = True
+    return obj
+
+
+@mark_extension_url_api
 class URLRouteHandler(Protocol):
     """Framework-agnostic 'view' function for routes"""
 
     def __call__(self, request: Any, *args: Any, **kwargs: Any) -> Any: ...  # noqa: E704
 
 
+@mark_extension_url_api
 @dataclass
 class URLRoute:
     """


Reviews:
user: login: JuroOravec
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-06T12:09:11Z
user: login: JuroOravec
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-06T12:10:13Z

Review comments:
diff_hunk: @@ -95,6 +95,9 @@ def gen_reference_api():
                 or _is_error_cls(obj)
                 or _is_tag_formatter_instance(obj)
                 or _is_tag_formatter_cls(obj)
+                or _is_extension_command_api(obj)
path: docs/scripts/reference.py
user: login: JuroOravec
body: This is added so that the entries related to extension hooks, urls, and commands are excluded from the main reference page.
created_at: 2025-04-06T12:09:11Z
author_association: COLLABORATOR
diff_hunk: @@ -27,26 +27,38 @@
 ################################################
 
 
+# Mark a class as an extension hook context so we can place these in
+# a separate documentation section
+def mark_extension_hook_api(cls: Type[Any]) -> Type[Any]:
path: src/django_components/extension.py
user: login: JuroOravec
body: To be able to mark which objects should be considered as related to extension hooks / urls / commands, I added little decorators like these.
created_at: 2025-04-06T12:10:13Z
author_association: COLLABORATOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-04-06T12:05:29Z
body: ## Performance Benchmark Results

Comparing PR changes against master branch:


Benchmarks that have stayed the same:

| Change   | Before [0ed46e4d]    | After [468e5e98]    |   Ratio | Benchmark (Parameter)                                             |
|----------|----------------------|---------------------|---------|-------------------------------------------------------------------|
|          | 72.2±0.3ms           | 72.9±0.6ms          |    1.01 | Components vs Django.timeraw_render_lg_first('django')            |
|          | 258±3ms              | 259±1ms             |    1.01 | Components vs Django.timeraw_render_lg_first('django-components') |

author_association: CONTRIBUTOR
user: login: JuroOravec
created_at: 2025-04-06T12:12:05Z
body: cc @EmilStenstrom 
author_association: COLLABORATOR