Skip to main content

Content

A comprehensive content management system for React Native applications that provides components for displaying, organizing, and managing various types of content including articles, media, and rich text. The Content module offers three main compound components for different content presentation patterns.

Overview

The Content module is built around FHIR Composition resources and provides a complete ecosystem for content management in healthcare and general applications. It offers flexible, reusable components that handle everything from simple content cards to complex detailed content views with metadata, tags, and rich HTML rendering.

Key Features

  • FHIR Integration: Native support for FHIR Composition resources with automatic data extraction
  • Rich Text Rendering: Advanced HTML content rendering with custom image optimization
  • Compound Components: Flexible component architecture for maximum customization
  • Content Cards: Beautiful card layouts for content previews and listings
  • Content Lists: Powerful list components with horizontal scrolling and sections
  • Content Details: Full-featured content detail views with metadata and rich content
  • Theme Integration: Seamless integration with app themes and Material Design 3
  • Accessibility: Full accessibility support with screen readers and keyboard navigation
  • Image Optimization: Built-in image optimization through OptimizedImage integration
  • Internationalization: Built-in localization support for multilingual content

Components

ContentCard

A compound card component for displaying content previews with cover images, badges, and titles.

Sub-components:

  • ContentCard.Cover - Image cover with badge support
  • ContentCard.CoverBadge - Overlay badge for covers
  • ContentCard.Title - Styled title text
  • ContentCard.Content - Card content container

ContentDetail

A comprehensive content detail view with FHIR Composition integration and rich content rendering.

Sub-components:

  • ContentDetail.Header - Content header section
  • ContentDetail.Title - Main content title
  • ContentDetail.Metadata - Content metadata display
  • ContentDetail.Body - Main content body container
  • ContentDetail.RichContent - HTML content renderer
  • ContentDetail.Footer - Content footer section
  • ContentDetail.Tags - Content tags display
  • ContentDetail.Resource - External resource links

ContentList

A flexible list component for organizing and displaying multiple content items with sections and horizontal scrolling.

Sub-components:

  • ContentList.Section - Content section container
  • ContentList.Header - Section header with title and actions
  • ContentList.Title - Section title text
  • ContentList.HorizontalList - Horizontal scrolling content list
  • ContentList.ViewAll - View all action button

Architecture

FHIR Integration

The Content module is designed around FHIR Composition resources, providing automatic extraction of:

  • Title: From composition.title
  • Content: From composition.section[].text.div
  • Metadata: From composition.meta (author, date, tags)
  • Resources: From composition.section[] with resource links
  • Reading Time: From custom section with reading-time code

Content Rendering

  • HTML Rendering: Uses react-native-render-html for rich content
  • Image Optimization: Automatic optimization through OptimizedImage
  • Custom Renderers: Extensible rendering system for custom HTML elements
  • Responsive Design: Automatic adaptation to screen sizes

Theme Integration

  • Material Design 3: Full MD3 component integration
  • Custom Themes: Seamless theme switching and customization
  • Typography: Consistent typography using DMSans font family
  • Spacing: Theme-based spacing system
  • Colors: Automatic color adaptation based on theme

Usage Patterns

Basic Content Card

import { ContentCard } from "@ovok/native";
import { useRouter } from "expo-router";
import * as React from "react";

const content = {
id: "1",
title: "Understanding React Native",
image: "https://placehold.co/600x400/000000/FFFFFF/png",
};

const BasicContentCardExample = () => {
const router = useRouter();

return (
<ContentCard
onPress={() => router.navigate("/content-detail", { id: content.id })}
>
<ContentCard.Cover uri={content.image} badge="Featured" />
<ContentCard.Content>
<ContentCard.Title>{content.title}</ContentCard.Title>
</ContentCard.Content>
</ContentCard>
);
};

export default BasicContentCardExample;

Content Detail View

import { ContentDetail } from "@ovok/native";
import { Composition } from "@medplum/fhirtypes";
import * as React from "react";

const composition: Composition = {
resourceType: "Composition",
meta: {
profile: ["http://example.org/fhir/StructureDefinition/blog-post"],
tag: [
{
code: "test",
display: "test",
},
],
versionId: "f77b2a8b-5db2-4c64-bbc2-0907b2ab6eb3",
lastUpdated: "2025-03-25T11:37:54.897Z",
author: {
reference: "Practitioner/1dd3f5d5-043f-4c19-99c1-4ce087f919f3",
display: "Fatih Kayan",
},
project: "f950e8aa-63b1-4cc3-bc39-3fe05847c585",
compartment: [
{
reference: "Project/f950e8aa-63b1-4cc3-bc39-3fe05847c585",
},
],
},
identifier: {
system: "https://api.ovok.com/fhir/StructureDefinition/cms-blog",
value: "cms-blog",
},
status: "final",
date: "2025-03-25T11:37:54.730Z",
language: "en",
author: [
{
reference: "Practitioner/1dd3f5d5-043f-4c19-99c1-4ce087f919f3",
},
],
type: {
coding: [
{
system: "https://api.ovok.com/fhir/CodeSystem/cms-type",
code: "blog",
display: "blog",
},
],
},
category: [
{
coding: [
{
system: "https://api.ovok.com/fhir/CodeSystem/cms-category",
code: "New category",
display: "New category",
},
],
},
],
title: "Example app",
section: [
{
text: {
status: "generated",
div: '<div xmlns=\'http://www.w3.org/1999/xhtml\'><p><strong>Bold</strong></p>\n<p><strong><em>Italic</em></strong></p>\n<p><strong><em><ins>Underline</ins></em></strong></p>\n<p><del><strong><em><ins>Stroke</ins></em></strong></del></p>\n<h1><del><strong><em><ins>Heading</ins></em></strong></del></h1>\n<p><span style="font-size: 9px;">Small</span></p>\n<p><span style="font-size: 30px;">Big</span></p>\n<ul>\n<li><span style="font-size: 14px;">Bullet</span></li>\n</ul>\n<ol>\n<li><span style="font-size: 14px;">Number</span></li>\n</ol>\n<p style="text-align:right;"><span style="font-size: 14px;">Right</span></p>\n<p style="text-align:center;"><span style="font-size: 14px;">Center</span></p>\n<p style="text-align:justify;"><span style="color: rgb(26,188,156);font-size: 14px;">Colored</span></p>\n<p><span style="color: rgb(26,188,156);font-size: 14px;">😫</span></p>\n<p></p>\n<img src="https://api.ovok.com/binary/0b5b4257-0d41-448c-b200-a0d66ca1c0f5?publicResourceToken=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyZWZlcmVuY2UiOiJEb2N1bWVudFJlZmVyZW5jZS8wYjViNDI1Ny0wZDQxLTQ0OGMtYjIwMC1hMGQ2NmNhMWMwZjUiLCJpYXQiOjE3NDI5MDI2MzV9.ISm4Wd10U4J0uTJmj6_c1xjfnJB_3xqNn0Mcll5wgzM&type=inline" alt="" style="height: auto;width: auto"/>\n<p></p>\n</div>',
},
extension: [
{
url: "https://api.ovok.com/binary/007c7a7a-d4df-4699-bd45-55c837ace49a?publicResourceToken=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyZWZlcmVuY2UiOiJEb2N1bWVudFJlZmVyZW5jZS8wMDdjN2E3YS1kNGRmLTQ2OTktYmQ0NS01NWM4MzdhY2U0OWEiLCJpYXQiOjE3NDI5MDI2NjF9.YSwvNehmHg8l3FmI2C_MvT9wC3n97o4FfegupTZrgtc&type=inline",
valueString: "cover-media",
},
],
code: {
coding: [
{
code: "main-content",
},
],
},
},
{
code: {
coding: [
{
code: "resource-link",
},
],
},
text: {
status: "generated",
div: "<div xmlns='http://www.w3.org/1999/xhtml'><a>https://google.com</a></div>",
},
},
{
code: {
coding: [
{
code: "reading-time",
},
],
},
text: {
status: "generated",
div: "<div xmlns='http://www.w3.org/1999/xhtml'>122 minutes</div>",
},
},
],
id: "d1235111-1f9e-4c37-b234-11e984a27ea0",
};

const ContentDetailExample = () => {
return (
<ContentDetail composition={composition}>
<ContentDetail.Header>
<ContentDetail.Title />
<ContentDetail.Metadata>
<ContentDetail.Metadata.Date />
<ContentDetail.Metadata.Separator />
<ContentDetail.Metadata.ReadingTime />
<ContentDetail.Metadata.Separator />
<ContentDetail.Metadata.Author />
</ContentDetail.Metadata>
</ContentDetail.Header>

<ContentDetail.Body>
<ContentDetail.RichContent />
</ContentDetail.Body>

<ContentDetail.Footer>
<ContentDetail.Tags />
<ContentDetail.Resource />
</ContentDetail.Footer>
</ContentDetail>
);
};

export default ContentDetailExample;

Content List with Sections

import { ContentCard, ContentList, useAppTheme } from "@ovok/native";
import { Composition } from "@medplum/fhirtypes";
import { Stack } from "expo-router";
import * as React from "react";
import { ListRenderItem, View } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";

const contents = [
{
category: {
title: "Physical Health",
},
contents: [
{
resourceType: "Composition",
title: "Physical Health 101",
text: {
div: "Add description",
status: "generated",
},
section: [
{
code: {
id: "cover-image",
},
text: {
div: "<img src='https://dashboard.dev.ovok.com/api/storage/6714f6d5-c4d0-488d-84b3-cd0cced180e8/69119826-7b97-438d-823b-1164580ce535?Expires=1726779799218&Signature=PleNdPAl1Q1tdb6p4EMM070jYXgmOn2WJohWZW2p6tCw7E8bwxNM96pFO1JX8YzNuOjnBhqGC51unIwWDRkSQp%2BbJAommSHvQrhKUwOEA8krCCC81A1lAyTUjhPg6UP%2BeKpHnD8RBEqDRqT2x9ASK37pwZ8BkscQcolhWBpZMH0%3D' alt='cover-image' />",
status: "final",
},
entry: [
{
display:
"https://dashboard.dev.ovok.com/api/storage/6714f6d5-c4d0-488d-84b3-cd0cced180e8/69119826-7b97-438d-823b-1164580ce535?Expires=1726779799218&Signature=PleNdPAl1Q1tdb6p4EMM070jYXgmOn2WJohWZW2p6tCw7E8bwxNM96pFO1JX8YzNuOjnBhqGC51unIwWDRkSQp%2BbJAommSHvQrhKUwOEA8krCCC81A1lAyTUjhPg6UP%2BeKpHnD8RBEqDRqT2x9ASK37pwZ8BkscQcolhWBpZMH0%3D",
},
],
},
{
code: {
id: "text",
coding: [
{
code: "reading-time",
display: "1 min",
},
],
},
text: {
div: "<p>hey im batuhan how are you asgjagkalgjagajgagnoasgagasg</p>",
status: "final",
},
},
],
status: "active",
language: "en-GB",
category: [
{
coding: [
{
code: "language",
display: "en-GB",
},
],
text: "en-GB",
},
],
author: [
{
id: "d1ebaca2-efbd-4129-b849-338bf25f06b7",
resourceType: "Project",
display: "MindGuide",
},
],
subject: {
reference: "List/ff565b2b-3dc1-4d6a-ac13-29ebe8ab90cd",
},
type: {
id: "source",
text: "Source",
},
relatesTo: [
{
code: "appends",
targetReference: {
id: "urn:uuid:fd2c74a3-0adb-49d5-9904-fd053ad2e8be",
reference: "urn:uuid:fd2c74a3-0adb-49d5-9904-fd053ad2e8be",
},
},
],
date: "2024-09-19T20:03:19.114Z",
identifier: {
value: "content",
},
confidentiality: "N",
event: [
{
code: [
{
coding: [
{
code: "ae146a5d-6bac-45f9-b13d-c88ae08de576",
},
],
},
],
detail: [
{
reference: "CatalogEntry/ae146a5d-6bac-45f9-b13d-c88ae08de576",
},
],
},
],
id: "503c3ec2-e65f-4dbd-9104-7d28cf99a87a",
meta: {
versionId: "9eb3483d-53cf-4025-a795-306e60f2346d",
lastUpdated: "2024-09-19T20:03:19.465Z",
},
},
{
resourceType: "Composition",
meta: {
profile: ["http://example.org/fhir/StructureDefinition/blog-post"],
tag: [
{
code: "one",
display: "one",
},
{
code: "another",
display: "another",
},
],
versionId: "1e3cfad7-bf56-4afa-a10f-2a5393720607",
lastUpdated: "2024-12-03T09:11:02.178Z",
author: {
reference: "Practitioner/e4e6370a-828e-4316-acf8-2b42b7b4e276",
display: "Michael Immerza",
},
project: "9901f08b-9328-43fb-9745-9d19e8eec71a",
compartment: [
{
reference: "Project/9901f08b-9328-43fb-9745-9d19e8eec71a",
},
],
},
status: "final",
date: "2024-12-03T09:11:01.957Z",
language: "en",
author: [
{
reference: "Practitioner/e4e6370a-828e-4316-acf8-2b42b7b4e276",
},
],
type: {
coding: [
{
code: "template",
display: "template",
},
],
},
category: [
{
coding: [
{
code: "template",
},
],
},
],
title: "Very long title for testing a bit more text than expected",
section: [
{
text: {
status: "generated",
div: "<div xmlns='http://www.w3.org/1999/xhtml'><p><strong>Bold content<br><br></strong><strong><em>Bold italic</em></strong></p>\n</div>",
},
code: {
coding: [
{
code: "main-content",
},
],
},
entry: [
{
reference:
"DocumentReference/2c2787a3-2777-45b6-a599-cb5c893a7fb5",
},
],
},
],
id: "d45dc6ad-8fce-4cd1-8be0-b70ac712af0d",
},
{
resourceType: "Composition",
meta: {
profile: ["http://example.org/fhir/StructureDefinition/blog-post"],
tag: [
{
code: "one",
display: "one",
},
{
code: "another",
display: "another",
},
],
versionId: "1e3cfad7-bf56-4afa-a10f-2a5393720607",
lastUpdated: "2024-12-03T09:11:02.178Z",
author: {
reference: "Practitioner/e4e6370a-828e-4316-acf8-2b42b7b4e276",
display: "Michael Immerza",
},
project: "9901f08b-9328-43fb-9745-9d19e8eec71a",
compartment: [
{
reference: "Project/9901f08b-9328-43fb-9745-9d19e8eec71a",
},
],
},
status: "final",
date: "2024-12-03T09:11:01.957Z",
language: "en",
author: [
{
reference: "Practitioner/e4e6370a-828e-4316-acf8-2b42b7b4e276",
},
],
type: {
coding: [
{
code: "template",
display: "template",
},
],
},
category: [
{
coding: [
{
code: "template",
},
],
},
],
title: "Another title",
section: [
{
code: {
coding: [
{
code: "main-content",
},
],
},
text: {
status: "generated",
div: "<div xmlns='http://www.w3.org/1999/xhtml'><p><strong>Bold content<br><br></strong><strong><em>Bold italic</em></strong></p>\n</div>",
},
entry: [
{
reference:
"DocumentReference/2c2787a3-2777-45b6-a599-cb5c893a7fb5",
},
],
},
],
id: "d45dc6ad-8fce-4cd1-8be0-b70ac712af01",
},
] as Composition[],
},
{
category: {
title: "Mental Health",
},
contents: [
{
resourceType: "Composition",
title: "Mental Health 101",
text: {
div: "Add description",
status: "generated",
},
section: [
{
code: {
id: "cover-image",
},
text: {
div: "<img src='https://dashboard.dev.ovok.com/api/storage/6714f6d5-c4d0-488d-84b3-cd0cced180e8/69119826-7b97-438d-823b-1164580ce535?Expires=1726779799218&Signature=PleNdPAl1Q1tdb6p4EMM070jYXgmOn2WJohWZW2p6tCw7E8bwxNM96pFO1JX8YzNuOjnBhqGC51unIwWDRkSQp%2BbJAommSHvQrhKUwOEA8krCCC81A1lAyTUjhPg6UP%2BeKpHnD8RBEqDRqT2x9ASK37pwZ8BkscQcolhWBpZMH0%3D' alt='cover-image' />",
status: "final",
},
entry: [
{
display:
"https://dashboard.dev.ovok.com/api/storage/6714f6d5-c4d0-488d-84b3-cd0cced180e8/69119826-7b97-438d-823b-1164580ce535?Expires=1726779799218&Signature=PleNdPAl1Q1tdb6p4EMM070jYXgmOn2WJohWZW2p6tCw7E8bwxNM96pFO1JX8YzNuOjnBhqGC51unIwWDRkSQp%2BbJAommSHvQrhKUwOEA8krCCC81A1lAyTUjhPg6UP%2BeKpHnD8RBEqDRqT2x9ASK37pwZ8BkscQcolhWBpZMH0%3D",
},
],
},
{
code: {
id: "text",
coding: [
{
code: "reading-time",
display: "1 min",
},
],
},
text: {
div: "<p>hey im batuhan how are you asgjagkalgjagajgagnoasgagasg</p>",
status: "final",
},
},
],
status: "active",
language: "en-GB",
category: [
{
coding: [
{
code: "language",
display: "en-GB",
},
],
text: "en-GB",
},
],
author: [
{
id: "d1ebaca2-efbd-4129-b849-338bf25f06b7",
resourceType: "Project",
display: "MindGuide",
},
],
subject: {
reference: "List/ff565b2b-3dc1-4d6a-ac13-29ebe8ab90cd",
},
type: {
id: "source",
text: "Source",
},
relatesTo: [
{
code: "appends",
targetReference: {
id: "urn:uuid:fd2c74a3-0adb-49d5-9904-fd053ad2e8be",
reference: "urn:uuid:fd2c74a3-0adb-49d5-9904-fd053ad2e8be",
},
},
],
date: "2024-09-19T20:03:19.114Z",
identifier: {
value: "content",
},
confidentiality: "N",
event: [
{
code: [
{
coding: [
{
code: "ae146a5d-6bac-45f9-b13d-c88ae08de576",
},
],
},
],
detail: [
{
reference: "CatalogEntry/ae146a5d-6bac-45f9-b13d-c88ae08de576",
},
],
},
],
id: "503c3ec2-e65f-4dbd-9104-7d28cf99a87a",
meta: {
versionId: "9eb3483d-53cf-4025-a795-306e60f2346d",
lastUpdated: "2024-09-19T20:03:19.465Z",
},
},
{
resourceType: "Composition",
meta: {
profile: ["http://example.org/fhir/StructureDefinition/blog-post"],
tag: [
{
code: "one",
display: "one",
},
{
code: "another",
display: "another",
},
],
versionId: "1e3cfad7-bf56-4afa-a10f-2a5393720607",
lastUpdated: "2024-12-03T09:11:02.178Z",
author: {
reference: "Practitioner/e4e6370a-828e-4316-acf8-2b42b7b4e276",
display: "Michael Immerza",
},
project: "9901f08b-9328-43fb-9745-9d19e8eec71a",
compartment: [
{
reference: "Project/9901f08b-9328-43fb-9745-9d19e8eec71a",
},
],
},
status: "final",
date: "2024-12-03T09:11:01.957Z",
language: "en",
author: [
{
reference: "Practitioner/e4e6370a-828e-4316-acf8-2b42b7b4e276",
},
],
type: {
coding: [
{
code: "template",
display: "template",
},
],
},
category: [
{
coding: [
{
code: "template",
},
],
},
],
title: "Test title",
section: [
{
code: {
coding: [
{
code: "main-content",
},
],
},
text: {
status: "generated",
div: "<div xmlns='http://www.w3.org/1999/xhtml'><p><strong>Bold content<br><br></strong><strong><em>Bold italic</em></strong></p>\n</div>",
},
entry: [
{
reference:
"DocumentReference/2c2787a3-2777-45b6-a599-cb5c893a7fb5",
},
],
},
],
id: "d45dc6ad-8fce-4cd1-8be0-b70ac712af0d",
},
{
resourceType: "Composition",
meta: {
profile: ["http://example.org/fhir/StructureDefinition/blog-post"],
tag: [
{
code: "one",
display: "one",
},
{
code: "another",
display: "another",
},
],
versionId: "1e3cfad7-bf56-4afa-a10f-2a5393720607",
lastUpdated: "2024-12-03T09:11:02.178Z",
author: {
reference: "Practitioner/e4e6370a-828e-4316-acf8-2b42b7b4e276",
display: "Michael Immerza",
},
project: "9901f08b-9328-43fb-9745-9d19e8eec71a",
compartment: [
{
reference: "Project/9901f08b-9328-43fb-9745-9d19e8eec71a",
},
],
},
status: "final",
date: "2024-12-03T09:11:01.957Z",
language: "en",
author: [
{
reference: "Practitioner/e4e6370a-828e-4316-acf8-2b42b7b4e276",
},
],
type: {
coding: [
{
code: "template",
display: "template",
},
],
},
category: [
{
coding: [
{
code: "template",
},
],
},
],
title: "Last title",
section: [
{
code: {
coding: [
{
code: "main-content",
},
],
},
text: {
status: "generated",
div: "<div xmlns='http://www.w3.org/1999/xhtml'><p><strong>Bold content<br><br></strong><strong><em>Bold italic</em></strong></p>\n</div>",
},
entry: [
{
reference:
"DocumentReference/2c2787a3-2777-45b6-a599-cb5c893a7fb5",
},
],
},
],
id: "d45dc6ad-8fce-4cd1-8be0-b70ac712af03",
},
] as Composition[],
},
];

type Content = {
id: string;
title: string;
imageUrl: string;
};

type CategoryContent = {
category: {
id: string;
title: string;
};
contents: Content[];
};

const featuredContent = contents[0]?.contents?.[0];

const ContentListScreen = () => {
const { bottom: bottomInset } = useSafeAreaInsets();
const theme = useAppTheme();

const mappedContents = React.useMemo(() => {
return contents.map((content, categoryIndex) => ({
category: {
id: `category-${categoryIndex}`,
title: content.category.title,
},
contents: content.contents.map((content, contentIndex) => ({
id:
content.id ??
`content-category-${categoryIndex}-content-${contentIndex}`,
title: content.title,
imageUrl: `https://picsum.photos/id/${
categoryIndex + 1
}${contentIndex}/200`,
})),
}));
}, []);

const renderHeader = React.useCallback(() => {
return (
<View
style={{
paddingHorizontal: theme.spacing(2),
}}
>
<ContentCard>
<ContentCard.Cover
uri="https://picsum.photos/200"
badge="Today's Selection"
/>
<ContentCard.Content>
<ContentCard.Title>{featuredContent?.title}</ContentCard.Title>
</ContentCard.Content>
</ContentCard>
</View>
);
}, [theme]);

const renderItem: ListRenderItem<CategoryContent> = React.useCallback(
({ item }) => (
<ContentList.Section style={{ paddingHorizontal: theme.spacing(2) }}>
<View
style={{
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
}}
>
<ContentList.Title>{item.category.title}</ContentList.Title>
<ContentList.ViewAll onPress={() => {}} />
</View>
<ContentList.HorizontalList
horizontalPadding={theme.spacing(2)}
data={item.contents}
renderItem={({ item: content }) => (
<ContentCard style={{ width: 200 }}>
<ContentCard.Cover
uri={content.imageUrl}
style={{
height: 134,
borderRadius: 12,
}}
/>
<ContentCard.Content>
<ContentCard.Title variant="titleMedium">
{content.title}
</ContentCard.Title>
</ContentCard.Content>
</ContentCard>
)}
/>
</ContentList.Section>
),
[theme],
);

return (
<>
<Stack.Screen
options={{
headerTitle: "Content List",
}}
/>

<ContentList
data={mappedContents}
ListHeaderComponent={renderHeader}
renderItem={renderItem}
contentContainerStyle={{
paddingVertical: theme.spacing(2),
paddingBottom: bottomInset + theme.spacing(2),
gap: theme.spacing(5),
}}
/>
</>
);
};

export default ContentListScreen;

Healthcare Content Use Cases

Patient Education

import { ContentDetail } from "@ovok/native";
import { Composition } from "@medplum/fhirtypes";
import * as React from "react";

const educationComposition: Composition = {
resourceType: "Composition",
title: "Managing Diabetes - Patient Education",
status: "final",
date: "2024-12-20T10:00:00Z",
meta: {
tag: [
{ code: "patient-education", display: "Patient Education" },
{ code: "diabetes", display: "Diabetes" },
],
},
section: [
{
text: {
status: "generated",
div: '<div xmlns="http://www.w3.org/1999/xhtml"><h2>Understanding Diabetes</h2><p>Diabetes is a condition where your blood glucose levels are too high...</p></div>',
},
code: { coding: [{ code: "main-content" }] },
},
{
code: { coding: [{ code: "reading-time" }] },
text: { status: "generated", div: "5 min read" },
},
],
id: "patient-education-diabetes",
};

const PatientEducationExample = () => {
const handleTagPress = (tag: any) => {
console.log("Tag pressed:", tag);
};

return (
<ContentDetail composition={educationComposition}>
<ContentDetail.Header>
<ContentDetail.Title />
<ContentDetail.Metadata>
<ContentDetail.Metadata.Date />
<ContentDetail.Metadata.Separator />
<ContentDetail.Metadata.ReadingTime />
</ContentDetail.Metadata>
</ContentDetail.Header>

<ContentDetail.Body>
<ContentDetail.RichContent />
</ContentDetail.Body>

<ContentDetail.Footer>
<ContentDetail.Tags onTagPress={handleTagPress} />
<ContentDetail.Resource />
</ContentDetail.Footer>
</ContentDetail>
);
};

export default PatientEducationExample;

Clinical Guidelines

import { ContentCard, ContentList } from "@ovok/native";
import * as React from "react";

type Guideline = {
id: string;
title: string;
imageUrl: string;
urgency: "High" | "Medium" | "Low";
};

const clinicalGuidelines = [
{
category: { title: "Cardiology" },
contents: [
{
id: "1",
title: "Hypertension Management Guidelines",
imageUrl: "https://picsum.photos/200/150?random=1",
urgency: "High" as const,
},
{
id: "2",
title: "Heart Failure Treatment Protocol",
imageUrl: "https://picsum.photos/200/150?random=2",
urgency: "Medium" as const,
},
],
},
{
category: { title: "Endocrinology" },
contents: [
{
id: "3",
title: "Diabetes Management Protocol",
imageUrl: "https://picsum.photos/200/150?random=3",
urgency: "High" as const,
},
],
},
];

const ClinicalGuidelinesExample = () => {
const handleViewGuidelines = (category: string) => {
console.log("View all guidelines for:", category);
};

const handleOpenGuideline = (guideline: Guideline) => {
console.log("Open guideline:", guideline.title);
};

return (
<ContentList
data={clinicalGuidelines}
renderItem={({ item }) => (
<ContentList.Section>
<ContentList.Header>
<ContentList.Title>{item.category.title}</ContentList.Title>
<ContentList.ViewAll
onPress={() => handleViewGuidelines(item.category.title)}
/>
</ContentList.Header>

<ContentList.HorizontalList
data={item.contents}
renderItem={({ item: guideline }) => (
<ContentCard onPress={() => handleOpenGuideline(guideline)}>
<ContentCard.Cover
uri={guideline.imageUrl}
badge={guideline.urgency}
/>
<ContentCard.Content>
<ContentCard.Title>{guideline.title}</ContentCard.Title>
</ContentCard.Content>
</ContentCard>
)}
/>
</ContentList.Section>
)}
/>
);
};

export default ClinicalGuidelinesExample;

Medical Articles

import { ContentCard } from "@ovok/native";
import * as React from "react";

type MedicalArticle = {
id: string;
title: string;
imageUrl: string;
isLatest: boolean;
};

const article: MedicalArticle = {
id: "med-article-1",
title: "Latest Advances in Cardiovascular Treatment",
imageUrl: "https://picsum.photos/300/200?random=medical",
isLatest: true,
};

const MedicalArticleExample = () => {
const handleOpenArticle = (article: MedicalArticle) => {
console.log("Opening article:", article.title);
};

return (
<ContentCard onPress={() => handleOpenArticle(article)}>
<ContentCard.Cover
uri={article.imageUrl}
badge={article.isLatest ? "Latest" : undefined}
/>
<ContentCard.Content>
<ContentCard.Title numberOfLines={2}>{article.title}</ContentCard.Title>
</ContentCard.Content>
</ContentCard>
);
};

export default MedicalArticleExample;

Performance Considerations

  • Image Optimization: Automatic image optimization and caching
  • List Performance: Optimized FlatList implementation for large datasets
  • HTML Rendering: Efficient HTML parsing and rendering
  • Memory Management: Automatic cleanup of resources and subscriptions
  • Lazy Loading: Support for lazy loading of images and content
  • Caching: Built-in caching for improved performance

Accessibility

  • Screen Readers: Full VoiceOver and TalkBack support
  • Focus Management: Proper focus order and navigation
  • Touch Targets: Minimum 44pt touch target sizes
  • Color Contrast: High contrast mode support
  • Dynamic Type: Automatic font scaling support
  • Keyboard Navigation: Full keyboard accessibility

Internationalization

  • Localization Support: Built-in i18n integration
  • RTL Support: Right-to-left language support
  • Date Formatting: Locale-aware date formatting
  • Number Formatting: Locale-aware number formatting

Dependencies

  • react-native-paper: Material Design components
  • react-native-render-html: HTML content rendering
  • @medplum/fhirtypes: FHIR TypeScript definitions
  • date-fns: Date formatting utilities
  • react-i18next: Internationalization framework
  • expo-web-browser: External link handling
  • @image/: Image optimization and display
  • @theme/: Theme and styling system
  • @tile/: Alternative card components
  • @patient/: Patient-specific content components