# ContentDetail

Canonical page: https://docs.ovok.com/native-sdk/guide/components/content/content-detail

`ContentDetail` is a scrollable compound component for content that may come from a FHIR `Composition`. The SDK supplies extraction helpers, but the app controls layout, loading, data fetching, and navigation.

```tsx
import { ContentDetail } from "@ovok/native";

<ContentDetail composition={composition}>
  <ContentDetail.Header>
    <ContentDetail.Title />
    <ContentDetail.Metadata>
      <ContentDetail.Metadata.Author />
      <ContentDetail.Metadata.Separator />
      <ContentDetail.Metadata.Date />
    </ContentDetail.Metadata>
  </ContentDetail.Header>
  <ContentDetail.Body>
    <ContentDetail.RichContent />
  </ContentDetail.Body>
  <ContentDetail.Footer>
    <ContentDetail.Tags />
    <ContentDetail.Resource />
  </ContentDetail.Footer>
</ContentDetail>;
```

## Root and context [#root-and-context]

The root is a `ScrollView`, accepts `ScrollViewProps` plus optional `composition`, and provides that composition through context. `Header`, `Body`, and `Footer` render React Native `View` containers and accept `ViewProps`. `Metadata` also accepts `ViewProps` and lays out children in a row.

`useComposition` is a named export from `@ovok/native` and must be called below `ContentDetail`; outside the provider it throws `useComposition must be used within a CompositionContext`.

## Title and rich content [#title-and-rich-content]

- `ContentDetail.Title` accepts optional string children or reads `composition.title`. It throws if neither exists and renders Paper `Text` with `variant="titleLarge"`.
- `ContentDetail.RichContent` accepts `content` plus `RenderHTMLProps` except `source`. If `content` is absent, it uses the first composition section with `text.div`, without excluding sections coded `reading-time` or `resource-link`; put the article body first or pass `content` explicitly. It throws when no HTML is available.
- Rich content uses `react-native-render-html`. `<img>` elements render through `OptimizedImage`; caller-supplied `baseStyle`, `customHTMLElementModels`, and `renderers` are merged with the SDK defaults.

## Metadata [#metadata]

Metadata items render Paper `Text` with the `bodyMedium` variant by default.

- `Metadata.Author` uses `composition.meta.author.display` unless children are supplied.
- `Metadata.Date` uses the top-level `composition.date`, formats valid dates as `dd MMM yyyy`, and renders `Invalid date` for an invalid value.
- `Metadata.ReadingTime` finds a section coded `reading-time`, strips HTML from its `text.div`, and renders the result.
- `Metadata.Item` is the base item for caller-owned content.
- `Metadata.Separator` defaults to `·`.

Each item accepts optional children and Paper text props. There is no built-in author, date, reading-time, or error translation layer beyond the resource label below.

## Tags and resource link [#tags-and-resource-link]

`ContentDetail.Tags` accepts optional `tags?: Coding[]`, `onTagPress`, `chipProps`, and `ViewProps`. Without `tags`, it reads `composition.meta.tag`. Each tag displays `tag.display` or `tag.code`; the component returns `null` when there are no tags. `chipProps` cannot override the generated children or `onPress`.

`ContentDetail.Resource` looks for a composition section containing coding `resource-link`, extracts the URL using the current anchor parser, and opens it with `expo-web-browser` by default. Until the parser is corrected, make the anchor's visible text exactly the same as its `href`; otherwise the visible text can be returned as the URL. Supply `onPress(resourceLink)` to own the action. It returns `null` when no resource link is found. Its visible label is localized with `content.detail.view-resource`.

### ContentDetail.Resource props [#contentdetailresource-props]

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `onPress` | `(resourceLink: string) => void` | Opens with `expo-web-browser` | Own the resource action. |
| `style` | `StyleProp<TextStyle>` | `undefined` | Style for the resource label. |
| `testID` | `string` | `undefined` | Test identifier. |

The matching Composition section must contain a coding with `code: "resource-link"`
and an HTML anchor in `section.text.div`, for example
`<a href="https://example.com/manual">Manual</a>`. A plain URL string is not parsed;
the current parser only works reliably when the visible anchor text is also the URL;
when no matching section or anchor is present, the component returns `null`.

The content-detail module uses only the `content.detail.view-resource` translation
key. Metadata values are formatted directly and do not use built-in `content.metadata.*`
or `content.error.*` keys.

## Styling and ownership [#styling-and-ownership]

The root adds theme-based gap and padding to `contentContainerStyle`; caller values are merged afterward. The component does not validate a FHIR resource beyond the fields it reads, does not load remote content, and does not own authentication, caching, or routing.
