diff --git a/CHANGES.rst b/CHANGES.rst
index 3c13f94317..2e0752cc56 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -17,6 +17,9 @@ Unreleased
         ``dict[str, Any]``.
     -   :class:`CompositeParamType` and the number-range base are now
         generic with abstract methods.
+-   Split string values from ``default_map`` for parameters with ``nargs > 1``
+    or :class:`Tuple` type, matching environment variable behavior.
+    :issue:`2745` :pr:`3364`
 
 Version 8.3.3
 -------------
diff --git a/docs/commands.md b/docs/commands.md
index 9f4c6e8f9e..ba064f515a 100644
--- a/docs/commands.md
+++ b/docs/commands.md
@@ -351,6 +351,35 @@ And in action:
     })
 ```
 
+### Multi-value parameters
+
+When a `default_map` value is a string for a parameter with `nargs > 1` or a
+{class}`Tuple` type, the string is split automatically, the same way an
+environment variable would be. By default, values are split on whitespace. See
+[Multiple Options from Environment
+Values](options.md#multiple-options-from-environment-values) for details on
+splitting behavior.
+
+```python
+default_map = {
+    "draw": {
+        "point": "3 4",  # split into ("3", "4") for nargs=2
+        "color": "red",  # passed as-is for nargs=1
+    }
+}
+```
+
+You can also pass an already-structured tuple or list, which will be used as-is
+without splitting:
+
+```python
+default_map = {
+    "draw": {
+        "point": (3, 4),  # used directly
+    }
+}
+```
+
 ## Context Defaults
 
 ```{versionadded} 2.0
diff --git a/docs/conf.py b/docs/conf.py
index 2b223ab422..d04b783274 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -31,6 +31,7 @@
 intersphinx_mapping = {
     "python": ("https://docs.python.org/3/", None),
 }
+myst_heading_anchors = 3
 
 # HTML -----------------------------------------------------------------
 
diff --git a/src/click/core.py b/src/click/core.py
index 295d3a6834..fbb12b78f2 100644
--- a/src/click/core.py
+++ b/src/click/core.py
@@ -2356,6 +2356,11 @@ def consume_value(
                 value = default_map_value
                 source = ParameterSource.DEFAULT_MAP
 
+                # A string from default_map must be split for multi-value
+                # parameters, matching value_from_envvar behavior.
+                if isinstance(value, str) and self.nargs != 1:
+                    value = self.type.split_envvar_value(value)
+
         if value is UNSET:
             default_value = self.get_default(ctx)
             if default_value is not UNSET:
diff --git a/tests/test_defaults.py b/tests/test_defaults.py
index cec051c67a..49f0ac6ed0 100644
--- a/tests/test_defaults.py
+++ b/tests/test_defaults.py
@@ -357,6 +357,43 @@ def cli(value):
     assert result.output == repr(expected)
 
 
+@pytest.mark.parametrize(
+    ("default_map", "option_kwargs", "cli_args", "expected"),
+    [
+        # String is split for nargs=2 option.
+        ({"point": "3 4"}, {"nargs": 2, "type": int}, [], (3, 4)),
+        # String is split for explicit Tuple type.
+        ({"point": "hello world"}, {"type": (str, str)}, [], ("hello", "world")),
+        # Already-structured tuple passes through unchanged.
+        ({"point": ("a", "b")}, {"nargs": 2}, [], ("a", "b")),
+        # Already-structured list passes through unchanged.
+        ({"point": [5, 6]}, {"nargs": 2, "type": int}, [], (5, 6)),
+        # CLI args override default_map for nargs > 1.
+        (
+            {"point": "3 4"},
+            {"nargs": 2, "type": int},
+            ["--point", "10", "20"],
+            (10, 20),
+        ),
+    ],
+)
+def test_default_map_nargs(runner, default_map, option_kwargs, cli_args, expected):
+    """A string in ``default_map`` for an option with ``nargs > 1`` should be
+    split the same way an environment variable string is split.
+
+    Regression test for https://github.com/pallets/click/issues/2745.
+    """
+
+    @click.command()
+    @click.option("--point", **option_kwargs)
+    def cli(point):
+        click.echo(repr(point))
+
+    result = runner.invoke(cli, cli_args, default_map=default_map)
+    assert result.exit_code == 0
+    assert result.output.strip() == repr(expected)
+
+
 def test_unset_in_default_map(runner):
     """An ``UNSET`` value in ``default_map`` should be treated as if
     the key is absent, and so fallback to the parameter's own default.
