Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 37 additions & 40 deletions docs/platforms/android/snapshots/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ Paparazzi and ComposePreviewScanner automatically turns every `@Preview` composa

First, choose a Paparazzi version compatible with your project:

| Version | Gradle | compileSdk | JDK | compose-bom |
| --------------- | -------------- | ---------- | --- | ------------ |
| `2.0.0-alpha05` | 9.x+ | 36 | 21+ | > 2025.05.00 |
| `1.3.5` | 8.x or 9.x | ≤ 35 | 17+ | ≤ 2025.04.00 |
| Version | Gradle | compileSdk | JDK | compose-bom |
| --------------- | ---------- | ---------- | --- | ------------ |
| `2.0.0-alpha05` | 9.x+ | 36 | 21+ | > 2025.05.00 |
| `1.3.5` | 8.x or 9.x | ≤ 35 | 17+ | ≤ 2025.04.00 |

Then apply the plugin:

Expand Down Expand Up @@ -172,7 +172,10 @@ sentry {
```

<Alert level="warning">
If `theme` references a style the renderer can't resolve (typo, wrong prefix, or a theme not on the classpath), neither Sentry nor Paparazzi raises an error or warning. Snapshots still generate, but with incorrect visuals. Double-check the theme string if rendered output looks unexpected.
If `theme` references a style the renderer can't resolve (typo, wrong prefix,
or a theme not on the classpath), neither Sentry nor Paparazzi raises an error
or warning. Snapshots still generate, but with incorrect visuals. Double-check
the theme string if rendered output looks unexpected.
</Alert>

### Set a Global Diff Threshold
Expand Down Expand Up @@ -215,11 +218,36 @@ fun BillingPagePreview() {

Per-preview values take precedence over the global `diffThreshold`. See [Diff Thresholds](/product/snapshots/reviewing-snapshots/#diff-thresholds) for other ways to configure thresholds.

### How Preview Annotations Map to Snapshot Metadata
### Preview Annotation Reference

When you use the Compose previews path, the plugin renders one image per `@Preview` and writes a JSON metadata file next to it. The annotations on the composable control everything about the snapshot: how it's rendered, how it's named and grouped in Sentry, and how it's compared against its baseline.

The table below lists all supported attributes across Google's [`@Preview`](https://developer.android.com/reference/kotlin/androidx/compose/ui/tooling/preview/Preview) annotation and Sentry's `@SentrySnapshot` annotation. The Metadata column shows how each attribute is recorded in the snapshot's [JSON metadata](/product/snapshots/uploading-snapshots/#json-metadata). Tags are only written when the attribute differs from its default, and tag values are always serialized as strings, even for numeric and boolean attributes.

| Attribute | Annotation | Description | Metadata |
| ----------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------- |
| `name` | `@Preview` | Distinguishes multiple previews of the same composable — several `@Preview` annotations or a multipreview annotation like `@PreviewLightDark`. Appended to the `display_name`. | `preview_name` tag |
| `group` | `@Preview` | Groups related snapshots so they're reviewed as a set in Sentry. Also becomes part of the `display_name`. | `group` field |
| `device` | `@Preview` | Device spec (for example `id:pixel_7`) that sets the screen size, density, and shape used for rendering. | `device` tag |
| `widthDp` | `@Preview` | Fixed render width in dp. Overrides the width of the device. | `width_dp` tag |
| `heightDp` | `@Preview` | Fixed render height in dp. Overrides the height of the device. | `height_dp` tag |
| `locale` | `@Preview` | Locale used for rendering. Defaults to `en`. | `locale` tag |
| `fontScale` | `@Preview` | Font scale multiplier applied when rendering. | `font_scale` tag |
| `apiLevel` | `@Preview` | Android API level to render against. | `api_level` tag |
| `uiMode` | `@Preview` | Only the night mode bit is honored: `UI_MODE_NIGHT_YES` renders the preview in dark mode. | `ui_mode` tag (`dark` or `light`) |
| `showSystemUi` | `@Preview` | Renders the preview at full device size. | `show_system_ui` tag |
| `showBackground` | `@Preview` | Draws a background behind the preview — white unless `backgroundColor` is set. | `show_background` tag |
| `backgroundColor` | `@Preview` | ARGB color of the background when `showBackground` is `true`. | — |
| `wallpaper` | `@Preview` | Ignored. Dynamic color wallpapers aren't applied when rendering. | — |
| `diffThreshold` | `@SentrySnapshot` | Minimum share of changed pixels (`0.0` to `1.0`) required to report the snapshot as changed. Overrides the global threshold — see [Set Diff Thresholds Per Preview](#set-diff-thresholds-per-preview). | `diff_threshold` field |

When you use the Compose previews path, the plugin renders one image per `@Preview` and writes a JSON metadata file next to it. The attributes you set on `@Preview` shape that metadata, which controls how the snapshot is named and grouped in Sentry. For the full schema, see [Uploading Snapshots](/product/snapshots/uploading-snapshots/#json-metadata).
<Alert level="info">
Spaces in `name` and `group` are replaced with underscores in the generated
`display_name` and filename, but the `group` field and `preview_name` tag keep
the original value.
</Alert>

Look at this example preview:
For example, this preview:

```kotlin

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The description for diffThreshold is ambiguous, suggesting an inclusive threshold (>=) while the actual behavior is exclusive (>), creating an inconsistency with other documentation.
Severity: LOW

Suggested Fix

Update the description for diffThreshold to explicitly state that the share of changed pixels must be "greater than" or "exceed" the value to be reported as changed. This will align it with the actual behavior and other documentation.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: docs/platforms/android/snapshots/index.mdx#L252

Potential issue: The new documentation for `diffThreshold` describes it as the "Minimum
share of changed pixels...required to report," which implies an inclusive comparison
(>=). This contradicts the actual behavior and existing documentation across multiple
files, which state that a snapshot is reported as changed only when the share of changed
pixels "exceeds" or is "greater than" the threshold (>). This ambiguity could cause
users to misconfigure the threshold, expecting a change to be reported at the exact
threshold value when it will not be.

Did we get this right? 👍 / 👎 to inform future reviews.

@Preview(name = "Dark", group = "Checkout")
Expand All @@ -229,7 +257,7 @@ fun BillingPagePreview() {
}
```

It produces the following metadata:
produces the following metadata:

```json
{
Expand All @@ -245,34 +273,3 @@ It produces the following metadata:
}
}
```

#### `group`

`@Preview(group = "...")` sets the top-level `group` field, which Sentry uses to organize related snapshots in the review UI. Give previews that belong together the same `group` so they're reviewed as a set. The group also becomes part of the generated `display_name`.

#### `name`

`@Preview(name = "...")` distinguishes multiple previews of the same composable. It's recorded as the `preview_name` tag and appended to the `display_name`. This matters most when one composable has several previews — either multiple `@Preview` annotations or a multipreview annotation like `@PreviewLightDark` — since each variant becomes its own snapshot and `name` is what tells them apart.

<Alert level="info">
Spaces in `name` and `group` are replaced with underscores in the generated
`display_name` and filename, but the `group` field and `preview_name` tag keep
the original value.
</Alert>

#### Tags

The remaining `@Preview` attributes are captured as `tags` describing how the preview was rendered. Each tag is included only when the attribute differs from its default. Tag values are always serialized as strings, even for numeric and boolean attributes:

| Tag | `@Preview` attribute | Example value |
| ----------------- | -------------------- | -------------- |
| `preview_name` | `name` | `"Dark"` |
| `locale` | `locale` | `"de"` |
| `device` | `device` | `"id:pixel_7"` |
| `font_scale` | `fontScale` | `"1.5"` |
| `api_level` | `apiLevel` | `"34"` |
| `width_dp` | `widthDp` | `"320"` |
| `height_dp` | `heightDp` | `"640"` |
| `show_system_ui` | `showSystemUi` | `"true"` |
| `show_background` | `showBackground` | `"true"` |
| `ui_mode` | `uiMode` (night bit) | `"dark"` |
Loading