-
-
Save danieldunderfelt/1982786761cf4156b732b3a128a8050f to your computer and use it in GitHub Desktop.
// 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 thanks! Yeah, I've made multiple fixes since this gist. It's only a starting point.
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)
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.
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
@danieldunderfelt if you still have plans to update this gist or create a lib, I'd be very interested to try it! Great initiative!
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
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?
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?
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 :-)
Apologies for not responding to comments here, I just haven't noticed the activity. Github notifications are an absolute mess.
@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!
@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.
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
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.
I fixed above issue by upgrading @mdx-js/runtime to 2.0.0-next.9
@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
thanks, useful to me :)
note your
does not wrap in which leads to RN errors