Skip to content

Conversation

@dariusz-biela
Copy link

Summary

This PR adds support for the data-sentry-label attribute in htmlTreeAsString function, allowing developers to annotate DOM elements with stable identifiers for better breadcrumb and INP span identification.

This is a partial solution to getsentry/sentry#104128.

Changes

  • Added data-sentry-label attribute detection in _htmlElementAsString() - takes priority over data-sentry-component and data-sentry-element
  • Added _getSentryLabel() helper function that traverses up to 15 DOM levels to find the nearest data-sentry-label attribute
  • If data-sentry-label is found on an ancestor (beyond the standard 5-level traversal), it prefixes the CSS selector:
    [data-sentry-label="ProductCard"] div.container > button.btn
  • Added comprehensive unit tests for the new functionality

Why this helps

In React Native Web and other frameworks with long/generated CSS class names, the current 80-character limit causes selectors to be truncated immediately, producing highly ambiguous selectors that can match 100+ elements on a page.

With data-sentry-label, developers can annotate interactive elements with stable identifiers:

 <div data-sentry-label="Product-ItemRow">
    <div class="css-175oi2r r-1awozwy r-18u37iz r-1wtj0ep">
      <div class="css-175oi2r r-1awozwy r-6koalj r-18u37iz">
         <div class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3">
          Item Title
        </div>
      </div>
    </div>
  </div>

Before (current behavior):

div.css-1jxf684.r-bcqeeo.r-1ttztb7.r-qvutc0.r-poiln3

This selector hits the 80-character limit immediately. If these classes represent common text styling, the selector matches every text element with that styling across the entire application.

After (with this PR):

[data-sentry-label="Product-ItemRow"] div.css-1jxf684.r-bcqeeo.r-1ttztb7.r-qvutc0.r-poiln3

The label prefix identifies which specific row the interaction occurred in.

This significantly improves the precision and usefulness of generated CSS selectors in INP spans.

Checklist

Before submitting a pull request, please take a look at our
Contributing guidelines and verify:

  • If you've added code that should be tested, please add tests.
  • Ensure your code lints and the test suite passes (yarn lint) & (yarn test).

@AbhiPrasad
Copy link
Member

AbhiPrasad commented Dec 3, 2025

We have a solution for this already: https://docs.sentry.io/platforms/javascript/guides/react/features/component-names/

You can set data-sentry-component to be the component name, and optionally data-sentry-source-file to be the source file. The component name should be included in the selector.

<div
  data-sentry-component="MyAwesomeComponent"
  data-sentry-source-file="myAwesomeComponent.jsx"
>
  This is a really cool and awesome component!
</div>

If you're using React Native, this is bundled in by default: https://docs.sentry.io/platforms/react-native/integrations/component-names/.

const { getDefaultConfig } = require("@react-native/metro-config");
const { withSentryConfig } = require("@sentry/react-native/metro");
const config = getDefaultConfig(__dirname);
module.exports = withSentryConfig(config, {
  annotateReactComponents: true,
});

@dariusz-biela
Copy link
Author

I started proposing this solution because I thought Sentry rarely marked elements with its own attributes.

image

When I checked today, it turned out that these attributes are from a different library; Sentry attributes don't work for us at all.

We use webpack/babel to build React Native for the web, and adding the following code doesn't change anything in our HTML:

sentryWebpackPlugin({
         reactComponentAnnotation: {
              enabled: true,
          },
 }) 

I will try to find out why this configuration does not work for us and will get back to you with information under this PR.

@dariusz-biela
Copy link
Author

I've added a patch to React Native Web that extends forwardPropsList with:

+ // Sentry props
+ dataSentryComponent: true,
+ dataSentryElement: true,
+ dataSentrySourceFile: true

and now adding attributes to HTML works, but unfortunately they don't alleviate the problem.

They add information about generic components, such as GenericPressable and OfflineWithFeedback, which are used for every button and container in our app.

image

@dariusz-biela
Copy link
Author

Do you have any ideas on how we can improve the behavior regarding which attribute values ​​are added to HTML?

@AbhiPrasad
Copy link
Member

Do you have any ideas on how we can improve the behavior regarding which attribute values ​​are added to HTML?

Is the issue that you want to control what components get the attribute value? Can you set custom one's yourself?

Or is it that for some components the injected component name is too generic so you actually lose information by enabling reactComponentAnnotation.

@dariusz-biela
Copy link
Author

Is the issue that you want to control what components get the attribute value? Can you set custom one's yourself?

I'd like Sentry to have an element handler for the INP metric that narrows down what was actually clicked, rather than a selector that points to every button in our app.
It can be automatically generated or configured by the developer.

Or is it that for some components the injected component name is too generic so you actually lose information by enabling reactComponentAnnotation.

This is also a problem.

I've now reviewed the React component structure, and there doesn't seem to be any way to make the reactComponentAnnotation solution work well there.

Furthermore, even if I create a wrapper around GenericPressable called ViewDetailsPressable, reactComponentAnnotation will still take on the GenericPressable for data-sentry-component.

image

@AbhiPrasad
Copy link
Member

Instead of using reactComponentAnnotation, could you set the data-sentry-component value yourself?

@dariusz-biela
Copy link
Author

dariusz-biela commented Dec 4, 2025

Instead of using reactComponentAnnotation, could you set the data-sentry-component value yourself?

Unfortunately, adding this attribute to all elements seems impossible. If I don't, I end up with the following example:

The current CSS selector generator used for INP spans is too limited (function htmlTreeAsString):

  • it only walks ~5 parent levels up the DOM,
  • it stops once the selector exceeds ~80 characters.

In this example I will never get the component information because the div will hit the 80 character limit via the css selector.

<button data-sentry-component="DetailsButton">
  <div class="css-view-g5y9jx r-WebkitUserSelect-9ffhgg r-alignItems-1awozwy r-backgroundColor-18lll9h r-flexDirection-18u37iz r-justifyContent-1h0z5md r-paddingBottom-kzbkwu r-userSelect-lrvibr r-width-13qz1uu r-paddingBlock-1mmae3n r-paddingInline-1fkl15p">
    Text
  </div>
</button>

That's why it is so important for me to do the bypass:

Added _getSentryLabel() helper function that traverses up to 15 DOM levels to find the nearest data-sentry-label attribute
If data-sentry-label is found on an ancestor (beyond the standard 5-level traversal), it prefixes the CSS selector:
[data-sentry-label="ProductCard"] div.container > button.btn

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants