Skip to content

SVG content does not re-render after state update when marker props are used on iOS #2937

Description

@rheidari

Description

When an SVG element (e.g. <Path>) uses the markerEnd prop, state changes that occur after mount do not cause the SVG to visually re-render on iOS. React processes the state update and re-renders the component tree correctly, but the SVG canvas never redraws — so any child element whose output depends on the updated state (e.g. a <Text> showing a counter) is stuck displaying the initial value.

Expected behavior: When state updates, the SVG should re-render and display the new values — e.g. the <Text> counter should advance from 0 to 1 after one second.

Actual behavior: The <Text> counter inside the SVG permanently shows 0. The state update fires (React processes it), but the SVG canvas never redraws to reflect it. The below reproduction is a trivial example, but it's a larger issue for us as we're building a whiteboard drawing application and this issue is preventing us from being able to allow users to draw paths with arrowheads in iOS as nothing is being re-rendered after the initial path is drawn.

Potential root cause (for maintainers):

- (void)setMarkerPath:(CGPathRef)markerPath
{
if (_markerPath == markerPath) {
return;
}
CGPathRelease(_markerPath);
_markerPath = markerPath;
[self invalidate];
}
when we comment out [self invalidate] when state changes a re-render is triggered and the updated counter is displayed.

Steps to reproduce

  1. Render a <Svg> containing a <Defs>/<Marker> block and an element using the marker prop (e.g. marker="url(#selection)").
  2. Include a <Text> inside the SVG that displays a state value.
  3. In a useEffect, use setTimeout to update that state value once after 1 second.
  4. Run on iOS.
  5. Observe: the <Text> inside the SVG shows 0 initially and never updates to 1, even after the timeout fires.
  6. Removing the marker prop from the <Ellipse> causes the <Text> to update correctly.

Minimal reproduction:
Update ellipse example within this repo (apps/common/example/examples/Markers.tsx)

import React, { useEffect, useState } from 'react';
import { Circle, Defs, Ellipse, Marker, Svg, Text } from 'react-native-svg';

export default function EllipseExample() {
  const [secondValue, setSecondValue] = useState(0);

  useEffect(() => {
    setTimeout(() => {
      setSecondValue(v => v + 1);
    }, 1000);
  }, []);

  return (
    <Svg height="300" width="400">
      <Defs>
        <Marker
          id="selection"
          markerUnits="userSpaceOnUse"
          refX="0"
          refY="0"
          orient="auto">
          <Circle fill="#3a6cff" r={5} cx={0} cy={0} strokeWidth={1} stroke="white" />
        </Marker>
      </Defs>
      {/* This text never updates from 0 to 1 when marker prop is present */}
      <Text x={5} y={10} fill="black">{secondValue}</Text>
      <Ellipse
        cx="200" cy="170" rx="30%" ry="10%"
        stroke="purple" strokeWidth="2" fill="yellow"
        marker="url(#selection)"
      />
    </Svg>
  );
}

Note: Removing marker="url(#selection)" from the <Ellipse> causes the <Text> to correctly update to 1 after one second.

Snack or a link to a repository

https://github.com/rheidari/react-native-svg/blob/main/apps/common/example/examples/Markers.tsx#L15-L59

SVG version

15.15.4 (current version as defined in the package.json of this repo)

React Native version

^0.77.0 (current version as defined in the package.json of this repo)

Platforms

iOS

JavaScript runtime

Hermes

Workflow

React Native

Architecture

Fabric (New Architecture)

Build type

Debug app & dev bundle

Device

iOS simulator

Device model

iPhone 17

Acknowledgements

Yes

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions