diff --git a/packages/python/plotly/codegen/figure.py b/packages/python/plotly/codegen/figure.py
index ab1e1c9b748..d18f790bed5 100644
--- a/packages/python/plotly/codegen/figure.py
+++ b/packages/python/plotly/codegen/figure.py
@@ -313,6 +313,7 @@ def update_{plural_name}(
             self,
             patch=None,
             selector=None,
+            overwrite=False,
             row=None, col=None{secondary_y_1},
             **kwargs):
         \"\"\"
@@ -330,6 +331,10 @@ def update_{plural_name}(
             properties corresponding to all of the dictionary's keys, with
             values that exactly match the supplied values. If None
             (the default), all {singular_name} objects are selected.
+        overwrite: bool
+            If True, overwrite existing properties. If False, apply updates
+            to existing properties recursively, preserving existing
+            properties that are not specified in the update operation.
         row, col: int or None (default None)
             Subplot row and column index of {singular_name} objects to select.
             To select {singular_name} objects by row and column, the Figure
@@ -348,7 +353,7 @@ def update_{plural_name}(
         \"\"\"
         for obj in self.select_{plural_name}(
                 selector=selector, row=row, col=col{secondary_y_2}):
-            obj.update(patch, **kwargs)
+            obj.update(patch, overwrite=overwrite, **kwargs)
 
         return self"""
         )
diff --git a/packages/python/plotly/plotly/basedatatypes.py b/packages/python/plotly/plotly/basedatatypes.py
index a423ae11978..bf2f9d3ba80 100644
--- a/packages/python/plotly/plotly/basedatatypes.py
+++ b/packages/python/plotly/plotly/basedatatypes.py
@@ -438,7 +438,7 @@ def _ipython_display_(self):
         else:
             print (repr(self))
 
-    def update(self, dict1=None, **kwargs):
+    def update(self, dict1=None, overwrite=False, **kwargs):
         """
         Update the properties of the figure with a dict and/or with
         keyword arguments.
@@ -450,6 +450,10 @@ def update(self, dict1=None, **kwargs):
         ----------
         dict1 : dict
             Dictionary of properties to be updated
+        overwrite: bool
+            If True, overwrite existing properties. If False, apply updates
+            to existing properties recursively, preserving existing
+            properties that are not specified in the update operation.
         kwargs :
             Keyword/value pair of properties to be updated
 
@@ -484,10 +488,11 @@ def update(self, dict1=None, **kwargs):
                 if d:
                     for k, v in d.items():
                         update_target = self[k]
-                        if update_target == ():
-                            # existing data or frames property is empty
-                            # In this case we accept the v as is.
+                        if update_target == () or overwrite:
                             if k == "data":
+                                # Overwrite all traces as special due to
+                                # restrictions on trace assignment
+                                self.data = ()
                                 self.add_traces(v)
                             else:
                                 # Accept v
@@ -843,7 +848,14 @@ def for_each_trace(self, fn, selector=None, row=None, col=None, secondary_y=None
         return self
 
     def update_traces(
-        self, patch=None, selector=None, row=None, col=None, secondary_y=None, **kwargs
+        self,
+        patch=None,
+        selector=None,
+        row=None,
+        col=None,
+        secondary_y=None,
+        overwrite=False,
+        **kwargs
     ):
         """
         Perform a property update operation on all traces that satisfy the
@@ -877,6 +889,10 @@ def update_traces(
             created using plotly.subplots.make_subplots. See the docstring
             for the specs argument to make_subplots for more info on
             creating subplots with secondary y-axes.
+        overwrite: bool
+            If True, overwrite existing properties. If False, apply updates
+            to existing properties recursively, preserving existing
+            properties that are not specified in the update operation.
         **kwargs
             Additional property updates to apply to each selected trace. If
             a property is specified in both patch and in **kwargs then the
@@ -890,10 +906,10 @@ def update_traces(
         for trace in self.select_traces(
             selector=selector, row=row, col=col, secondary_y=secondary_y
         ):
-            trace.update(patch, **kwargs)
+            trace.update(patch, overwrite=overwrite, **kwargs)
         return self
 
-    def update_layout(self, dict1=None, **kwargs):
+    def update_layout(self, dict1=None, overwrite=False, **kwargs):
         """
         Update the properties of the figure's layout with a dict and/or with
         keyword arguments.
@@ -905,6 +921,10 @@ def update_layout(self, dict1=None, **kwargs):
         ----------
         dict1 : dict
             Dictionary of properties to be updated
+        overwrite: bool
+            If True, overwrite existing properties. If False, apply updates
+            to existing properties recursively, preserving existing
+            properties that are not specified in the update operation.
         kwargs :
             Keyword/value pair of properties to be updated
 
@@ -913,7 +933,7 @@ def update_layout(self, dict1=None, **kwargs):
         BaseFigure
             The Figure object that the update_layout method was called on
         """
-        self.layout.update(dict1, **kwargs)
+        self.layout.update(dict1, overwrite=overwrite, **kwargs)
         return self
 
     def _select_layout_subplots_by_prefix(
@@ -2697,7 +2717,7 @@ def _is_dict_list(v):
         return isinstance(v, list) and len(v) > 0 and isinstance(v[0], dict)
 
     @staticmethod
-    def _perform_update(plotly_obj, update_obj):
+    def _perform_update(plotly_obj, update_obj, overwrite=False):
         """
         Helper to support the update() methods on :class:`BaseFigure` and
         :class:`BasePlotlyType`
@@ -2747,6 +2767,12 @@ def _perform_update(plotly_obj, update_obj):
             # ------------------------
             for key in update_obj:
                 val = update_obj[key]
+
+                if overwrite:
+                    # Don't recurse and assign property as-is
+                    plotly_obj[key] = val
+                    continue
+
                 validator = plotly_obj._get_prop_validator(key)
 
                 if isinstance(validator, CompoundValidator) and isinstance(val, dict):
@@ -3530,7 +3556,7 @@ def _raise_on_invalid_property_error(self, *args):
                 )
             )
 
-    def update(self, dict1=None, **kwargs):
+    def update(self, dict1=None, overwrite=False, **kwargs):
         """
         Update the properties of an object with a dict and/or with
         keyword arguments.
@@ -3542,6 +3568,10 @@ def update(self, dict1=None, **kwargs):
         ----------
         dict1 : dict
             Dictionary of properties to be updated
+        overwrite: bool
+            If True, overwrite existing properties. If False, apply updates
+            to existing properties recursively, preserving existing
+            properties that are not specified in the update operation.
         kwargs :
             Keyword/value pair of properties to be updated
 
@@ -3552,11 +3582,11 @@ def update(self, dict1=None, **kwargs):
         """
         if self.figure:
             with self.figure.batch_update():
-                BaseFigure._perform_update(self, dict1)
-                BaseFigure._perform_update(self, kwargs)
+                BaseFigure._perform_update(self, dict1, overwrite=overwrite)
+                BaseFigure._perform_update(self, kwargs, overwrite=overwrite)
         else:
-            BaseFigure._perform_update(self, dict1)
-            BaseFigure._perform_update(self, kwargs)
+            BaseFigure._perform_update(self, dict1, overwrite=overwrite)
+            BaseFigure._perform_update(self, kwargs, overwrite=overwrite)
 
         return self
 
diff --git a/packages/python/plotly/plotly/graph_objs/_figure.py b/packages/python/plotly/plotly/graph_objs/_figure.py
index a3890db0188..d62ac17cafe 100644
--- a/packages/python/plotly/plotly/graph_objs/_figure.py
+++ b/packages/python/plotly/plotly/graph_objs/_figure.py
@@ -15081,7 +15081,9 @@ def for_each_coloraxis(self, fn, selector=None, row=None, col=None):
 
         return self
 
-    def update_coloraxes(self, patch=None, selector=None, row=None, col=None, **kwargs):
+    def update_coloraxes(
+        self, patch=None, selector=None, overwrite=False, row=None, col=None, **kwargs
+    ):
         """
         Perform a property update operation on all coloraxis objects
         that satisfy the specified selection criteria
@@ -15097,6 +15099,10 @@ def update_coloraxes(self, patch=None, selector=None, row=None, col=None, **kwar
             properties corresponding to all of the dictionary's keys, with
             values that exactly match the supplied values. If None
             (the default), all coloraxis objects are selected.
+        overwrite: bool
+            If True, overwrite existing properties. If False, apply updates
+            to existing properties recursively, preserving existing
+            properties that are not specified in the update operation.
         row, col: int or None (default None)
             Subplot row and column index of coloraxis objects to select.
             To select coloraxis objects by row and column, the Figure
@@ -15113,7 +15119,7 @@ def update_coloraxes(self, patch=None, selector=None, row=None, col=None, **kwar
             Returns the Figure object that the method was called on
         """
         for obj in self.select_coloraxes(selector=selector, row=row, col=col):
-            obj.update(patch, **kwargs)
+            obj.update(patch, overwrite=overwrite, **kwargs)
 
         return self
 
@@ -15175,7 +15181,9 @@ def for_each_geo(self, fn, selector=None, row=None, col=None):
 
         return self
 
-    def update_geos(self, patch=None, selector=None, row=None, col=None, **kwargs):
+    def update_geos(
+        self, patch=None, selector=None, overwrite=False, row=None, col=None, **kwargs
+    ):
         """
         Perform a property update operation on all geo objects
         that satisfy the specified selection criteria
@@ -15191,6 +15199,10 @@ def update_geos(self, patch=None, selector=None, row=None, col=None, **kwargs):
             properties corresponding to all of the dictionary's keys, with
             values that exactly match the supplied values. If None
             (the default), all geo objects are selected.
+        overwrite: bool
+            If True, overwrite existing properties. If False, apply updates
+            to existing properties recursively, preserving existing
+            properties that are not specified in the update operation.
         row, col: int or None (default None)
             Subplot row and column index of geo objects to select.
             To select geo objects by row and column, the Figure
@@ -15207,7 +15219,7 @@ def update_geos(self, patch=None, selector=None, row=None, col=None, **kwargs):
             Returns the Figure object that the method was called on
         """
         for obj in self.select_geos(selector=selector, row=row, col=col):
-            obj.update(patch, **kwargs)
+            obj.update(patch, overwrite=overwrite, **kwargs)
 
         return self
 
@@ -15269,7 +15281,9 @@ def for_each_mapbox(self, fn, selector=None, row=None, col=None):
 
         return self
 
-    def update_mapboxes(self, patch=None, selector=None, row=None, col=None, **kwargs):
+    def update_mapboxes(
+        self, patch=None, selector=None, overwrite=False, row=None, col=None, **kwargs
+    ):
         """
         Perform a property update operation on all mapbox objects
         that satisfy the specified selection criteria
@@ -15285,6 +15299,10 @@ def update_mapboxes(self, patch=None, selector=None, row=None, col=None, **kwarg
             properties corresponding to all of the dictionary's keys, with
             values that exactly match the supplied values. If None
             (the default), all mapbox objects are selected.
+        overwrite: bool
+            If True, overwrite existing properties. If False, apply updates
+            to existing properties recursively, preserving existing
+            properties that are not specified in the update operation.
         row, col: int or None (default None)
             Subplot row and column index of mapbox objects to select.
             To select mapbox objects by row and column, the Figure
@@ -15301,7 +15319,7 @@ def update_mapboxes(self, patch=None, selector=None, row=None, col=None, **kwarg
             Returns the Figure object that the method was called on
         """
         for obj in self.select_mapboxes(selector=selector, row=row, col=col):
-            obj.update(patch, **kwargs)
+            obj.update(patch, overwrite=overwrite, **kwargs)
 
         return self
 
@@ -15363,7 +15381,9 @@ def for_each_polar(self, fn, selector=None, row=None, col=None):
 
         return self
 
-    def update_polars(self, patch=None, selector=None, row=None, col=None, **kwargs):
+    def update_polars(
+        self, patch=None, selector=None, overwrite=False, row=None, col=None, **kwargs
+    ):
         """
         Perform a property update operation on all polar objects
         that satisfy the specified selection criteria
@@ -15379,6 +15399,10 @@ def update_polars(self, patch=None, selector=None, row=None, col=None, **kwargs)
             properties corresponding to all of the dictionary's keys, with
             values that exactly match the supplied values. If None
             (the default), all polar objects are selected.
+        overwrite: bool
+            If True, overwrite existing properties. If False, apply updates
+            to existing properties recursively, preserving existing
+            properties that are not specified in the update operation.
         row, col: int or None (default None)
             Subplot row and column index of polar objects to select.
             To select polar objects by row and column, the Figure
@@ -15395,7 +15419,7 @@ def update_polars(self, patch=None, selector=None, row=None, col=None, **kwargs)
             Returns the Figure object that the method was called on
         """
         for obj in self.select_polars(selector=selector, row=row, col=col):
-            obj.update(patch, **kwargs)
+            obj.update(patch, overwrite=overwrite, **kwargs)
 
         return self
 
@@ -15457,7 +15481,9 @@ def for_each_scene(self, fn, selector=None, row=None, col=None):
 
         return self
 
-    def update_scenes(self, patch=None, selector=None, row=None, col=None, **kwargs):
+    def update_scenes(
+        self, patch=None, selector=None, overwrite=False, row=None, col=None, **kwargs
+    ):
         """
         Perform a property update operation on all scene objects
         that satisfy the specified selection criteria
@@ -15473,6 +15499,10 @@ def update_scenes(self, patch=None, selector=None, row=None, col=None, **kwargs)
             properties corresponding to all of the dictionary's keys, with
             values that exactly match the supplied values. If None
             (the default), all scene objects are selected.
+        overwrite: bool
+            If True, overwrite existing properties. If False, apply updates
+            to existing properties recursively, preserving existing
+            properties that are not specified in the update operation.
         row, col: int or None (default None)
             Subplot row and column index of scene objects to select.
             To select scene objects by row and column, the Figure
@@ -15489,7 +15519,7 @@ def update_scenes(self, patch=None, selector=None, row=None, col=None, **kwargs)
             Returns the Figure object that the method was called on
         """
         for obj in self.select_scenes(selector=selector, row=row, col=col):
-            obj.update(patch, **kwargs)
+            obj.update(patch, overwrite=overwrite, **kwargs)
 
         return self
 
@@ -15551,7 +15581,9 @@ def for_each_ternary(self, fn, selector=None, row=None, col=None):
 
         return self
 
-    def update_ternaries(self, patch=None, selector=None, row=None, col=None, **kwargs):
+    def update_ternaries(
+        self, patch=None, selector=None, overwrite=False, row=None, col=None, **kwargs
+    ):
         """
         Perform a property update operation on all ternary objects
         that satisfy the specified selection criteria
@@ -15567,6 +15599,10 @@ def update_ternaries(self, patch=None, selector=None, row=None, col=None, **kwar
             properties corresponding to all of the dictionary's keys, with
             values that exactly match the supplied values. If None
             (the default), all ternary objects are selected.
+        overwrite: bool
+            If True, overwrite existing properties. If False, apply updates
+            to existing properties recursively, preserving existing
+            properties that are not specified in the update operation.
         row, col: int or None (default None)
             Subplot row and column index of ternary objects to select.
             To select ternary objects by row and column, the Figure
@@ -15583,7 +15619,7 @@ def update_ternaries(self, patch=None, selector=None, row=None, col=None, **kwar
             Returns the Figure object that the method was called on
         """
         for obj in self.select_ternaries(selector=selector, row=row, col=col):
-            obj.update(patch, **kwargs)
+            obj.update(patch, overwrite=overwrite, **kwargs)
 
         return self
 
@@ -15645,7 +15681,9 @@ def for_each_xaxis(self, fn, selector=None, row=None, col=None):
 
         return self
 
-    def update_xaxes(self, patch=None, selector=None, row=None, col=None, **kwargs):
+    def update_xaxes(
+        self, patch=None, selector=None, overwrite=False, row=None, col=None, **kwargs
+    ):
         """
         Perform a property update operation on all xaxis objects
         that satisfy the specified selection criteria
@@ -15661,6 +15699,10 @@ def update_xaxes(self, patch=None, selector=None, row=None, col=None, **kwargs):
             properties corresponding to all of the dictionary's keys, with
             values that exactly match the supplied values. If None
             (the default), all xaxis objects are selected.
+        overwrite: bool
+            If True, overwrite existing properties. If False, apply updates
+            to existing properties recursively, preserving existing
+            properties that are not specified in the update operation.
         row, col: int or None (default None)
             Subplot row and column index of xaxis objects to select.
             To select xaxis objects by row and column, the Figure
@@ -15677,7 +15719,7 @@ def update_xaxes(self, patch=None, selector=None, row=None, col=None, **kwargs):
             Returns the Figure object that the method was called on
         """
         for obj in self.select_xaxes(selector=selector, row=row, col=col):
-            obj.update(patch, **kwargs)
+            obj.update(patch, overwrite=overwrite, **kwargs)
 
         return self
 
@@ -15768,7 +15810,14 @@ def for_each_yaxis(self, fn, selector=None, row=None, col=None, secondary_y=None
         return self
 
     def update_yaxes(
-        self, patch=None, selector=None, row=None, col=None, secondary_y=None, **kwargs
+        self,
+        patch=None,
+        selector=None,
+        overwrite=False,
+        row=None,
+        col=None,
+        secondary_y=None,
+        **kwargs
     ):
         """
         Perform a property update operation on all yaxis objects
@@ -15785,6 +15834,10 @@ def update_yaxes(
             properties corresponding to all of the dictionary's keys, with
             values that exactly match the supplied values. If None
             (the default), all yaxis objects are selected.
+        overwrite: bool
+            If True, overwrite existing properties. If False, apply updates
+            to existing properties recursively, preserving existing
+            properties that are not specified in the update operation.
         row, col: int or None (default None)
             Subplot row and column index of yaxis objects to select.
             To select yaxis objects by row and column, the Figure
@@ -15815,6 +15868,6 @@ def update_yaxes(
         for obj in self.select_yaxes(
             selector=selector, row=row, col=col, secondary_y=secondary_y
         ):
-            obj.update(patch, **kwargs)
+            obj.update(patch, overwrite=overwrite, **kwargs)
 
         return self
diff --git a/packages/python/plotly/plotly/graph_objs/_figurewidget.py b/packages/python/plotly/plotly/graph_objs/_figurewidget.py
index af6758944b3..d4b5239abb6 100644
--- a/packages/python/plotly/plotly/graph_objs/_figurewidget.py
+++ b/packages/python/plotly/plotly/graph_objs/_figurewidget.py
@@ -15081,7 +15081,9 @@ def for_each_coloraxis(self, fn, selector=None, row=None, col=None):
 
         return self
 
-    def update_coloraxes(self, patch=None, selector=None, row=None, col=None, **kwargs):
+    def update_coloraxes(
+        self, patch=None, selector=None, overwrite=False, row=None, col=None, **kwargs
+    ):
         """
         Perform a property update operation on all coloraxis objects
         that satisfy the specified selection criteria
@@ -15097,6 +15099,10 @@ def update_coloraxes(self, patch=None, selector=None, row=None, col=None, **kwar
             properties corresponding to all of the dictionary's keys, with
             values that exactly match the supplied values. If None
             (the default), all coloraxis objects are selected.
+        overwrite: bool
+            If True, overwrite existing properties. If False, apply updates
+            to existing properties recursively, preserving existing
+            properties that are not specified in the update operation.
         row, col: int or None (default None)
             Subplot row and column index of coloraxis objects to select.
             To select coloraxis objects by row and column, the Figure
@@ -15113,7 +15119,7 @@ def update_coloraxes(self, patch=None, selector=None, row=None, col=None, **kwar
             Returns the Figure object that the method was called on
         """
         for obj in self.select_coloraxes(selector=selector, row=row, col=col):
-            obj.update(patch, **kwargs)
+            obj.update(patch, overwrite=overwrite, **kwargs)
 
         return self
 
@@ -15175,7 +15181,9 @@ def for_each_geo(self, fn, selector=None, row=None, col=None):
 
         return self
 
-    def update_geos(self, patch=None, selector=None, row=None, col=None, **kwargs):
+    def update_geos(
+        self, patch=None, selector=None, overwrite=False, row=None, col=None, **kwargs
+    ):
         """
         Perform a property update operation on all geo objects
         that satisfy the specified selection criteria
@@ -15191,6 +15199,10 @@ def update_geos(self, patch=None, selector=None, row=None, col=None, **kwargs):
             properties corresponding to all of the dictionary's keys, with
             values that exactly match the supplied values. If None
             (the default), all geo objects are selected.
+        overwrite: bool
+            If True, overwrite existing properties. If False, apply updates
+            to existing properties recursively, preserving existing
+            properties that are not specified in the update operation.
         row, col: int or None (default None)
             Subplot row and column index of geo objects to select.
             To select geo objects by row and column, the Figure
@@ -15207,7 +15219,7 @@ def update_geos(self, patch=None, selector=None, row=None, col=None, **kwargs):
             Returns the Figure object that the method was called on
         """
         for obj in self.select_geos(selector=selector, row=row, col=col):
-            obj.update(patch, **kwargs)
+            obj.update(patch, overwrite=overwrite, **kwargs)
 
         return self
 
@@ -15269,7 +15281,9 @@ def for_each_mapbox(self, fn, selector=None, row=None, col=None):
 
         return self
 
-    def update_mapboxes(self, patch=None, selector=None, row=None, col=None, **kwargs):
+    def update_mapboxes(
+        self, patch=None, selector=None, overwrite=False, row=None, col=None, **kwargs
+    ):
         """
         Perform a property update operation on all mapbox objects
         that satisfy the specified selection criteria
@@ -15285,6 +15299,10 @@ def update_mapboxes(self, patch=None, selector=None, row=None, col=None, **kwarg
             properties corresponding to all of the dictionary's keys, with
             values that exactly match the supplied values. If None
             (the default), all mapbox objects are selected.
+        overwrite: bool
+            If True, overwrite existing properties. If False, apply updates
+            to existing properties recursively, preserving existing
+            properties that are not specified in the update operation.
         row, col: int or None (default None)
             Subplot row and column index of mapbox objects to select.
             To select mapbox objects by row and column, the Figure
@@ -15301,7 +15319,7 @@ def update_mapboxes(self, patch=None, selector=None, row=None, col=None, **kwarg
             Returns the Figure object that the method was called on
         """
         for obj in self.select_mapboxes(selector=selector, row=row, col=col):
-            obj.update(patch, **kwargs)
+            obj.update(patch, overwrite=overwrite, **kwargs)
 
         return self
 
@@ -15363,7 +15381,9 @@ def for_each_polar(self, fn, selector=None, row=None, col=None):
 
         return self
 
-    def update_polars(self, patch=None, selector=None, row=None, col=None, **kwargs):
+    def update_polars(
+        self, patch=None, selector=None, overwrite=False, row=None, col=None, **kwargs
+    ):
         """
         Perform a property update operation on all polar objects
         that satisfy the specified selection criteria
@@ -15379,6 +15399,10 @@ def update_polars(self, patch=None, selector=None, row=None, col=None, **kwargs)
             properties corresponding to all of the dictionary's keys, with
             values that exactly match the supplied values. If None
             (the default), all polar objects are selected.
+        overwrite: bool
+            If True, overwrite existing properties. If False, apply updates
+            to existing properties recursively, preserving existing
+            properties that are not specified in the update operation.
         row, col: int or None (default None)
             Subplot row and column index of polar objects to select.
             To select polar objects by row and column, the Figure
@@ -15395,7 +15419,7 @@ def update_polars(self, patch=None, selector=None, row=None, col=None, **kwargs)
             Returns the Figure object that the method was called on
         """
         for obj in self.select_polars(selector=selector, row=row, col=col):
-            obj.update(patch, **kwargs)
+            obj.update(patch, overwrite=overwrite, **kwargs)
 
         return self
 
@@ -15457,7 +15481,9 @@ def for_each_scene(self, fn, selector=None, row=None, col=None):
 
         return self
 
-    def update_scenes(self, patch=None, selector=None, row=None, col=None, **kwargs):
+    def update_scenes(
+        self, patch=None, selector=None, overwrite=False, row=None, col=None, **kwargs
+    ):
         """
         Perform a property update operation on all scene objects
         that satisfy the specified selection criteria
@@ -15473,6 +15499,10 @@ def update_scenes(self, patch=None, selector=None, row=None, col=None, **kwargs)
             properties corresponding to all of the dictionary's keys, with
             values that exactly match the supplied values. If None
             (the default), all scene objects are selected.
+        overwrite: bool
+            If True, overwrite existing properties. If False, apply updates
+            to existing properties recursively, preserving existing
+            properties that are not specified in the update operation.
         row, col: int or None (default None)
             Subplot row and column index of scene objects to select.
             To select scene objects by row and column, the Figure
@@ -15489,7 +15519,7 @@ def update_scenes(self, patch=None, selector=None, row=None, col=None, **kwargs)
             Returns the Figure object that the method was called on
         """
         for obj in self.select_scenes(selector=selector, row=row, col=col):
-            obj.update(patch, **kwargs)
+            obj.update(patch, overwrite=overwrite, **kwargs)
 
         return self
 
@@ -15551,7 +15581,9 @@ def for_each_ternary(self, fn, selector=None, row=None, col=None):
 
         return self
 
-    def update_ternaries(self, patch=None, selector=None, row=None, col=None, **kwargs):
+    def update_ternaries(
+        self, patch=None, selector=None, overwrite=False, row=None, col=None, **kwargs
+    ):
         """
         Perform a property update operation on all ternary objects
         that satisfy the specified selection criteria
@@ -15567,6 +15599,10 @@ def update_ternaries(self, patch=None, selector=None, row=None, col=None, **kwar
             properties corresponding to all of the dictionary's keys, with
             values that exactly match the supplied values. If None
             (the default), all ternary objects are selected.
+        overwrite: bool
+            If True, overwrite existing properties. If False, apply updates
+            to existing properties recursively, preserving existing
+            properties that are not specified in the update operation.
         row, col: int or None (default None)
             Subplot row and column index of ternary objects to select.
             To select ternary objects by row and column, the Figure
@@ -15583,7 +15619,7 @@ def update_ternaries(self, patch=None, selector=None, row=None, col=None, **kwar
             Returns the Figure object that the method was called on
         """
         for obj in self.select_ternaries(selector=selector, row=row, col=col):
-            obj.update(patch, **kwargs)
+            obj.update(patch, overwrite=overwrite, **kwargs)
 
         return self
 
@@ -15645,7 +15681,9 @@ def for_each_xaxis(self, fn, selector=None, row=None, col=None):
 
         return self
 
-    def update_xaxes(self, patch=None, selector=None, row=None, col=None, **kwargs):
+    def update_xaxes(
+        self, patch=None, selector=None, overwrite=False, row=None, col=None, **kwargs
+    ):
         """
         Perform a property update operation on all xaxis objects
         that satisfy the specified selection criteria
@@ -15661,6 +15699,10 @@ def update_xaxes(self, patch=None, selector=None, row=None, col=None, **kwargs):
             properties corresponding to all of the dictionary's keys, with
             values that exactly match the supplied values. If None
             (the default), all xaxis objects are selected.
+        overwrite: bool
+            If True, overwrite existing properties. If False, apply updates
+            to existing properties recursively, preserving existing
+            properties that are not specified in the update operation.
         row, col: int or None (default None)
             Subplot row and column index of xaxis objects to select.
             To select xaxis objects by row and column, the Figure
@@ -15677,7 +15719,7 @@ def update_xaxes(self, patch=None, selector=None, row=None, col=None, **kwargs):
             Returns the Figure object that the method was called on
         """
         for obj in self.select_xaxes(selector=selector, row=row, col=col):
-            obj.update(patch, **kwargs)
+            obj.update(patch, overwrite=overwrite, **kwargs)
 
         return self
 
@@ -15768,7 +15810,14 @@ def for_each_yaxis(self, fn, selector=None, row=None, col=None, secondary_y=None
         return self
 
     def update_yaxes(
-        self, patch=None, selector=None, row=None, col=None, secondary_y=None, **kwargs
+        self,
+        patch=None,
+        selector=None,
+        overwrite=False,
+        row=None,
+        col=None,
+        secondary_y=None,
+        **kwargs
     ):
         """
         Perform a property update operation on all yaxis objects
@@ -15785,6 +15834,10 @@ def update_yaxes(
             properties corresponding to all of the dictionary's keys, with
             values that exactly match the supplied values. If None
             (the default), all yaxis objects are selected.
+        overwrite: bool
+            If True, overwrite existing properties. If False, apply updates
+            to existing properties recursively, preserving existing
+            properties that are not specified in the update operation.
         row, col: int or None (default None)
             Subplot row and column index of yaxis objects to select.
             To select yaxis objects by row and column, the Figure
@@ -15815,6 +15868,6 @@ def update_yaxes(
         for obj in self.select_yaxes(
             selector=selector, row=row, col=col, secondary_y=secondary_y
         ):
-            obj.update(patch, **kwargs)
+            obj.update(patch, overwrite=overwrite, **kwargs)
 
         return self
diff --git a/packages/python/plotly/plotly/tests/test_core/test_graph_objs/test_figure.py b/packages/python/plotly/plotly/tests/test_core/test_graph_objs/test_figure.py
index 04051bffc4e..57bd060b59a 100644
--- a/packages/python/plotly/plotly/tests/test_core/test_graph_objs/test_figure.py
+++ b/packages/python/plotly/plotly/tests/test_core/test_graph_objs/test_figure.py
@@ -5,6 +5,11 @@
 
 
 class FigureTest(TestCaseNoTemplate):
+    def setUp(self):
+        import plotly.io as pio
+
+        pio.templates.default = None
+
     def test_instantiation(self):
 
         native_figure = {"data": [], "layout": {}, "frames": []}
@@ -166,3 +171,31 @@ def test_pop_invalid_key(self):
         fig = go.Figure(layout=go.Layout(width=1000))
         with self.assertRaises(KeyError):
             fig.pop("bogus")
+
+    def test_update_overwrite_layout(self):
+        fig = go.Figure(layout=go.Layout(width=1000))
+
+        # By default, update works recursively so layout.width should remain
+        fig.update(layout={"title": {"text": "Fig Title"}})
+        fig.layout.pop("template")
+        self.assertEqual(
+            fig.layout.to_plotly_json(), {"title": {"text": "Fig Title"}, "width": 1000}
+        )
+
+        # With overwrite=True, all existing layout properties should be
+        # removed
+        fig.update(overwrite=True, layout={"title": {"text": "Fig2 Title"}})
+        fig.layout.pop("template")
+        self.assertEqual(fig.layout.to_plotly_json(), {"title": {"text": "Fig2 Title"}})
+
+    def test_update_overwrite_data(self):
+        fig = go.Figure(
+            data=[go.Bar(marker_color="blue"), go.Bar(marker_color="yellow")]
+        )
+
+        fig.update(overwrite=True, data=[go.Marker(y=[1, 3, 2], line_color="yellow")])
+
+        self.assertEqual(
+            fig.to_plotly_json()["data"],
+            [{"type": "scatter", "y": [1, 3, 2], "line": {"color": "yellow"}}],
+        )
diff --git a/packages/python/plotly/plotly/tests/test_core/test_graph_objs/test_update.py b/packages/python/plotly/plotly/tests/test_core/test_graph_objs/test_update.py
index 4783faa96a8..188a9a34988 100644
--- a/packages/python/plotly/plotly/tests/test_core/test_graph_objs/test_update.py
+++ b/packages/python/plotly/plotly/tests/test_core/test_graph_objs/test_update.py
@@ -157,3 +157,51 @@ def test_update_initialize_nonempty_list_with_list_extends(self):
         }
 
         self.assertEqual(layout.to_plotly_json(), expected)
+
+    def test_overwrite_compound_prop(self):
+        layout = go.Layout(title_font_family="Courier")
+
+        # First update with default (recursive) behavior
+        layout.update(title={"text": "Fig Title"})
+        expected = {"title": {"text": "Fig Title", "font": {"family": "Courier"}}}
+        self.assertEqual(layout.to_plotly_json(), expected)
+
+        # Update with overwrite behavior
+        layout.update(title={"text": "Fig Title2"}, overwrite=True)
+        expected = {"title": {"text": "Fig Title2"}}
+        self.assertEqual(layout.to_plotly_json(), expected)
+
+    def test_overwrite_tuple_prop(self):
+        layout = go.Layout(
+            annotations=[
+                go.layout.Annotation(text="one"),
+                go.layout.Annotation(text="two"),
+            ]
+        )
+
+        layout.update(
+            overwrite=True,
+            annotations=[
+                go.layout.Annotation(width=10),
+                go.layout.Annotation(width=20),
+                go.layout.Annotation(width=30),
+                go.layout.Annotation(width=40),
+                go.layout.Annotation(width=50),
+            ],
+        )
+
+        expected = {
+            "annotations": [
+                {"width": 10},
+                {"width": 20},
+                {"width": 30},
+                {"width": 40},
+                {"width": 50},
+            ]
+        }
+
+        self.assertEqual(layout.to_plotly_json(), expected)
+
+        # Remove all annotations
+        layout.update(overwrite=True, annotations=None)
+        self.assertEqual(layout.to_plotly_json(), {})
diff --git a/packages/python/plotly/plotly/tests/test_core/test_update_objects/test_update_layout.py b/packages/python/plotly/plotly/tests/test_core/test_update_objects/test_update_layout.py
index 595aa6ae8b0..3a637167c61 100644
--- a/packages/python/plotly/plotly/tests/test_core/test_update_objects/test_update_layout.py
+++ b/packages/python/plotly/plotly/tests/test_core/test_update_objects/test_update_layout.py
@@ -3,6 +3,11 @@
 
 
 class TestUpdateLayout(TestCase):
+    def setUp(self):
+        import plotly.io as pio
+
+        pio.templates.default = None
+
     def test_update_layout_kwargs(self):
         # Create initial figure
         fig = go.Figure()
@@ -26,3 +31,41 @@ def test_update_layout_dict(self):
         fig.update_layout(dict(title=dict(font=dict(family="Courier New"))))
         orig_fig.layout.update(title_font_family="Courier New")
         self.assertEqual(fig, orig_fig)
+
+    def test_update_layout_overwrite(self):
+        fig = go.Figure(
+            layout=go.Layout(
+                annotations=[
+                    go.layout.Annotation(text="one"),
+                    go.layout.Annotation(text="two"),
+                ]
+            )
+        )
+
+        fig.update_layout(
+            overwrite=True,
+            annotations=[
+                go.layout.Annotation(width=10),
+                go.layout.Annotation(width=20),
+                go.layout.Annotation(width=30),
+                go.layout.Annotation(width=40),
+                go.layout.Annotation(width=50),
+            ],
+        )
+
+        expected = {
+            "annotations": [
+                {"width": 10},
+                {"width": 20},
+                {"width": 30},
+                {"width": 40},
+                {"width": 50},
+            ]
+        }
+
+        fig.layout.pop("template")
+        self.assertEqual(fig.layout.to_plotly_json(), expected)
+
+        # Remove all annotations
+        fig.update_layout(overwrite=True, annotations=None)
+        self.assertEqual(fig.layout.annotations, ())
diff --git a/packages/python/plotly/plotly/tests/test_core/test_update_objects/test_update_subplots.py b/packages/python/plotly/plotly/tests/test_core/test_update_objects/test_update_subplots.py
index 19f81db2e8e..14a4758b102 100644
--- a/packages/python/plotly/plotly/tests/test_core/test_update_objects/test_update_subplots.py
+++ b/packages/python/plotly/plotly/tests/test_core/test_update_objects/test_update_subplots.py
@@ -563,3 +563,12 @@ def test_update_by_type_and_grid_and_selector(self):
             row=1,
             selector={"title.text": "A"},
         )
+
+    def test_update_subplot_overwrite(self):
+        fig = go.Figure(layout_xaxis_title_text="Axis title")
+        fig.update_xaxes(overwrite=True, title={"font": {"family": "Courier"}})
+
+        self.assertEqual(
+            fig.layout.xaxis.to_plotly_json(),
+            {"title": {"font": {"family": "Courier"}}},
+        )
diff --git a/packages/python/plotly/plotly/tests/test_core/test_update_objects/test_update_traces.py b/packages/python/plotly/plotly/tests/test_core/test_update_objects/test_update_traces.py
index 3bd16adac27..b1c8487ceae 100644
--- a/packages/python/plotly/plotly/tests/test_core/test_update_objects/test_update_traces.py
+++ b/packages/python/plotly/plotly/tests/test_core/test_update_objects/test_update_traces.py
@@ -362,3 +362,18 @@ def test_update_traces_by_grid_and_selector(self):
         )
 
         self.assert_update_traces([9], {"marker.size": 6}, col=1, secondary_y=True)
+
+    def test_update_traces_overwrite(self):
+        fig = go.Figure(
+            data=[go.Scatter(marker_line_color="red"), go.Bar(marker_line_color="red")]
+        )
+
+        fig.update_traces(overwrite=True, marker={"line": {"width": 10}})
+
+        self.assertEqual(
+            fig.to_plotly_json()["data"],
+            [
+                {"type": "scatter", "marker": {"line": {"width": 10}}},
+                {"type": "bar", "marker": {"line": {"width": 10}}},
+            ],
+        )