# Structured text
`` is a React component that you can use to render the value contained inside a DatoCMS [Structured Text field type](https://www.datocms.com/docs/structured-text/dast).
## Table of Contents
- [Installation](#installation)
- [Basic usage](#basic-usage)
- [Custom renderers for inline records, blocks, and links](#custom-renderers-for-inline-records-blocks-and-links)
- [Override default rendering of nodes](#override-default-rendering-of-nodes)
- [Props](#props)
## Installation
```
npm install --save react-datocms
```
## Basic usage
```js
import React from 'react';
import { StructuredText } from 'react-datocms';
const Page = ({ data }) => {
// data.blogPost.content = {
// value: {
// schema: "dast",
// document: {
// type: "root",
// children: [
// {
// type: "heading",
// level: 1,
// children: [
// {
// type: "span",
// value: "Hello ",
// },
// {
// type: "span",
// marks: ["strong"],
// value: "world!",
// },
// ],
// },
// ],
// },
// },
// }
return (
{data.blogPost.title}
{/* -> Hello world!
*/}
);
};
const query = gql`
query {
blogPost {
title
content {
value
}
}
}
`;
export default withQuery(query)(Page);
```
## Custom renderers for inline records, blocks, and links
You can also pass custom renderers for special nodes (inline records, record links and blocks) as an optional parameter like so:
```js
import React from 'react';
import { StructuredText, Image } from 'react-datocms';
const Page = ({ data }) => {
// data.blogPost.content ->
// {
// value: {
// schema: "dast",
// document: {
// type: "root",
// children: [
// {
// type: "heading",
// level: 1,
// children: [
// { type: "span", value: "Welcome onboard " },
// { type: "inlineItem", item: "324321" },
// ],
// },
// {
// type: "paragraph",
// children: [
// { type: "span", value: "So happy to have " },
// {
// type: "itemLink",
// item: "324321",
// children: [
// {
// type: "span",
// marks: ["strong"],
// value: "this awesome humang being",
// },
// ]
// },
// { type: "span", value: " in our team! We call him " },
// { type: "inlineBlock", item: "1984560" }
// ]
// },
// { type: "block", item: "1984559" }
// ],
// },
// },
// links: [
// {
// id: "324321",
// __typename: "TeamMemberRecord",
// firstName: "Mark",
// slug: "mark-smith",
// },
// ],
// blocks: [
// {
// id: "1984559",
// __typename: "CtaRecord",
// title: "Call to action",
// url: "https://google.com"
// },
// ],
// inlineBlocks: [
// {
// id: "1984560",
// __typename: "MentionRecord",
// username: "steffoz",
// },
// ],
// }
return (
{data.blogPost.title}
{
switch (record.__typename) {
case 'TeamMemberRecord':
return {record.firstName};
default:
return null;
}
}}
renderLinkToRecord={({ record, children, transformedMeta }) => {
switch (record.__typename) {
case 'TeamMemberRecord':
return (
{children}
);
default:
return null;
}
}}
renderBlock={({ record }) => {
switch (record.__typename) {
case 'CtaRecord':
return (
{record.title}
);
default:
return null;
}
}}
renderInlineBlock={({ record }) => {
switch (record.__typename) {
case 'MentionRecord':
return (
@{record.username}
);
default:
return null;
}
}}
/>
{/*
Final result:
Welcome onboard Mark
So happy to have this awesome humang being in our team! We call him @steffoz
*/}
);
};
const query = gql`
query {
blogPost {
title
content {
value
links {
... on RecordInterface {
id
__typename
}
... on TeamMemberRecord {
firstName
slug
}
}
blocks {
... on RecordInterface {
id
__typename
}
... on CtaRecord {
title
url
}
}
inlineBlocks {
... on RecordInterface {
id
__typename
}
... on MentionRecord {
username
}
}
}
}
}
`;
export default withQuery(query)(Page);
```
## Override default rendering of nodes
This component automatically renders all nodes (except for `inlineItem`, `itemLink`, `block` and `inlineBlock`) using a set of default rules, but you might want to customize those. For example:
For example:
- For `heading` nodes, you might want to add an anchor;
- For `code` nodes, you might want to use a custom sytax highlighting component like [`prism-react-renderer`](https://github.com/FormidableLabs/prism-react-renderer);
- Apply different logic/formatting to a node based on what its parent node is (using the `ancestors` parameter)
- For all possible node types, refer to the [list of typeguard functions defined in the main `structured-text` package](https://github.com/datocms/structured-text/tree/main/packages/utils#typescript-type-guards). The [DAST format documentation](https://www.datocms.com/docs/structured-text/dast) has additional details.
In this case, you can easily override default rendering rules with the `customNodeRules` and `customMarkRules` props.
```jsx
import { renderNodeRule, renderMarkRule, StructuredText } from 'react-datocms';
import { isHeading, isCode } from 'datocms-structured-text-utils';
import { render as toPlainText } from 'datocms-structured-text-to-plain-text';
import SyntaxHighlight from 'components/SyntaxHighlight';
{
const HeadingTag = `h${node.level}`;
const anchor = toPlainText(node)
.toLowerCase()
.replace(/ /g, '-')
.replace(/[^\w-]+/g, '');
return (
{children}
);
}),
// Use a custom syntax highlighter component for code blocks
renderNodeRule(isCode, ({ node, key }) => {
return (
);
}),
// Apply different formatting to top-level paragraphs
renderNodeRule(
isParagraph,
({ adapter: { renderNode }, node, children, key, ancestors }) => {
if (isRoot(ancestors[0])) {
// If this paragraph node is a top-level one, give it a special class
return renderNode(
'p',
{ key, className: 'top-level-paragraph-container-example' },
children,
);
} else {
// Proceed with default paragraph rendering...
// return renderNode('p', { key }, children);
// Or even completely remove the paragraph and directly render the inner children:
return {children};
}
},
),
]}
customMarkRules={[
// convert "strong" marks into tags
renderMarkRule('strong', ({ mark, children, key }) => {
return {children};
}),
]}
/>;
```
Note: if you override the rules for `inlineItem`, `itemLink`, `block` or `inlineBlock` nodes, then the `renderInlineRecord`, `renderLinkToRecord`, `renderBlock` and `renderInlineBlock` props won't be considered!
## Props
| prop | type | required | description | default |
| ------------------ | --------------------------------------------------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- |
| data | `StructuredTextGraphQlResponse \| DastNode` | :white_check_mark: | The actual [field value](https://www.datocms.com/docs/structured-text/dast) you get from DatoCMS | |
| renderInlineRecord | `({ record }) => ReactElement \| null` | Only required if document contains `inlineItem` nodes | Convert an `inlineItem` DAST node into React | `[]` |
| renderLinkToRecord | `({ record, children }) => ReactElement \| null` | Only required if document contains `itemLink` nodes | Convert an `itemLink` DAST node into React | `null` |
| renderBlock | `({ record }) => ReactElement \| null` | Only required if document contains `block` nodes | Convert a `block` DAST node into React | `null` |
| renderInlineBlock | `({ record }) => ReactElement \| null` | Only required if document contains `inlineBlock` nodes | Convert an `inlineBlock` DAST node into React | `null` |
| metaTransformer | `({ node, meta }) => Object \| null` | :x: | Transform `link` and `itemLink` meta property into HTML props | [See function](https://github.com/datocms/structured-text/blob/main/packages/generic-html-renderer/src/index.ts#L61) |
| customNodeRules | `Array` | :x: | Customize how nodes are converted in JSX (use `renderNodeRule()` to generate rules) | `null` |
| customMarkRules | `Array` | :x: | Customize how marks are converted in JSX (use `renderMarkRule()` to generate rules) | `null` |
| renderText | `(text: string, key: string) => ReactElement \| string \| null` | :x: | Convert a simple string text into React | `(text) => text` |