Markdown renderer for React Native powered by marked.js
329
stars
2,209
commits
TypeScript
primary language
Sep 10, 2026
updated
Markdown renderer for React Native powered by marked.js with built-in theming support
yarn add react-native-marked react-native-svg
yarn add react-native-marked@6.0.7 react-native-svg
import * as React from "react";
import Markdown from "react-native-marked";
const ExampleComponent = () => {
return (
<Markdown
value={`# Hello world`}
flatListProps={{
initialNumToRender: 8,
}}
/>
);
};
export default ExampleComponent;
| Prop | Description | Type | Optional? |
|---|---|---|---|
| value | Markdown value | string | false |
| flatListProps | Props for customizing the underlying FlatList used | Omit<FlatListProps<ReactNode>, 'data' | 'renderItem' | 'horizontal'>( 'data', 'renderItem', and 'horizontal' props are omitted and cannot be overridden.) | true |
| styles | Styles for parsed components | MarkedStyles | true |
| theme | Props for customizing colors and spacing for all components,and it will get overridden with custom component style applied via 'styles' prop | UserTheme | true |
| baseUrl | A prefix url for any relative link | string | true |
| renderer | Custom component Renderer | RendererInterface | true |
| hooks | Hooks run during parsing to transform tokens | Marked Hooks | true |
| selectable | Whether Text elements are selectable. Defaults to true. When a custom renderer is provided, this prop is ignored — configure via new Renderer({ selectable }). | boolean | true |
useMarkdown hook will return list of elements that can be rendered using a list component of your choice.
import React, { Fragment } from "react";
import { ScrollView, useColorScheme } from "react-native";
import { useMarkdown, type useMarkdownHookOptions } from "react-native-marked";
const CustomComponent = () => {
const colorScheme = useColorScheme();
const options: useMarkdownHookOptions = {
colorScheme
}
const elements = useMarkdown("# Hello world", options);
return (
<ScrollView>
{elements.map((element, index) => {
return <Fragment key={`demo_${index}`}>{element}</Fragment>
})}
</ScrollView>
);
};
| Option | Description | Type | Optional? |
|---|---|---|---|
| colorScheme | Device color scheme ("dark" or "light") | ColorSchemeName | false |
| styles | Styles for parsed components | MarkedStyles | true |
| theme | Props for customizing colors and spacing for all components,and it will get overridden with custom component style applied via 'styles' prop | UserTheme | true |
| baseUrl | A prefix url for any relative link | string | true |
| renderer | Custom component Renderer | RendererInterface | true |
| tokenizer | Generate custom tokens | MarkedTokenizer | true |
| hooks | Hooks run during parsing to transform tokens | Marked Hooks | true |
| selectable | Whether Text elements are selectable. Defaults to true. When a custom renderer is provided, this option is ignored — configure via new Renderer({ selectable }). | boolean | true |
useMarkdownWithComponents)Ref: CommonMark
HTML will be treated as plain text. Please refer issue#290 for a potential solution
Use code for the block container (ViewStyle) and codeText / codespan for the text (TextStyle) to guarantee fontFamily (and other TextStyle props) are applied:
<Markdown
value={"```js\nconsole.log('hello')\n```\n\nUse `inline` code"}
styles={{
code: { backgroundColor: "#f6f8fa", padding: 16 }, // ViewStyle – container
codeText: { fontFamily: "Menlo", fontSize: 14 }, // TextStyle – code block text
codespan: { fontFamily: "Menlo", backgroundColor: "#f6f8fa" }, // TextStyle – inline
}}
/>
codeText was introduced to replace the previous em-based fallback for code blocks and ensures all TextStyle props win over parent styles.
You can embed React components directly in your markdown using JSX-style syntax. This is useful for adding interactive elements like buttons, custom info boxes, or any other React component.
import React, { Fragment } from "react";
import { Pressable, ScrollView, Text, View } from "react-native";
import {
ReactComponentRegistryProvider,
useMarkdownWithComponents,
type ReactComponentRegistry,
} from "react-native-marked";
// Define your components
const components: ReactComponentRegistry = {
Button: ({ props }) => (
<Pressable onPress={() => console.log("Pressed!")}>
<Text>{String(props.label ?? "Click me")}</Text>
</Pressable>
),
InfoBox: ({ props, children }) => (
<View style={{ backgroundColor: "#E3F2FD", padding: 16 }}>
{props.title && <Text style={{ fontWeight: "bold" }}>{String(props.title)}</Text>}
<Text>{children}</Text>
</View>
),
};
const markdown = `
# Hello World
Click the button below:
<Button label="Get Started" />
<InfoBox title="Note">
This is an info box with **markdown** content.
</InfoBox>
`;
function MarkdownContent() {
const elements = useMarkdownWithComponents(markdown);
return (
<ScrollView>
{elements.map((element, index) => (
<Fragment key={index}>{element}</Fragment>
))}
</ScrollView>
);
}
export default function App() {
return (
<ReactComponentRegistryProvider components={components}>
<MarkdownContent />
</ReactComponentRegistryProvider>
);
}
<ComponentName prop="value" /><ComponentName>content</ComponentName>"value"), number ({42}), and boolean ({true}) propsComponents must be registered via ReactComponentRegistryProvider. Unregistered components are automatically removed from the output.
Custom components can be used to override elements, i.e. Code Highlighting, Fast Image integration
import React, { ReactNode, Fragment } from "react";
import { Text, ScrollView } from "react-native";
import type { ImageStyle, TextStyle } from "react-native";
import Markdown, { Renderer, useMarkdown } from "react-native-marked";
import type { RendererInterface } from "react-native-marked";
import FastImage from "react-native-fast-image";
class CustomRenderer extends Renderer implements RendererInterface {
constructor() {
super();
}
codespan(text: string, _styles?: TextStyle): ReactNode {
return (
<Text key={this.getKey()} style={{ backgroundColor: "#ff0000" }}>
{text}
</Text>
);
}
image(uri: string, _alt?: string, _style?: ImageStyle): ReactNode {
return (
<FastImage
key={this.getKey()}
style={{ width: 200, height: 200 }}
source={{ uri: uri }}
resizeMode={FastImage.resizeMode.contain}
/>
);
}
}
const renderer = new CustomRenderer();
const ExampleComponent = () => {
return (
<Markdown
value={"`Hello world`"}
flatListProps={{
initialNumToRender: 8,
}}
renderer={renderer}
/>
);
};
// Alternate using hook
const ExampleComponentWithHook = () => {
const elements = useMarkdown("`Hello world`", { renderer });
return (
<ScrollView>
{elements.map((element, index) => {
return <Fragment key={`demo_${index}`}>{element}</Fragment>
})}
</ScrollView>
)
}
export default ExampleComponent;
Please refer to RendererInterface for all the overrides
Note:
For
keyproperty for a component, you can use thegetKeymethod from Renderer class.
Overriding default codespan tokenizer to include LaTeX.
import React, { ReactNode } from "react";
import Markdown, { Renderer, MarkedTokenizer, MarkedLexer } from "react-native-marked";
import type { RendererInterface, CustomToken } from "react-native-marked";
class CustomTokenizer extends Tokenizer {
codespan(src: string): Tokens.Codespan | undefined {
const match = src.match(/^\$+([^\$\n]+?)\$+/);
if (match?.[1]) {
return {
type: "codespan",
raw: match[0],
text: match[1].trim(),
};
}
return super.codespan(src);
}
}
class CustomRenderer extends Renderer implements RendererInterface {
codespan(text: string, styles?: TextStyle): ReactNode {
return (
<Text style={styles} key={"key-1"}>
{text}
</Text>
)
}
}
const renderer = new CustomRenderer();
const tokenizer = new CustomTokenizer();
const ExampleComponent = () => {
return (
<Markdown
value={"$ latex code $\n\n` other code `"}
flatListProps={{
initialNumToRender: 8,
}}
renderer={renderer}
tokenizer={tokenizer}
/>
);
};
| Dark Theme | Light Theme |
|---|---|
![]() | ![]() |
See the contributing guide to learn how to contribute to the repository and the development workflow.
MIT
Made with create-react-native-library
TypeScript
99.6%
Markdown renderer for React Native powered by marked.js
329
stars
2,209
commits
TypeScript
primary language
Sep 10, 2026
updated
Markdown renderer for React Native powered by marked.js with built-in theming support
yarn add react-native-marked react-native-svg
yarn add react-native-marked@6.0.7 react-native-svg
import * as React from "react";
import Markdown from "react-native-marked";
const ExampleComponent = () => {
return (
<Markdown
value={`# Hello world`}
flatListProps={{
initialNumToRender: 8,
}}
/>
);
};
export default ExampleComponent;
| Prop | Description | Type | Optional? |
|---|---|---|---|
| value | Markdown value | string | false |
| flatListProps | Props for customizing the underlying FlatList used | Omit<FlatListProps<ReactNode>, 'data' | 'renderItem' | 'horizontal'>( 'data', 'renderItem', and 'horizontal' props are omitted and cannot be overridden.) | true |
| styles | Styles for parsed components | MarkedStyles | true |
| theme | Props for customizing colors and spacing for all components,and it will get overridden with custom component style applied via 'styles' prop | UserTheme | true |
| baseUrl | A prefix url for any relative link | string | true |
| renderer | Custom component Renderer | RendererInterface | true |
| hooks | Hooks run during parsing to transform tokens | Marked Hooks | true |
| selectable | Whether Text elements are selectable. Defaults to true. When a custom renderer is provided, this prop is ignored — configure via new Renderer({ selectable }). | boolean | true |
useMarkdown hook will return list of elements that can be rendered using a list component of your choice.
import React, { Fragment } from "react";
import { ScrollView, useColorScheme } from "react-native";
import { useMarkdown, type useMarkdownHookOptions } from "react-native-marked";
const CustomComponent = () => {
const colorScheme = useColorScheme();
const options: useMarkdownHookOptions = {
colorScheme
}
const elements = useMarkdown("# Hello world", options);
return (
<ScrollView>
{elements.map((element, index) => {
return <Fragment key={`demo_${index}`}>{element}</Fragment>
})}
</ScrollView>
);
};
| Option | Description | Type | Optional? |
|---|---|---|---|
| colorScheme | Device color scheme ("dark" or "light") | ColorSchemeName | false |
| styles | Styles for parsed components | MarkedStyles | true |
| theme | Props for customizing colors and spacing for all components,and it will get overridden with custom component style applied via 'styles' prop | UserTheme | true |
| baseUrl | A prefix url for any relative link | string | true |
| renderer | Custom component Renderer | RendererInterface | true |
| tokenizer | Generate custom tokens | MarkedTokenizer | true |
| hooks | Hooks run during parsing to transform tokens | Marked Hooks | true |
| selectable | Whether Text elements are selectable. Defaults to true. When a custom renderer is provided, this option is ignored — configure via new Renderer({ selectable }). | boolean | true |
useMarkdownWithComponents)Ref: CommonMark
HTML will be treated as plain text. Please refer issue#290 for a potential solution
Use code for the block container (ViewStyle) and codeText / codespan for the text (TextStyle) to guarantee fontFamily (and other TextStyle props) are applied:
<Markdown
value={"```js\nconsole.log('hello')\n```\n\nUse `inline` code"}
styles={{
code: { backgroundColor: "#f6f8fa", padding: 16 }, // ViewStyle – container
codeText: { fontFamily: "Menlo", fontSize: 14 }, // TextStyle – code block text
codespan: { fontFamily: "Menlo", backgroundColor: "#f6f8fa" }, // TextStyle – inline
}}
/>
codeText was introduced to replace the previous em-based fallback for code blocks and ensures all TextStyle props win over parent styles.
You can embed React components directly in your markdown using JSX-style syntax. This is useful for adding interactive elements like buttons, custom info boxes, or any other React component.
import React, { Fragment } from "react";
import { Pressable, ScrollView, Text, View } from "react-native";
import {
ReactComponentRegistryProvider,
useMarkdownWithComponents,
type ReactComponentRegistry,
} from "react-native-marked";
// Define your components
const components: ReactComponentRegistry = {
Button: ({ props }) => (
<Pressable onPress={() => console.log("Pressed!")}>
<Text>{String(props.label ?? "Click me")}</Text>
</Pressable>
),
InfoBox: ({ props, children }) => (
<View style={{ backgroundColor: "#E3F2FD", padding: 16 }}>
{props.title && <Text style={{ fontWeight: "bold" }}>{String(props.title)}</Text>}
<Text>{children}</Text>
</View>
),
};
const markdown = `
# Hello World
Click the button below:
<Button label="Get Started" />
<InfoBox title="Note">
This is an info box with **markdown** content.
</InfoBox>
`;
function MarkdownContent() {
const elements = useMarkdownWithComponents(markdown);
return (
<ScrollView>
{elements.map((element, index) => (
<Fragment key={index}>{element}</Fragment>
))}
</ScrollView>
);
}
export default function App() {
return (
<ReactComponentRegistryProvider components={components}>
<MarkdownContent />
</ReactComponentRegistryProvider>
);
}
<ComponentName prop="value" /><ComponentName>content</ComponentName>"value"), number ({42}), and boolean ({true}) propsComponents must be registered via ReactComponentRegistryProvider. Unregistered components are automatically removed from the output.
Custom components can be used to override elements, i.e. Code Highlighting, Fast Image integration
import React, { ReactNode, Fragment } from "react";
import { Text, ScrollView } from "react-native";
import type { ImageStyle, TextStyle } from "react-native";
import Markdown, { Renderer, useMarkdown } from "react-native-marked";
import type { RendererInterface } from "react-native-marked";
import FastImage from "react-native-fast-image";
class CustomRenderer extends Renderer implements RendererInterface {
constructor() {
super();
}
codespan(text: string, _styles?: TextStyle): ReactNode {
return (
<Text key={this.getKey()} style={{ backgroundColor: "#ff0000" }}>
{text}
</Text>
);
}
image(uri: string, _alt?: string, _style?: ImageStyle): ReactNode {
return (
<FastImage
key={this.getKey()}
style={{ width: 200, height: 200 }}
source={{ uri: uri }}
resizeMode={FastImage.resizeMode.contain}
/>
);
}
}
const renderer = new CustomRenderer();
const ExampleComponent = () => {
return (
<Markdown
value={"`Hello world`"}
flatListProps={{
initialNumToRender: 8,
}}
renderer={renderer}
/>
);
};
// Alternate using hook
const ExampleComponentWithHook = () => {
const elements = useMarkdown("`Hello world`", { renderer });
return (
<ScrollView>
{elements.map((element, index) => {
return <Fragment key={`demo_${index}`}>{element}</Fragment>
})}
</ScrollView>
)
}
export default ExampleComponent;
Please refer to RendererInterface for all the overrides
Note:
For
keyproperty for a component, you can use thegetKeymethod from Renderer class.
Overriding default codespan tokenizer to include LaTeX.
import React, { ReactNode } from "react";
import Markdown, { Renderer, MarkedTokenizer, MarkedLexer } from "react-native-marked";
import type { RendererInterface, CustomToken } from "react-native-marked";
class CustomTokenizer extends Tokenizer {
codespan(src: string): Tokens.Codespan | undefined {
const match = src.match(/^\$+([^\$\n]+?)\$+/);
if (match?.[1]) {
return {
type: "codespan",
raw: match[0],
text: match[1].trim(),
};
}
return super.codespan(src);
}
}
class CustomRenderer extends Renderer implements RendererInterface {
codespan(text: string, styles?: TextStyle): ReactNode {
return (
<Text style={styles} key={"key-1"}>
{text}
</Text>
)
}
}
const renderer = new CustomRenderer();
const tokenizer = new CustomTokenizer();
const ExampleComponent = () => {
return (
<Markdown
value={"$ latex code $\n\n` other code `"}
flatListProps={{
initialNumToRender: 8,
}}
renderer={renderer}
tokenizer={tokenizer}
/>
);
};
| Dark Theme | Light Theme |
|---|---|
![]() | ![]() |
See the contributing guide to learn how to contribute to the repository and the development workflow.
MIT
Made with create-react-native-library
TypeScript
99.6%