-
-
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