diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index 6b0982eab8c..2324080ba14 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -1946,6 +1946,20 @@ class ModelAdmin(BaseModelAdmin):
             "admin/object_history.html"
         ], context)
 
+    def get_formset_kwargs(self, request, obj, inline, prefix):
+        formset_params = {
+            'instance': obj,
+            'prefix': prefix,
+            'queryset': inline.get_queryset(request),
+        }
+        if request.method == 'POST':
+            formset_params.update({
+                'data': request.POST.copy(),
+                'files': request.FILES,
+                'save_as_new': '_saveasnew' in request.POST
+            })
+        return formset_params
+
     def _create_formsets(self, request, obj, change):
         "Helper function to generate formsets for add/change_view."
         formsets = []
@@ -1959,17 +1973,7 @@ class ModelAdmin(BaseModelAdmin):
             prefixes[prefix] = prefixes.get(prefix, 0) + 1
             if prefixes[prefix] != 1 or not prefix:
                 prefix = "%s-%s" % (prefix, prefixes[prefix])
-            formset_params = {
-                'instance': obj,
-                'prefix': prefix,
-                'queryset': inline.get_queryset(request),
-            }
-            if request.method == 'POST':
-                formset_params.update({
-                    'data': request.POST.copy(),
-                    'files': request.FILES,
-                    'save_as_new': '_saveasnew' in request.POST
-                })
+            formset_params = self.get_formset_kwargs(request, obj, inline, prefix)
             formset = FormSet(**formset_params)
 
             def user_deleted_form(request, obj, formset, index):
diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt
index 1fd5d0f60a2..5b3493c858e 100644
--- a/docs/ref/contrib/admin/index.txt
+++ b/docs/ref/contrib/admin/index.txt
@@ -2088,6 +2088,22 @@ templates used by the :class:`ModelAdmin` views:
     ``obj_id`` is the serialized identifier used to retrieve the object to be
     deleted.
 
+.. method:: ModelAdmin.get_formset_kwargs(request, obj, inline, prefix)
+
+    .. versionadded:: 4.0
+
+    A hook for customizing the keyword arguments passed to the constructor of a
+    formset. For example, to pass ``request`` to formset forms::
+
+        class MyModelAdmin(admin.ModelAdmin):
+            def get_formset_kwargs(self, request, obj, inline, prefix):
+                return {
+                    **super().get_formset_kwargs(request, obj, inline, prefix),
+                    'form_kwargs': {'request': request},
+                }
+
+    You can also used it to set ``initial`` for formset forms.
+
 .. method:: ModelAdmin.get_changeform_initial_data(request)
 
     A hook for the initial data on admin change forms. By default, fields are
diff --git a/docs/releases/4.0.txt b/docs/releases/4.0.txt
index 1339b88f247..f1a25d0d927 100644
--- a/docs/releases/4.0.txt
+++ b/docs/releases/4.0.txt
@@ -37,6 +37,9 @@ Minor features
 * The ``admin/base.html`` template now has a new block ``header`` which
   contains the admin site header.
 
+* The new :meth:`.ModelAdmin.get_formset_kwargs` method allows customizing the
+  keyword arguments passed to the constructor of a formset.
+
 :mod:`django.contrib.admindocs`
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
diff --git a/tests/admin_views/admin.py b/tests/admin_views/admin.py
index 925da719821..92dca62035d 100644
--- a/tests/admin_views/admin.py
+++ b/tests/admin_views/admin.py
@@ -951,6 +951,12 @@ class CityAdmin(admin.ModelAdmin):
     inlines = [RestaurantInlineAdmin]
     view_on_site = True
 
+    def get_formset_kwargs(self, request, obj, inline, prefix):
+        return {
+            **super().get_formset_kwargs(request, obj, inline, prefix),
+            'form_kwargs': {'initial': {'name': 'overridden_name'}},
+        }
+
 
 class WorkerAdmin(admin.ModelAdmin):
     def view_on_site(self, obj):
diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py
index 8cb3fda9668..94ddf2be34d 100644
--- a/tests/admin_views/tests.py
+++ b/tests/admin_views/tests.py
@@ -1117,6 +1117,10 @@ class AdminViewBasicTest(AdminViewBasicTestCase):
         self.assertContains(response, '<h1>View article</h1>')
         self.assertContains(response, '<h2>Article 2</h2>')
 
+    def test_formset_kwargs_can_be_overridden(self):
+        response = self.client.get(reverse('admin:admin_views_city_add'))
+        self.assertContains(response, 'overridden_name')
+
 
 @override_settings(TEMPLATES=[{
     'BACKEND': 'django.template.backends.django.DjangoTemplates',