Open thib92 opened 5 years ago
cc @sbezludny @cribbles
Since the react-renderer is flexible enough I think we can have a package that just exports a renderer option to add the rich-text-react-renderer
Example:
import { documentToReactComponents } from '@contentful/rich-text-react-renderer';
import { reactNativeRenderer } from '@contentful/rich-text-react-renderer';
documentToReactComponents(document, reactNativeRenderer)
@thib92 So would an <h1/>
translate to a <Text style={{ fontSize: CONTENTFUL_DEFAULT_H1_FONT_SIZE }}/>
?
@henrymoulton you can always override any default config
For those of you who came here looking for a solution, try looking in react-native-render-html.
Since the react-renderer is flexible enough I think we can have a package that just exports a renderer option to add the
rich-text-react-renderer
Example:
import { documentToReactComponents } from '@contentful/rich-text-react-renderer'; import { reactNativeRenderer } from '@contentful/rich-text-react-renderer'; documentToReactComponents(document, reactNativeRenderer)
@Khaledgarbaya Upon doing this:
documentToReactComponents(item, reactNativeRenderer)
// OR
documentToReactComponents(item.fields, reactNativeRenderer)
I'm getting:
undefined is not an object (evaluating 'nodes.map') - node_modules\@contentful\rich-text-react-renderer\dist\rich-text-react-renderer.es5.js:878:9 in nodeListToReactComponents
With this:
documentToReactComponents(item.fields.body, reactNativeRenderer)
// OR
<Text>{documentToReactComponents(item.fields.body, reactNativeRenderer)}</Text>
I'm getting:
Text strings must be rendered within a
component. - node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:4137:14 in - node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:4134:2 in createTextInstance
- node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:15909:12 in completeWork
For those of you who came here looking for a solution, try looking in react-native-render-html.
@IsaacHardy Could you give a sample mock-up? For example how you can render a Contentful entry item.fields.body
with react-native-render-html
?
By the way, I'm using Expo.
@5ervant you need to pass the content of the rich-text field
@Khaledgarbaya item.fields.body
is the rich text content.
@Khaledgarbaya Anyway, reactNativeRenderer
is undefined
.
import { reactNativeRenderer } from '@contentful/rich-text-react-renderer';
console.log('reactNativeRenderer', reactNativeRenderer); // reactNativeRenderer == undefined
Sorry for the confusion here yes it is undefined because it does not exist
I was just giving a suggestion
Yes, a confusion because this issue is more like a feature request.
My found solution is to use ReactDOMServer
and such a react-native-webview
library:
import ReactDOMServer from 'react-dom/server';
import { WebView } from 'react-native-webview';
// ...
<WebView
originWhitelist={['*']}
source={{
html: ReactDOMServer.renderToStaticMarkup(ContentfulRichTextForWeb(item.fields.body))
}}
/>
Yes, a confusion because this issue is more like a feature request.
My found solution is to use
ReactDOMServer
and such areact-native-webview
library:import ReactDOMServer from 'react-dom/server'; import { WebView } from 'react-native-webview'; // ... <WebView originWhitelist={['*']} source={{ html: ReactDOMServer.renderToStaticMarkup(ContentfulRichTextForWeb(item.fields.body)) }} />
Interesting, where is ContentfulRichTextForWeb
generated?
For anyone looking for a solution, you can use this as a starting point. Your use case will be different based on where your using it, but for most people it's enough to get started. I've edited the code inside the github comment box so if there is errors or formatting issues sorry about that.
const contentfulToReactnative = {
renderMark: {
[MARKS.UNDERLINE]: (text) => {
return text;
},
[MARKS.BOLD]: (text) => {
return <Text>{text}</Text>;
},
[MARKS.ITALIC]: (text) => {
return <Text>{text}</Text>;
},
[MARKS.CODE]: (text) => {
return <Text>{text}</Text>;
},
},
renderNode: {
[INLINES.HYPERLINK]: (node) => {
return null;
},
[BLOCKS.EMBEDDED_ENTRY]: (node) => {
return null;
},
[BLOCKS.PARAGRAPH]: (_node, children) => {
return <Text>{children}</Text>;
},
[BLOCKS.EMBEDDED_ASSET]: (node) => {
return null;
},
[BLOCKS.HEADING_1]: (_node, children) => <Text>{children}</Text>,
[BLOCKS.HEADING_2]: (_node, children) => <Text>{children}</Text>,
[BLOCKS.HEADING_3]: (_node, children) => <Text>{children}</Text>,
[BLOCKS.HEADING_4]: (_node, children) => <Text>{children}</Text>,
[BLOCKS.HEADING_5]: (_node, children) => <Text>{children}</Text>,
[BLOCKS.HEADING_6]: (_node, children) => <Text>{children}</Text>,
[BLOCKS.UL_LIST]: (_node, children) => {
return (
<View>
{children.map((child, i) => {
return child;
})}
</View>
);
},
[BLOCKS.OL_LIST]: (_node, children) => {
return children.map((child, i) => {
return child;
});
},
[BLOCKS.LIST_ITEM]: (_node, child) => {
return <View>{child}</View>;
},
[BLOCKS.QUOTE]: (_node, child) => {
return <Text>{child}</Text>;
},
[BLOCKS.HR]: (_node, child) => {
return <Text>{child}</Text>;
},
},
};
And then you can do this:
import { documentToReactComponents } from '@contentful/rich-text-react-renderer'
documentToReactComponents(objFromContentful, contentfulToReactnative)
Like a miracle this
Since the react-renderer is flexible enough I think we can have a package that just exports a renderer option to add the
rich-text-react-renderer
Example:
import { documentToReactComponents } from '@contentful/rich-text-react-renderer'; import { reactNativeRenderer } from '@contentful/rich-text-react-renderer'; documentToReactComponents(document, reactNativeRenderer)
Like a miracle I actually got this working by:
A) transforming response using https://www.contentful.com/developers/docs/javascript/tutorials/rendering-contentful-rich-text-with-javascript/
import {documentToHtmlString} from '@contentful/rich-text-html-renderer';
import {Document} from '@contentful/rich-text-types';
import getContentfulEntries, {
ContentfulLocale,
} from '../../lib/contentful/getContentfulEntries';
import {IFood, IFoodContentful} from './IFood';
export const GetFoodItems = async (
locale?: string,
): Promise<IFood[] | undefined> => {
try {
const response = await getContentfulEntries<IFoodContentful>({
entryType: 'food',
locale: locale || ContentfulLocale,
});
return response.items.map((i) => {
const html = documentToHtmlString(i.fields.description as Document);
console.log('FOOD as HTML', html);
const item: IFood = {
...i.fields,
id: i.sys.id,
category: i.fields.category.sys.id,
description: html,
};
return item;
});
} catch (error) {
console.warn(error.message);
return undefined;
}
};
B) Rendering HTML using https://github.com/archriss/react-native-render-html
import {StatusBar, useWindowDimensions, View} from 'react-native';
import HTML from 'react-native-render-html';
...
export const MenuScreen: React.FC = () => {
const menuContext = React.useContext(MenuContext);
const contentWidth = useWindowDimensions().width;
return (
<HTML
source={{html: f.description}}
contentWidth={contentWidth}
/>);
Any updates on this? Doesn't seem like there's support for RN, and the best solution is to render HTML and use react-native-render-html. Any plans on adding first party support for this?
@smadan - Have you tried my solution? I've found it works really well, it's not as flexible as a web based solution, but it's still suitable.
I ended up using @thomashagstrom's solution.
@neil-gebbie-smarterley thanks for your help :), works fine for me
@christophsaile No problems. Glad I could help.
For anyone looking for a solution, you can use this as a starting point. Your use case will be different based on where your using it, but for most people it's enough to get started. I've edited the code inside the github comment box so if there is errors or formatting issues sorry about that.
const contentfulToReactnative = { renderMark: { [MARKS.UNDERLINE]: (text) => { return text; }, [MARKS.BOLD]: (text) => { return <Text>{text}</Text>; }, [MARKS.ITALIC]: (text) => { return <Text>{text}</Text>; }, [MARKS.CODE]: (text) => { return <Text>{text}</Text>; }, }, renderNode: { [INLINES.HYPERLINK]: (node) => { return null; }, [BLOCKS.EMBEDDED_ENTRY]: (node) => { return null; }, [BLOCKS.PARAGRAPH]: (_node, children) => { return <Text>{children}</Text>; }, [BLOCKS.EMBEDDED_ASSET]: (node) => { return null; }, [BLOCKS.HEADING_1]: (_node, children) => <Text>{children}</Text>, [BLOCKS.HEADING_2]: (_node, children) => <Text>{children}</Text>, [BLOCKS.HEADING_3]: (_node, children) => <Text>{children}</Text>, [BLOCKS.HEADING_4]: (_node, children) => <Text>{children}</Text>, [BLOCKS.HEADING_5]: (_node, children) => <Text>{children}</Text>, [BLOCKS.HEADING_6]: (_node, children) => <Text>{children}</Text>, [BLOCKS.UL_LIST]: (_node, children) => { return ( <View> {children.map((child, i) => { return child; })} </View> ); }, [BLOCKS.OL_LIST]: (_node, children) => { return children.map((child, i) => { return child; }); }, [BLOCKS.LIST_ITEM]: (_node, child) => { return <View>{child}</View>; }, [BLOCKS.QUOTE]: (_node, child) => { return <Text>{child}</Text>; }, [BLOCKS.HR]: (_node, child) => { return <Text>{child}</Text>; }, }, };
And then you can do this:
import { documentToReactComponents } from '@contentful/rich-text-react-renderer' documentToReactComponents(objFromContentful, contentfulToReactnative)
not working for me, I tried this documentToReactComponents(content?.fields?.html, contentfulToReactnative)
And when you console.log(content?.fields?.html) what is it showing? What content type is the field?
@avillarubia ^^^
@neil-gebbie-smarterley thank you for your response, this is the sample data, of content
https://codesandbox.io/s/contentful-content-k75jk6?file=/src/data.json
fields has no type as shown in the image below
html is document in nodeType
Thanks to
rich-text-react-render
, we can render Contentful rich text inside a React app. Moreover, some defaults are provided for DOM rendering (using tags like<p>
,<h1>
and so on).However, it is not possible to render these tags in a React Native app, as React Native uses custom components, not DOM elements (for example
<Text>
for text).Fortunately,
rich-text-react-renderer
makes possible to override the default components and allows to provide a custom renderer for each type of content.Then, it is up to the user to provide their own React Native renderer.
Do you think it could be a good idea to provide a default renderer for React Native as well?
People might override it to use their own components for easier styling, but this could make it easier to get started using Contentful with React Native.