Skip to content

Instantly share code, notes, and snippets.

@danieldunderfelt
Created July 10, 2019 05:49
Show Gist options
  • Select an option

  • Save danieldunderfelt/1982786761cf4156b732b3a128a8050f to your computer and use it in GitHub Desktop.

Select an option

Save danieldunderfelt/1982786761cf4156b732b3a128a8050f to your computer and use it in GitHub Desktop.
MDX in React-native
// The actual components that will be rendered with markdown. You may need to change or add components,
// this list is not fully tested. Some components may also never be used by MDX.
// Copied from https://github.com/mientjan/react-native-markdown-renderer and heavily modified.
import React from 'react'
import { Text, TouchableOpacity, View } from 'react-native'
import FitImage from 'react-native-fit-image'
import openUrl from './openUrl'
import { styles } from './styles'
const components = {
div: ({ children }) => <View style={styles.div}>{children}</View>,
wrapper: ({ children }) => <View style={styles.div}>{children}</View>,
textgroup: ({ children }) => {
return <Text style={styles.text}>{children}</Text>
},
inline: ({ children }) => {
return <Text>{children}</Text>
},
text: ({ children }) => {
return <Text>{children}</Text>
},
span: ({ children }) => {
return <Text>{children}</Text>
},
strong: ({ children }) => {
return <Text style={styles.strong}>{children}</Text>
},
a: ({ href, children }) => {
return (
<TouchableOpacity style={styles.link} onPress={() => openUrl(href)}>
{children}
</TouchableOpacity>
)
},
em: ({ children }) => {
return <Text style={styles.em}>{children}</Text>
},
h1: ({ children }) => {
return (
<View style={styles.headingContainer}>
<Text style={[styles.heading, styles.heading1]}>
{children}
</Text>
</View>
)
},
h2: ({ children }) => {
return (
<View style={styles.headingContainer}>
<Text style={[styles.heading, styles.heading2]}>
{children}
</Text>
</View>
)
},
h3: ({ children }) => (
<View style={styles.headingContainer}>
<Text style={[styles.heading, styles.heading3]}>
{children}
</Text>
</View>
),
h4: ({ children }) => (
<View style={styles.headingContainer}>
<Text style={[styles.heading, styles.heading4]}>
{children}
</Text>
</View>
),
h5: ({ children }) => (
<View style={styles.headingContainer}>
<Text style={[styles.heading, styles.heading5]}>
{children}
</Text>
</View>
),
h6: ({ children }) => (
<View style={styles.headingContainer}>
<Text style={[styles.heading, styles.heading6]}>
{children}
</Text>
</View>
),
p: ({ children }) => <View style={styles.paragraph}>{children}</View>,
blockquote: ({ children }) => <View style={styles.blockquote}>{children}</View>,
inlineCode: ({ children }) => {
return <Text style={styles.codeInline}>{children}</Text>
},
code: ({ children }) => {
return <Text style={styles.codeBlock}>{children}</Text>
},
pre: ({ children }) => <View style={styles.pre}>{children}</View>,
ul: ({ children }) => {
return <View style={[styles.list, styles.listUnordered]}>{children}</View>
},
ol: ({ children }) => {
return <View style={[styles.list, styles.listOrdered]}>{children}</View>
},
li: ({ children }) => {
return (
<View style={styles.listUnorderedItem}>
<Text style={styles.listUnorderedItemIcon}>{'\u00B7'}</Text>
<View style={[styles.listItem]}>{children}</View>
</View>
)
},
table: ({ children }) => <View style={[styles.table]}>{children}</View>,
thead: ({ children }) => <View style={[styles.tableHeader]}>{children}</View>,
tbody: ({ children }) => <View>{children}</View>,
th: ({ children }) => {
return <View style={[styles.tableHeaderCell]}>{children}</View>
},
tr: ({ children }) => {
return <View style={[styles.tableRow]}>{children}</View>
},
td: ({ children }) => {
return <View style={[styles.tableRowCell]}>{children}</View>
},
hr: ({ children }) => {
return <View style={[styles.hr]} />
},
br: ({ children }) => <Text>{'\n'}</Text>,
img: ({ src, children }) => {
return <FitImage indicator={true} style={styles.image} source={{ uri: src }} />
},
}
export default components
// Use your MDX content with this component.
import React from 'react'
import MDX from '@mdx-js/runtime'
import components from '../utils/markdown/markdown'
// Renders a cimple loading spinner as a test
import Loading from './Loading'
const mdxComponents = {
...components,
Loading: Loading, // Add the custom component to the default markdown HTML components
}
// Add variables here if needed.
const scope = {}
// Children is an MDX string
const MdxContent = ({ children, style = {} }) => {
return (
<View style={style}>
<MDX components={mdxComponents} scope={scope}>
{children}
</MDX>
</View>
)
}
export default MdxContent
// To open URLs from Markdown links. Modify as needed. Untested.
// Copied from https://github.com/mientjan/react-native-markdown-renderer
import { Linking } from 'react-native';
export default function openUrl(url) {
if( url ) {
Linking.openURL(url);
}
}
// Add this to the root of your project.
// Adds Node libs that are needed, plus an fs implementation.
// mdx-runtime needs these.
// Yes, this works with Expo.
const libs = require('node-libs-react-native')
libs['fs'] = require.resolve('react-native-level-fs')
module.exports = {
resolver: {
extraNodeModules: libs,
},
}
// Styles used for the components. Change to your liking. Some styles may
// be superfluous or not working, this list is not tested.
// Copied from https://github.com/mientjan/react-native-markdown-renderer and modified slightly.
import { StyleSheet } from 'react-native'
export const styles = StyleSheet.create({
root: {},
view: {},
codeBlock: {
borderWidth: 1,
borderColor: '#CCCCCC',
backgroundColor: '#f5f5f5',
padding: 10,
borderRadius: 4,
},
codeInline: {
borderWidth: 1,
borderColor: '#CCCCCC',
backgroundColor: '#f5f5f5',
padding: 10,
borderRadius: 4,
},
del: {
backgroundColor: '#000000',
},
em: {
fontStyle: 'italic',
},
headingContainer: {
flexDirection: 'row',
},
heading: {},
heading1: {
fontSize: 32,
},
heading2: {
fontSize: 24,
},
heading3: {
fontSize: 18,
},
heading4: {
fontSize: 16,
},
heading5: {
fontSize: 13,
},
heading6: {
fontSize: 11,
},
hr: {
backgroundColor: '#000000',
height: 1,
},
blockquote: {
paddingHorizontal: 20,
paddingVertical: 10,
margin: 20,
backgroundColor: '#CCCCCC',
},
inlineCode: {
borderRadius: 3,
borderWidth: 1,
fontFamily: 'Courier',
fontWeight: 'bold',
},
list: {},
listItem: {
flex: 1,
flexWrap: 'wrap',
// backgroundColor: 'green',
},
listUnordered: {},
listUnorderedItem: {
flexDirection: 'row',
justifyContent: 'flex-start',
},
listUnorderedItemIcon: {
marginLeft: 10,
marginRight: 10,
lineHeight: 30,
},
listUnorderedItemText: {
fontSize: 20,
lineHeight: 20,
},
listOrdered: {},
listOrderedItem: {
flexDirection: 'row',
},
listOrderedItemIcon: {
marginLeft: 10,
marginRight: 10,
lineHeight: 30,
},
listOrderedItemText: {
fontWeight: 'bold',
lineHeight: 20,
},
div: {},
paragraph: {
marginTop: 10,
marginBottom: 10,
flexWrap: 'wrap',
flexDirection: 'row',
alignItems: 'flex-start',
justifyContent: 'flex-start',
},
hardbreak: {
width: '100%',
height: 1,
},
strong: {
fontWeight: 'bold',
},
table: {
borderWidth: 1,
borderColor: '#000000',
borderRadius: 3,
},
tableHeader: {},
tableHeaderCell: {
flex: 1,
// color: '#000000',
padding: 5,
// backgroundColor: 'green',
},
tableRow: {
borderBottomWidth: 1,
borderColor: '#000000',
flexDirection: 'row',
},
tableRowCell: {
flex: 1,
padding: 5,
},
text: {},
strikethrough: {
textDecorationLine: 'line-through',
},
pre: {},
link: {},
image: {
flex: 1,
},
})
@slorber
Copy link
Copy Markdown

slorber commented Feb 1, 2020

thanks, useful to me :)

note your

does not wrap in which leads to RN errors

@danieldunderfelt
Copy link
Copy Markdown
Author

@slorber thanks! Yeah, I've made multiple fixes since this gist. It's only a starting point.

@slorber
Copy link
Copy Markdown

slorber commented Feb 2, 2020

Sure, also got the same problem with li

Also wondering how did you do to make images work. The src is just a string while for RN we should not have "src": "./pathToImage" but instead "src": require("./pathToImage");

Could you share more details about what you figured out since this gist? (I'm available on Twitter DM to chat if you want to)

@danieldunderfelt
Copy link
Copy Markdown
Author

I actually haven't tried images yet! 😅 Thanks for the heads up. I can update this gist with the newest version of my MDX code, it's coming along rather nicely. I'm also maybe going to release it as a library when I'm done.

It might be better to pre-process the MDX instead of using the runtime though. Will have to investigate.

@slorber
Copy link
Copy Markdown

slorber commented Feb 3, 2020

I'm actually running MDX AOT in a build step. Will write about it when everything will be more production ready but we can chat if you want

@andrekovac
Copy link
Copy Markdown

@danieldunderfelt if you still have plans to update this gist or create a lib, I'd be very interested to try it! Great initiative!

@slorber
Copy link
Copy Markdown

slorber commented Aug 28, 2020

Hey,

I'm running MDX on my website/mobile app, and it will be the subject of my RN EU talk the 4th September.

I think I'll try to work on a Metro transformer to provide the compilation automatically, like require("myBlogPost.mdx"), as it's more convenient than embedding the mdx compilation at runtime or having a build step, but nothing published yet and the Metro transformer API is not very easy to understand :D

@farcaller
Copy link
Copy Markdown

Have anyone faced node_modules/@babel/core/lib/config/files/import.js: node_modules/@babel/core/lib/config/files/import.js:Invalid call at line 9: import(filepath)? Any idea what to do about it?

@enstulen
Copy link
Copy Markdown

Have anyone faced node_modules/@babel/core/lib/config/files/import.js: node_modules/@babel/core/lib/config/files/import.js:Invalid call at line 9: import(filepath)? Any idea what to do about it?

I get the same issue, did you figure it out?

@farcaller
Copy link
Copy Markdown

I get the same issue, did you figure it out?

Yeah, I ditched MDX and wrote my own markdown parser and renderer. I don't not suggest to try that :-)

@danieldunderfelt
Copy link
Copy Markdown
Author

Apologies for not responding to comments here, I just haven't noticed the activity. Github notifications are an absolute mess.

@danieldunderfelt
Copy link
Copy Markdown
Author

@andrekovac I have been thinking about it! The app where this is used has been on ice for a while, but I'm continuing with it soon and I want to develop a great way to handle content within it. So yeah, something will probably come of this yet!

@danieldunderfelt
Copy link
Copy Markdown
Author

@slorber that would be ideal! I will continue developing an MDX solution for my needs soon. I'm also looking at https://github.com/kentcdodds/mdx-bundler, it might be useful.

@slorber
Copy link
Copy Markdown

slorber commented May 18, 2021

Didn't work on this much this year but my talk on MDX + RN is here: https://www.youtube.com/watch?v=ScgFQojbAAc
The 2nd part is dedicated to running MDX in RN

@danieldunderfelt
Copy link
Copy Markdown
Author

Didn't work on this much this year but my talk on MDX + RN is here: https://www.youtube.com/watch?v=ScgFQojbAAc
The 2nd part is dedicated to running MDX in RN

Great, thanks! I'll check it out.

@MaganAnkur
Copy link
Copy Markdown

Screen Shot 2021-10-07 at 10 07 09 PM

Any idea how to resolve this, I already installed mdx-js

@MaganAnkur
Copy link
Copy Markdown

I fixed above issue by upgrading @mdx-js/runtime to 2.0.0-next.9

@danieldunderfelt
Copy link
Copy Markdown
Author

@MaganAnkur Yes, that's what you needed to do. I've also made a library, check it out and give feedback: https://www.npmjs.com/package/rn-mdx

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment