Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Create or update component documentation in Sentry's MDX stories format. Use when asked to "document a component", "add stories", "write component docs", "create an mdx file", "add a stories.mdx", or document a design system component. Generates structured MDX with live demos, accessibility guidance, and auto-generated API docs from TypeScript types.
.claude/skills/react-component-documentation/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | — | — |
| case-08 | ✗→✓ | ▲ Improved | — | — |
| case-14 | ✗→✓ | ▲ Improved | — | — |
| case-10 | ✗→✓ | ▲ Improved | — | — |
| case-11 | ✗→✓ | ▲ Improved | — | — |
Create a .mdx file for a Sentry component following the conventions in static/app/components/core/.
Before writing, collect the information that makes documentation useful beyond mechanical structure. Ask the user these questions — or, if they aren't available, search existing usages in the codebase (Grep for the component name across static/app/views/) to infer answers.
| Question | Where it surfaces in the docs | | --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | | When should a developer reach for this component? | Introduction or a ## When to use section | | When should they NOT use it — and what should they use instead? | > [!WARNING] callout or ## See Also with guidance | | What does each variant/priority mean semantically? | Description in each variant's section (e.g., "danger = destructive and irreversible") | | What do developers commonly get wrong? | > [!WARNING] callouts, icon-only accessibility notes, required prop reminders | | Does this component require a specific parent, peer, or provider to work correctly? | Noted in the introduction or a > [!NOTE] callout | | Are there related components that overlap in purpose? | ## See Also with one-line guidance on when to prefer each | | Which props and variants are worth documenting with a demo? | See prop triage below |
You don't need answers to all questions for every component. Skip ones that don't apply. The goal is to not write docs that only describe _how_ to use the API — write docs that tell developers _when and why_.
Not every prop needs a demo section. Ask the user: "Which props should I document, and are there any variants or values with specific intended uses?"
If the user isn't available, read the component's TypeScript props and classify each:
| Tier | Document how | Examples | | ------------------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------- | | Core — defines the component's primary behavior or appearance | Full ## section with live demo and semantic description of each value | priority, variant, size | | Modifier — adjusts a single aspect; values are self-explanatory | Brief mention with a demo, or a single combined demo with other modifiers | disabled, busy, icon, showIcon | | Structural — controls layout or composition | Demo showing the before/after or compound usage | system, expand, trailingItems | | Internal / pass-through — not user-facing | Skip entirely | className, style, ref, data-test-id |
For enum props specifically, always ask: "Does each value have a distinct intended meaning, or are they purely visual?" If distinct (e.g., danger means destructive, not just red), document the semantics — not just the visual difference.
Find the component source file:
static/app/components/core/<category>/<component>/index.tsx
static/app/components/core/<category>/<component>/<component>.tsxRead the component file to understand:
Component.SubComponent, export {TabList, TabPanels})The MDX file goes next to the component: <component-dir>/<component>.mdx.
If the file already exists, read it first and update rather than overwrite.
Determining the import path:
static/app/components/core/ are published as @sentry/scraps/<name>sentry/components/<path>To confirm the exact @sentry/scraps package name and type-loader path, check an existing import in the component directory or a neighboring .mdx file — the type-loader path can be @sentry/scraps/<name> or @sentry/scraps/<name>/<name> depending on the package structure.
yaml--- title: <ComponentName> description: <One sentence describing what it is and its primary purpose.> category: <category> # See category table below; omit for principle docs source: '@sentry/scraps/<component>' # or 'sentry/<path>' for product components resources: figma: <figma-url> # Include if known js: https://github.com/getsentry/sentry/blob/master/static/app/components/core/<path> a11y: # Include for interactive components WCAG 1.4.3: https://www.w3.org/TR/WCAG22/#contrast-minimum WAI-ARIA <Pattern> Practices: https://www.w3.org/WAI/ARIA/apg/patterns/<pattern>/ ---
Category values:
| Category | Components | | ------------ | ---------------------------------------------- | | buttons | Button, ButtonBar, LinkButton | | forms | Input, Select, Checkbox, Radio, Slider, Switch | | navigation | Tabs, SegmentedControl, Disclosure | | status | Alert, Badge, Tag, Toast | | layout | Flex, Grid, Stack, Container, Surface | | typography | Text, Heading, Prose | | patterns | design patterns, principles |
For principle/pattern docs (no interactive component), use layout: document instead of category, and replace a11y: with reference: under resources.
Follow this import order exactly:
jsx// 1. External packages (react, etc.) — only if needed for examples import {useState} from 'react'; // 5. Type-loader for auto-generated API docs (@sentry/scraps components only) import documentation from '!!type-loader!@sentry/scraps/<component>'; // 3. @sentry/scraps component(s) import {ComponentName} from '@sentry/scraps/<component>'; // 2. Sentry internals used in examples (icons, utils) import {IconAdd, IconEdit} from 'sentry/icons'; // 4. Stories namespace (always last before type-loader) import * as Storybook from 'sentry/stories'; export {documentation};
Omit any group that isn't needed. For product components (not in @sentry/scraps), omit the type-loader lines and document props manually in a table (see Step 5).
Complex export: If the component exports need filtering (e.g., to hide internal exports), use the explicit form instead of export {documentation}:
jsximport RawDocumentation from '!!type-loader!@sentry/scraps/<component>'; export const documentation = { exports: RawDocumentation.exports, props: { ...RawDocumentation?.props, // Remove internal props if needed }, };
Organize content by feature or user-facing variant, not by prop name:
<Storybook.SideBySide>## per major feature: ## Sizes, ## Priorities, ## States, ## CompositionPrefer titles like ## Sizes, ## Variants, ## States over ## The size prop.
When to use / See Also patterns:
mdx## When to use Use `<Alert>` for inline feedback within a page. For application-level banners that span the full viewport, use the `system` prop or reach for `<Toast>` if the message is transient. > [!WARNING] > Do not use `<Alert variant="danger">` for confirmation dialogs. Use a modal instead.
mdx## See Also - [LinkButton](/stories/core/linkbutton/) — use when the action navigates to a new URL - [Link](/stories/core/link/) — use for inline text navigation, not standalone CTAs
`mdxTo create a basic <component>, wrap content in `<ComponentName>`.
<ComponentName prop="value">Content</ComponentName>
`### Sub-components When a component exposes sub-components, show the full compound usage early:
<Tabs> is a compound component. Use <TabList> and <TabPanels> together:
jsx<Tabs> <TabList> <TabList.Item key="tab1">Tab 1</TabList.Item> </TabList> <TabPanels> <TabPanels.Item key="tab1">Content 1</TabPanels.Item> </TabPanels> </Tabs>
`### Demo pattern Every feature section must have a `<Storybook.Demo>` followed **immediately** by the matching code block:
<brief description>
<Storybook.Demo> <Component size="sm">Small</Component> <Component size="md">Medium</Component> <Component size="lg">Large</Component> </Storybook.Demo>
jsx<Component size="sm">Small</Component> <Component size="md">Medium</Component> <Component size="lg">Large</Component>
`**Demo layout helpers:** | Component | Use when | |-----------|----------| | `<Storybook.Demo>` | Default; horizontally arranges examples | | `<Storybook.Grid>` | Grid layout for many variants | | `<Storybook.SideBySide>` | Two-column comparisons (do/don't) | | `<Storybook.TokenReference>` | Displaying design tokens (spacing, color, etc.) | | `<Storybook.ColorReference>` | Displaying color tokens specifically | ### Accessibility section
This component meets WCAG 2.2 AA standards:
aria-label or visible label text)`### Callout syntax
> !TIP] > Use the <prop> prop when you need <use case>.
> !WARNING] > Avoid <pattern> because <reason>.
> !NOTE] > > <Additional context.>
## Step 5: Props Table (product components only)
For components not in `@sentry/scraps`, list props manually instead of using the type-loader:
| Prop | Type | Default | Description | | --------- | --------------------------------- | -------- | ------------------------- | | variant | 'info' \| 'warning' \| 'danger' | 'info' | Controls the visual style | | size | 'sm' \| 'md' \| 'lg' | 'md' | Controls the size |
## Complete Example
title: Alert description: Alerts provide contextual feedback messages with different severity levels. category: status source: '@sentry/scraps/alert' resources: js: https://github.com/getsentry/sentry/blob/master/static/app/components/core/alert/index.tsx a11y: WCAG 1.4.3: https://www.w3.org/TR/WCAG22/#contrast-minimum WCAG 2.1.1: https://www.w3.org/TR/WCAG22/#keyboard WAI-ARIA Alert Pattern: https://www.w3.org/WAI/ARIA/apg/patterns/alert/
import {Alert} from '@sentry/scraps/alert';
import as Storybook from 'sentry/stories';
import documentation from '!!type-loader!@sentry/scraps/alert';
export {documentation};
To create a basic alert, wrap a message in <Alert> and specify the appropriate type.
jsx<Alert variant="info">This is an informational message</Alert>
`## Types Alerts come in five types: `muted`, `info`, `warning`, `success`, and `danger`. <Storybook.Demo> <Alert.Container> <Alert variant="muted">Muted</Alert> <Alert variant="info">Info</Alert> <Alert variant="warning">Warning</Alert> <Alert variant="success">Success</Alert> <Alert variant="danger">Danger</Alert> </Alert.Container> </Storybook.Demo>
<Alert.Container> <Alert variant="muted">Muted</Alert> <Alert variant="info">Info</Alert> <Alert variant="warning">Warning</Alert> <Alert variant="success">Success</Alert> <Alert variant="danger">Danger</Alert> </Alert.Container>
## Accessibility
This component meets [WCAG 2.2 AA](https://www.w3.org/TR/WCAG22/) standards:
- **Color contrast**: All variants meet 4.5:1 ratio (WCAG 1.4.3)
- **Keyboard accessible**: Fully operable via keyboard (WCAG 2.1.1)
## Checklist
Before completing, verify:
- [ ] MDX file is colocated next to the component source file (named `<component>.mdx`)
- [ ] Frontmatter has `title`, `description`, and `source`
- [ ] Imports follow the correct order (external → icons/utils → @sentry/scraps → Storybook → type-loader)
- [ ] Type-loader import is included for `@sentry/scraps` components
- [ ] Every `<Storybook.Demo>` is immediately followed by a matching code block
- [ ] Sections are organized by feature/variant, not by prop name
- [ ] Sub-components are documented with compound usage examples
- [ ] Accessibility section covers WCAG compliance and developer responsibilities
- [ ] No raw `<img>` tags (use `<Image>`), no inline SVGs, no styled components| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-08 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-09 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-06 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-14 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-16 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-05 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-07 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-19 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-10 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-11 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-20 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-13 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-15 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-17 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-18 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-04 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-12 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-22 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-03 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-21 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-02 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +45 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
The per-case answers from this run were removed by the retention sweep, so the case table below shows the verdicts without the text either arm produced. The counts above were recorded at the time and are unaffected. Answers are now kept for 180 days.
Other measured skills in the registry, with their headline benchmark lift.