sanity-io / block-content-to-react Goto Github PK
View Code? Open in Web Editor NEWDeprecated in favor of @portabletext/react
Home Page: https://github.com/portabletext/react-portabletext
License: MIT License
Deprecated in favor of @portabletext/react
Home Page: https://github.com/portabletext/react-portabletext
License: MIT License
I am using block-content-to-react for some basic rich text. Somewhere in the pipeline it is turning what should be:
<ol>
<li>Alpha<li>
<li>Bravo<li>
<li>Charlie<li>
</ol>
Into
<ol>
<li>Alpha<li>
</ol>
<ol>
<li>Bravo<li>
</ol>
<ol>
<li>Charlie<li>
</ol>
Here is what I have in sanity and what I expect:
...but here in staging it breaks what should be a list into multiple lists:
I apologize if I missed something, but reading through the code, I don't see anything that lets me override the text styles for react-native
.
I know it's possible to completely replace the element used during serialization but that's not exactly what I want. The default serializers are good! I just need to update the font.
Let me know if you're accepting PRs or thoughts. Would love to get a good solution in here is possible. Thanks for making it work in react-native in the first place!
I'm using the default serializer with a few types for custom blocks
When using the list option in the editor, more specifically an ordered list, I can see that the content in the editor is numbered correctly.
When it comes to rendering this out on the frontend though, everything has the number 1 beside it.
The code shows that each list item is wrapped in an <ol>
as you can see from this screenshot.
Is there anything I should be adding to add to the serializer to get these to group up into the same list?
@sanity/block-content-to-react 2.0.7
react 17.0.1
I'm just getting started with Sanity (+ NextJS) so I'm hoping I haven't made a rookie error here. I've started a project from scratch and this part of it deals with content modelling for a simple blog. I'm struggling with an error when trying to output the contents of a custom block field from a blog post.
Here is my (simplified) post document definition:
export default {
title: 'Post',
name: 'post',
type: 'document',
fields: [
{
title: 'Title',
name: 'title',
type: 'string',
validation: Rule => Rule.required(),
},
{
title: 'Content',
name: 'contentBlocks',
type: 'contentBlocks',
validation: Rule => Rule.required(),
},
],
};
Here is the definition for my custom contentBlocks field type (slightly simplified):
export default {
title: 'Content Blocks',
name: 'contentBlocks',
type: 'array',
of: [
{
title: 'Text',
name: 'text',
type: 'block',
},
{
title: 'Image',
name: 'image',
type: 'image',
fields: [
{
title: 'Caption',
name: 'caption',
type: 'string',
},
],
},
{
title: 'Embed',
name: 'embed',
type: 'object',
fields: [
{
title: 'URL',
name: 'url',
type: 'url',
validation: Rule => Rule.required(),
},
{
title: 'Caption',
name: 'caption',
type: 'string',
},
],
},
],
};
And my simple Post component looks like this (post
is injected by getStaticProps
):
import BlockContent from '@sanity/block-content-to-react';
import client from '../client';
const Post = ({ post }) => {
return (
<article>
<h1>{post.title}</h1>
<BlockContent
blocks={post.contentBlocks}
imageOptions={{ w: 320, h: 240, fit: 'max' }}
{...client.config()}
/>
</article>
);
};
However, upon requesting a post document path, I get the following error:
Error: Unknown block type "text", please specify a serializer for it in the `serializers.types` prop
So it seems the BlockContent component is receiving a block of type text
(even though, as you can see, none are defined in the field spec) and doesn't know what to do with it. If I inspect the contents of post.contentBlocks
, you can see the errant type in there (this is for an example post containing a line of text, an image block, followed by another line of text):
[
{
_key: 'fa67a6cc016b',
_type: 'text',
children: [ [Object] ],
markDefs: [],
style: 'normal'
},
{
_key: '75c4937c4a94',
_type: 'image',
asset: {
_ref: 'image-fe769ba1ea3b199479d317c59a4ac999a4489507-1296x1296-jpg',
_type: 'reference'
}
},
{
_key: 'cd3522b721fc',
_type: 'text',
children: [ [Object] ],
markDefs: [],
style: 'normal'
}
]
I can solve this error by manually mapping the text
type to the standard serializer for block
:
BlockContent.defaultSerializers.types.text = BlockContent.defaultSerializers.types.block;
Despite the fix however I still don't understand why this is happening, but I've not found anyone else with the same issue so I'm pretty sure I'm doing something stupid.
I should also note that I've tested existing documents and new documents in case some previous definition was persisting, but the result is the same.
Any suggestions would be much appreciated! Please let me know if I can provide any more information. Thanks!
Hi, I have very simple data text (a description) and want to apply lodash truncate
function on it.
But on some descriptions I have line-breaks which make my content a little more complex from a sanity point of view.
If I use a "block serializer"
<BlockContent
blocks={work.descriptionRaw}
serializers={{
types: {
block: (props) => {
console.log(props.children);
return <p>{props.children}</p>;
},
},
}}
/>;
I can see some of the texts contain the following object which seems to represent a line break
{ "$$typeof": Symbol("react.element"), type: HardBreakSerializer(), key: "hb-1", … }
Therefore if I apply truncate
function on it, it displays [object Object]
for those objects being misinterpreted. What can I do to go around this problem ? Thanks !
Hello everyone. I try to using Sanity Latex Input. Sanity studio ok but in frontend(nextjs) i cant show latex content with @sanity/block-content-to-react. How can i do this?
Error: Unknown block type "latex", please specify a serializer for it in the serializers.types
prop.
and
Unknown block type "undefined", please specify a serializer for it in the serializers.types
prop
how can i serialize Latex?
thanks a lot.
I can't get list items from the editor/schema
--mySchema
export default {
title: "Block Content",
name: "blockContent",
type: "array",
of: [
{
title: "Block",
type: "block",
// Styles let you set what your user can mark up blocks with. These
// correspond with HTML tags, but you can set any title or value
// you want and decide how you want to deal with it where you want to
// use your content.
styles: [
{ title: "Normal", value: "normal" },
{ title: "H1", value: "h1" },
{ title: "H2", value: "h2" },
{ title: "H3", value: "h3" },
{ title: "H4", value: "h4" },
{ title: "Quote", value: "blockquote" },
],
lists: [
{ title: "Bullet", value: "bullet" },
{ title: "Numbered", value: "number" }
],
// Marks let you mark up inline text in the block editor.
marks: {
// Decorators usually describe a single property – e.g. a typographic
// preference or highlighting by editors.
decorators: [
{ title: "Strong", value: "strong" },
{ title: "Emphasis", value: "em" },
],
// Annotations can be any object structure – e.g. a link or a footnote.
annotations: [
{
title: "URL",
name: "link",
type: "object",
fields: [
{
title: "URL",
name: "href",
type: "url",
},
],
},
],
},
},
// You can add additional types here. Note that you can't use
// primitive types such as 'string' and 'number' in the same array
// as a block type.
{
type: "image",
options: { hotspot: true },
},
],
};
tried with a lot of variations of serializer like here:
#29
#33
but it just doesn't read the list items.
Would be great to get Enums for block types, like in Contentful. Also, typings for the props of the render nodes would be useful.
I am looking for some guidance. (I could not find a sample with this use case.)
I am looking to include table data (rows, columns) within text. (In my case it is a price list.)
Imagine a product page with like:
Our product comes in three different styles.
| Style | Price |
|--------|-------|
| Small | $10 |
| Medium | $20 |
| Large | $30 |
More text...
My understanding is that Portable Text has the List type but not Table type, correct?
Could this be edited with Sanity Studio?
If not, I believe one solution would be to add a new type in Sanity like "ProductStyle" with "Name" and "Price" properties.
Could I reference this from Portable Text?
Something like this:
Our product comes in three different styles.
<REFERENCE PRICE TABLE HERE>
More text...
How would I extend "block-content-to-react" to render the table?
When trying to build my gatsby site, i am getting this error after using block-content-to-react in more places than before. I dont get it when building locally.
upgraded to latest version of block-content-to-react without solving it. I dont even get the error message.
3:02:19 AM: error Building static HTML failed for path "/malte-brix-es-muss-gerechtigkeit-her/"
3:02:19 AM:
3:02:19 AM: 40 |
3:02:19 AM: 41 | if (!projectId || !dataset) {
3:02:19 AM: > 42 | throw new Error(materializeError);
3:02:19 AM: | ^
3:02:19 AM: 43 | }
3:02:19 AM: 44 |
3:02:19 AM: 45 | var ref = asset._ref;
3:02:19 AM:
3:02:19 AM: WebpackError: You must either:
3:02:19 AM:
3:02:19 AM: - getImageUrl.js:42
3:02:19 AM: [BrixLangeKanzleipage]/[@sanity]/block-content-to-hyperscript/lib/getImageUr l.js:42:1
3:02:19 AM:
3:02:19 AM: - serializers.js:127
3:02:19 AM: [BrixLangeKanzleipage]/[@sanity]/block-content-to-hyperscript/lib/serializer s.js:127:1
3:02:19 AM:
3:02:19 AM: - construct.js:3
3:02:19 AM: [BrixLangeKanzleipage]/[@babel]/runtime/helpers/construct.js:3:32
3:02:19 AM:
3:02:19 AM: - construct.js:13
3:02:19 AM: [BrixLangeKanzleipage]/[@babel]/runtime/helpers/construct.js:13:1
3:02:19 AM:
3:02:19 AM: - static-entry.js:263
3:02:19 AM: BrixLangeKanzleipage/.cache/static-entry.js:263:20
const BlockRenderer = props => (
<animated.div style={captionAnimation} ref={captionRef}>
<p>{props.children}</p>
</animated.div>
);
. . .
<BlockContent
blocks={section._rawContent}
serializers={{
types: { block: BlockRenderer},
}}
/>
Seems like when I try and add an animated.div to the BlockRenderer it gets re-rendered multiple times. this seems to happen because of the ref I am passing. if I set a count on BlockRenderer, it is called multiple times on the page even though the content is not changing. wrapping the component in React.memo() also did not seem to help. Is there a way to pass a ref to the BlockContent component to use in the serializer/BlockRenderer?
There is a lot documentation on how to build the sanity cms (https://www.sanity.io/docs) but I coudn't find sufficient documentation on how to use the fetched data on the client side, which describes how to use sanity-io/block-content-to-react library with serializers e.g. types, marks, list, listItem to render custom react components.
Is there a hidden documentation I missed?
I upgrade a bunch of dependencies in my gatsby
application, including block-content-to-react
from 1.3.5 to 1.3.9 and now I get this error whenever I try to render a block:
I had a dig into source, and this line appears to be the culprit:
var internals = require('@sanity/block-content-to-hyperscript/internals');
mergeSerializers
is expect to be a property of that import, but the contents of that file are simply:
module.exports = {
blocksToNodes: require('./lib/blocksToNodes'),
getImageUrl: require('./lib/getImageUrl'),
getSerializers: require('./lib/serializers')
}
I have a case where I want to serialze a special style inside the block type. But I want to return default rendering of all other styles. I dont want to have to write the serializer for all styles inside the block
How can I do this?
I'm not sure what happened, but I was trying to get my BlockContent component to work because it isn't displaying any styles (only plain text), and now I can't deploy my sanity project! It runs fine except when I try to deploy it.
I am getting this error:
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
at resolveDispatcher (C:/Users/Mail4/OneDrive/Documents/Programming/ReactJS Projects/my-portfolio/brandonsblog/node_modules/@sanity/base/node_modules/react/cjs/react.development.js:1476:13)
at useMemo (C:/Users/Mail4/OneDrive/Documents/Programming/ReactJS Projects/my-portfolio/brandonsblog/node_modules/@sanity/base/node_modules/react/cjs/react.development.js:1531:20)
at Document (C:/Users/Mail4/OneDrive/Documents/Programming/ReactJS Projects/my-portfolio/brandonsblog/node_modules/@sanity/base/lib/components/Document.js:49:40)
at processChild (C:/Users/Mail4/OneDrive/Documents/Programming/ReactJS Projects/my-portfolio/brandonsblog/node_modules/react-dom/cjs/react-dom-server.node.development.js:3353:14)
at resolve (C:/Users/Mail4/OneDrive/Documents/Programming/ReactJS Projects/my-portfolio/brandonsblog/node_modules/react-dom/cjs/react-dom-server.node.development.js:3270:5)
at ReactDOMServerRenderer.render (C:/Users/Mail4/OneDrive/Documents/Programming/ReactJS Projects/my-portfolio/brandonsblog/node_modules/react-dom/cjs/react-dom-server.node.development.js:3753:22)
at ReactDOMServerRenderer.read (C:/Users/Mail4/OneDrive/Documents/Programming/ReactJS Projects/my-portfolio/brandonsblog/node_modules/react-dom/cjs/react-dom-server.node.development.js:3690:29)
at Object.renderToStaticMarkup (C:/Users/Mail4/OneDrive/Documents/Programming/ReactJS Projects/my-portfolio/brandonsblog/node_modules/react-dom/cjs/react-dom-server.node.development.js:4314:27)
at buildStaticAssets (C:/Users/Mail4/OneDrive/Documents/Programming/ReactJS Projects/my-portfolio/brandonsblog/node_modules/@sanity/core/lib/actions/build/buildStaticAssets.js:162:118)
at async _default (C:/Users/Mail4/OneDrive/Documents/Programming/ReactJS Projects/my-portfolio/brandonsblog/node_modules/@sanity/core/lib/actions/deploy/deployAction.js:93:9)
and here is my package.json file:
{
"name": "brandonsblog",
"private": true,
"version": "1.0.0",
"description": "",
"main": "package.json",
"author": "Brandon Pyle <[email protected]>",
"license": "UNLICENSED",
"scripts": {
"start": "sanity start",
"build": "sanity build"
},
"keywords": [
"sanity"
],
"dependencies": {
"@sanity/base": "^2.23.2",
"@sanity/block-content-to-react": "^3.0.0",
"@sanity/code-input": "^2.23.2",
"@sanity/core": "^2.23.2",
"@sanity/default-layout": "^2.23.1",
"@sanity/default-login": "^2.23.1",
"@sanity/desk-tool": "^2.23.2",
"@sanity/vision": "^2.23.1",
"prop-types": "^15.7",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"sanity-plugin-markdown": "^2.1.0",
"styled-components": "^5.2.0"
},
"devDependencies": {},
"repository": {
"type": "git",
"url": "https://github.com/bpyle02/my-portfolio-new.git"
}
}
You can find my code here: https://github.com/bpyle02/portfolio
And the live website here: https://brandonpyle.netlify.app/blog/how-to-properly-write-a-github-readme
The title says it all, I was curious if it would be a good idea to be able to either set an ENV variable or send a prop to the block renderer, so if NODE_ENV !== "production"
, only write missing serializer types errors to the console, instead of throwing. I am working in Next.js, and these errors cause the app to crash if BlockContent throws an error on server-side. We cannot add all the serializer types at once.
@sanity/block-content-to-react 2.0.1
react 16.3.2
When the BlockContent
encounters an unknown type defined in Sanity, the component crashes and effectively crashes the page. I don't know if this applies to marks and annotations as well.
It would be nice to have some way of telling the component to ignore unknown fields, and for several reasons:
array-of-block
field, it's very handy to deploy this beforehand in production and prepare all content before deploying frontend(s) to handle the new content.array-of-block
field. One workaround here is to work on a separate document, but you get the idea.You could potentially add a switch for this in the component. I guess it would be up to you to decide if you should make this behaviour default as well. As we've already discussed, one could use the __DEV__
to define the behaviour, but then you would still encounter problems when working on a multi-developer setup as you would be breaking the other developers documents.
@sanity/block-content-to-react 2.0.6
react 16.8.0-alpha.1
I've a pretty standard setup:
import BlockContent from "@sanity/block-content-to-react";
const Footer = ({ footerCards }) => (
{footerCards.map(footerCard => (
<div key={footerCard._id}>
<FooterCard title={footerCard.title}>
<BlockContent blocks={footerCard.information} />
</FooterCard>
</div>
))}
);
export default Footer;
But I receive a warning in the console:
Warning: Failed prop type: The prop `blocks` is marked as required in `SanityBlockContent`, but its value is `undefined`.
But everything else except this error seems to work fine and value of footerCard.information
isn't undefined.
i add some customization in my sanity portable text. i just want to show the highlighted text but in my web project it did not show highlighted but the em and strong tag is working in portable text
Cross-posting because it probably originates here: sanity-io/next-sanity#20
This may apply to plain React as well.
Hotspot & crop settings aren't getting translated into image URL parameters for block content images. It looks like it should be happening, but there is some translation in internals
happening to the image/asset objects that doesn't allow the crop and hotspot properties to get translated.
Example image node:
{_key: "8e61aa8bb377"_type: "image"asset: {_ref: "image-208d3bbfdb69bf70d8101c5d7917e87534e82fc6-2400x3000-jpg", _type: "reference"}, crop: {_type: "sanity.imageCrop", bottom: 0, left: 0, right: 0, top: 0}, hotspot: {_type: "sanity.imageHotspot", height: 0.1439999999999996, width: 0.7633333333333336, x: 0.3816666666666668, y: 0.6746666666666663}, __proto__: Object }
Turns into this URL: https://cdn.sanity.io/images/[mine]/staging/208d3bbfdb69bf70d8101c5d7917e87534e82fc6-2400x3000.jpg
Whereas, a non-Block Content image object:
__typename: "Image" asset: __typename: "SanityImageAsset" url: "https://cdn.sanity.io/images/[mine]/staging/e90e33423f8ff21a275dd8b475ab5b5702db638b-1578x876.png" __proto__: Object crop: __typename: "SanityImageCrop" bottom: 0 left: 0 right: 0 top: 0 __proto__: Object hotspot: __typename: "SanityImageHotspot" height: 0.9571509678525129 width: 0.29369797859690766 x: 0.45362663495838296 y: 0.47857548392625643 __proto__: Object
using imageUrlBuilder from @sanity/image-url translates the rect and focal point URL parameters correctly.
EDIT: It's working as expected. I was passing it in a different component. Please delete this issue if possible
<BlockContent blocks={content} projectId={process.env.NEXT_PUBLIC_SANITY_PROJECT_ID} dataset={process.env.NEXT_PUBLIC_SANITY_DATASET} className={markdownStyles.markdown} renderContainerOnSingleChild={true} />
The aboce is my code. The className works fine when there are multiple children but when I pass a single child the className is not utilized. The renderContainerOnSingleChild doesn't seem to change anything.
The default container is a div
. This creates an empty div
that wrapping every element rendered. Sometimes we have invalid HTML markup because the contents are rendered inside a Heading tag. Heading
tag can only contain inline elements: https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#phrasing_content
Validator used: https://validator.w3.org/nu
Here is the code:
https://github.com/sanity-io/block-content-to-hyperscript/blob/d9d5adac36d50c0c012f7bb4099fb6f3fa67b382/src/serializers.js#L156
Should the default container value be just a Fragment
for React apps?
I know we can just set the container prop to a Fragment. I am wondering if there are any other implications I am unable to see?
I see a lot of unmounting/re-mounting of my component that is used in a custom serializer. In my mind, the component should not be unmounted/remounted on every props update, but instead just updated with new props.
Given the following app (or see sandbox):
import ReactDOM from 'react-dom';
import React, {Component} from "react";
import BlockContent from "@sanity/block-content-to-react";
class Sometype extends Component {
componentDidMount() {
console.log("Sometype.componentDidMount - this should only happen once", arguments);
}
componentWillReceiveProps(nextProps) {
console.log("Sometype.componentWillReceiveProps - this should happen on every click, but never happens", arguments);
}
render() {
return <p onClick={this.props.clickHandler}>Click me! {this.props.children}</p>
}
}
const block = {
"_key":"58cf8aa1fc74",
"_type":"sometype",
"something":{someProperty: "someValue"}
};
class App extends Component {
constructor() {
super();
this.state = {clicks: 0};
this.serializers = {
types: {
sometype: props => (
<Sometype clickHandler={() => this.setState({clicks: this.state.clicks + 1})}>
{props.node.something.someProperty + " " + this.state.clicks}
</Sometype>
)
}
};
}
render() {
return (
<div className="App">
<BlockContent key={'blockcontent-1'} blocks={[block]} serializers={this.serializers}/>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
The app uses BlockContent
with a map of a single serializer for type sometype
. The serializer renders a Sometype
component.
In the browser, when clicking any content from Sometype
, the state of the container component is updated, thus triggering a re-render.
At this point, I would expect Sometype.componentWillUpdate
to be called, with the new updated props. Instead, the Sometype
instance is unmounted, and a new instance is mounted, thus never calling componentWillUpdate
. I'm not sure this violates any spec of a React component, but it is certainly uncommon behaviour.
Compare to an app not using BlockContent
, where Sometype.componentWillUpdate
is called on props update.
By modifying block-content-to-hyperscript/lib/serializers.js
so that it returns an object instead of a function (thus hardcoding React.createElement
instead of passing it as a parameter to the function), and modifying BlockContent
to used the exported object instead of calling the function, the problem goes away, and Sometype.componentWillUpdate
is called as expected.
This article describes a somewhat similar problem, but I am unable to see how it can apply to this problem.
Hi! I commented on an issue I found on gatsby-source-sanity
as well but I think this repo is a better place for this question?
I'm using sanity on a gatsby project and therefore working with graphql. I am attempting to render Portable Text with BlockContent but it was breaking. I believe this is due to the fact that markDefs
aren't supported by the graphql api as escalated here, but this component expects markDefs
to be present. When they are not, I hit the error detailed below.
So here is the component I am using:
import { graphql } from 'gatsby';
import React from 'react';
import BlockContent from '@sanity/block-content-to-react';
const serializers = {
types: {
block: (props) => BlockContent.defaultSerializers.types.block(props),
},
};
export default function TestPage({ data }) {
const { portableTextDescription } = data.item;
// portableTextDescription.map((i) => {
// i.markDefs = [];
// return i;
// });
return (
<>
<BlockContent
blocks={portableTextDescription}
serializers={serializers}
/>
</>
);
}
export const query = graphql`
query TestItem {
item: sanityItem(
slug: { current: { eq: "slug-for-desired-product" } }
) {
portableTextDescription {
_key
_type
children {
_key
_type
marks
text
__typename
}
style
list
_rawChildren
__typename
}
}
}
`;
This hits an error:
TypeError: Cannot read property `find` of undefined in node_modules/@sanity/block-content-to-hyperscript/lib/buildMarksTree.js:52
If you uncomment lines 14:17 where I manually add markDefs
to each item in the Portable Text array, the bug is fixed and everything renders correctly:
portableTextDescription.map((i) => {
i.markDefs = [];
return i;
});
I know this package is deprecated now so I will try and move to the new package to see if it fixes this but we have a strange issue where we have a simple text of links and the 3rd url is re-used in the 6th link. The strange thing is it only does it in gatsby production/build mode and not dev. We're on the latest 3.0.0 version.
Thought I would report it anyway in case someone has seen this before. In the screenshot below for example the url for the 3rd link "Help Desk" is showing up re-used in the final 6th link "Spring Show Gallery" in prod/build only. I've tried debugging the data and it looks correct.
I try to overwrite and add a class to the paragraph-element with this code:
<BlockContent
blockTypeHandlers={{
textBlock: {
p: ({ children = [] }) => <p className="lede">{children}</p>,
},
}}
blocks={text}
/>
But it only returns the default <p>...</p>
syntax. I've tried several variations, with and without parameter destructuring.
It's more of a question, but wanted to see if it's possible and if we could turn it into a feature.
Currently each mark serialiser is getting only props from a mark, but I need to use additional props that will be passed to <PortableText />
.
Example:
export const PortableText = createPortableTextComponent({
...sanityConfig,
serializers: {
marks: {
someSerializer(props, propsFromPortableText) {
return <span>{props.children} {propsFromPortableText.something}</span>
}
},
},
});
<PortableText blocks={blocks} extraProps={{ something: 50 }} />
Any ideas?
If I upgrade to latest react-native (at the moment 0.57.7
), the test suite breaks due to a lot of snapshot mismatches.
Seems to be mostly caused by the same issue, <Text
doesnt receive expected props:
● 001-empty-block
expect(value).toMatchSnapshot()
Received value does not match stored snapshot "001-empty-block 1".
- Snapshot
+ Received
@@ -3,11 +3,7 @@
Object {
"marginBottom": 16,
}
}
>
- <Text
- accessible={true}
- allowFontScaling={true}
- ellipsizeMode="tail"
- />
+ <Text />
</View>
59 | .toJSON()
60 |
> 61 | expect(tree).toMatchSnapshot()
| ^
62 | })
63 | })
64 |
at Object.toMatchSnapshot (test/rn/BlockContent.test.js:61:18)
I'm using the @sanity/block-content-to-react
package like this:
<SanityBlockContent
blocks={c._rawBlockContent}
imageOptions={{ w: 600, fit: 'max' }}
renderContainerOnSingleChild
projectId={process.env.GATSBY_SANITY_PROJECT_ID}
dataset={process.env.GATSBY_SANITY_DATASET}
className="mt-5 prose"
/>
The image in the _rawBlockContent
looks like this (it's using the blockContent
that gets generate when you run sanity init
and choose the blog schema):
{
"_key": "a0fd6af0192c",
"_type": "image",
"asset": {
"_ref": "image-5b0f778989438587159b584d5258a65c73f0217b-1800x1200-png",
"_type": "reference"
},
"crop": {
"_type": "sanity.imageCrop",
"bottom": 0.15053948435618247,
"left": 0,
"right": 0,
"top": 0.17204512497849422
},
"hotspot": {
"_type": "sanity.imageHotspot",
"height": 0.6774153906653233,
"width": 1,
"x": 0.5,
"y": 0.5107528203111559
}
}
And the outputted HTML looks like this:
<figure>
<img src="https://cdn.sanity.io/images/vxhsnaeh/production/5b0f778989438587159b584d5258a65c73f0217b-1800x1200.png?rect=0,206,1800,813&w=600&fit=max">
</figure>
As most of the images I would be using are purely decorative, I was happy to use alt=""
, but this doesn't seem to be added by default.
Writing a custom serializer works, but I need to bring in the @sanity/image-url
and @sanity/client
packages to build the image URL which I'd rather not do.
import { urlFor } from '../utils/sanity-client';
function ImageRenderer({ node }) {
return <img src={urlFor(node.asset._ref).width(600).url()} alt="" />;
}
const serializers = {
types: {
image: ImageRenderer,
},
};
<img src="https://cdn.sanity.io/images/vxhsnaeh/production/5b0f778989438587159b584d5258a65c73f0217b-1800x1200.png?w=600" alt="">
Is there a better way to handle this?
I am trying to include links in block content, but they are being output as marks with random ids such as 8d165bb07736
instead of link
.
I have essentially just followed the Sanity tutorial here:
https://www.sanity.io/guides/portable-text-internal-and-external-links
In other words I have added the following to annotations
in the schema:
{
name: 'link',
type: 'object',
title: 'External link',
fields: [
{
name: 'href',
type: 'url',
title: 'URL',
},
{
title: 'Open in new tab',
name: 'blank',
type: 'boolean',
},
],
},
It appears as expected in Sanity Studio: I am able to set the URL, there is a checkbox for "open in new tab", etc.
I am also providing a custom serializer:
const serializers = {
marks: {
link: ({mark, children}) => {
// Read https://css-tricks.com/use-target_blank/
const { blank, href } = mark
return blank ?
<a href={href} target="_blank" rel="noopener">{children}</a>
: <a href={href}>{children}</a>
}
}
}
<BlockContent blocks={event.body} serializers={serializers} />
However in the console I am seeing that the custom serializer never kicks in, because the mark is not being named "link", instead it arrives as "8d165bb07736":
Unknown mark type "8d165bb07736", please specify a serializer for it in the serializers.marks
Gatsby dependencies
"dependencies": {
"@sanity/block-content-to-react": "^2.0.7",
"babel-plugin-styled-components": "^1.12.0",
"dotenv": "^9.0.0",
"gatsby": "^3.4.1",
"gatsby-image": "^3.9.0",
"gatsby-plugin-anchor-links": "^1.2.1",
"gatsby-plugin-image": "^1.4.1",
"gatsby-plugin-manifest": "^3.4.0",
"gatsby-plugin-react-helmet": "^4.4.0",
"gatsby-plugin-sanity-image": "^0.8.0",
"gatsby-plugin-sharp": "^3.4.2",
"gatsby-plugin-sitemap": "^4.0.0",
"gatsby-plugin-styled-components": "^4.4.0",
"gatsby-source-filesystem": "^3.4.0",
"gatsby-source-sanity": "^7.0.4",
"gatsby-transformer-sharp": "^3.4.0",
"normalize.css": "^8.0.1",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-helmet": "^6.1.0",
"react-slick": "^0.28.1",
"styled-components": "^5.3.0"
},
Sanity dependencies
"dependencies": {
"@sanity/base": "^2.10.2",
"@sanity/components": "^2.2.6",
"@sanity/core": "^2.10.2",
"@sanity/default-layout": "^2.10.2",
"@sanity/default-login": "^2.8.0",
"@sanity/desk-tool": "^2.10.2",
"@sanity/vision": "^2.10.0",
"prop-types": "^15.7",
"react": "^17.0",
"react-dom": "^17.0",
"sanity-plugin-order-documents": "^0.0.19"
},
I am pulling content to Gatsby from Sanity using GraphQL. Content arrives without issues and I can use inline images, header tags, block quotes etc. However, when a block contains a link, I get the following error:
Unknown mark type "68344811ed4b", please specify a serializer for it in the `serializers.marks` prop
at SpanSerializer (http://localhost:8000/commons.js:10953:29)
"68344811ed4b" corresponds to the key of the link.
This is the content I am passing to the BlockContent component:
content: Array(1)
0:
children: Array(2)
0: {_key: "48b95fb74a12", _type: "span", marks: Array(1), text: "Here is the link"}
1: {_key: "4ac794989d8d", _type: "span", marks: Array(0), text: ""}
length: 2
__proto__: Array(0)
markDefs: Array(2)
0: {_key: "a02fe6d7d7ca", _type: "link"}
1: {_key: "68344811ed4b", _type: "link", href: "http://exmple.com"}
length: 2
__proto__: Array(0)
style: "normal"
_key: "9cb5d7dcd417"
_type: "block"
If I create a custom serializer with the name of the key (serializers.marks.68344811ed4b) this gets passed an item of type span. This seems to be true for any link in any Sanity document.
I am using v 2.0.7 of @sanity/block-content-to-react.
The default style 'Normal' uses
, how to override it to plain text.
I cannot seem to figure out how to serialize for lists in block
This is my serializer.
const serializer = {
types: {
image: props => (
<figure className="has-text-centered">
<img
src={urlFor(props.node.asset)
.width(600)
.url()}
alt={props.node.alt}
style={{ borderRadius: "5px" }}
/>
<figcaption>{props.node.caption}</figcaption>
</figure>
),
block(props) {
switch (props.node.style) {
case 'h1':
return <h1 className="">{props.children}</h1>
case 'h2':
return <h2 className="">{props.children}</h2>
case 'h3':
return <h3 className="">{props.children}</h3>
case 'h4':
return <h4 className="">{props.children}</h4>
case 'li':
return <h4 className="">{props.children}</h4>
case 'blockquote':
return <blockquote className="">{props.children}</blockquote>
case 'normal':
if (props.listItem) return <strong>{props.children}</strong>
else return <p className="is-family-secondary is-size-5">{props.children}</p>
default:
return <p className="is-family-secondary">{props.children}</p>
}
}
}
};
Ok, so I am working on a blog for my portfolio website, and I just got it working until I realized that when I style the body of my blog posts in Sanity Studio, those styles do not transfer to the actual website. All I see in the website is plain text. This might be a dumb question, but I have no Idea what i'm doing wrong.
Here is my SinglePost.js file:
import { useState, useEffect } from "react";
import { useParams, Link } from "react-router-dom";
import client from "../../client";
import BlockContent from "@sanity/block-content-to-react";
import Header from "../Header";
export default function SinglePost() {
const [singlePost, setSinglePost] = useState([])
const [isLoading, setIsLoading] = useState(true)
const { slug } = useParams()
useEffect(() => {
client
.fetch(
`*[slug.current == "${slug}"] {
title,
body,
mainImage {
asset -> {
_id,
url
},
alt
}
}`
)
.then((data) => setSinglePost(data[0]))
setIsLoading(false)
}, [slug])
const serializers = {
types: {
code: (props) => (
<pre data-language={props.node.language}>
<code>{props.node.code}</code>
</pre>
),
},
}
return (
<div className = "bg-gray-100 dark:bg-zinc-900">
<Header />
{isLoading ? ( <h1>Loading...</h1> ) : (
<section className = "p-5 pb-20 lg:mx-28 md:mx-16 sm:mx-8">
<h1 className = "title mb-20">{singlePost.title}</h1>
<div className = "flex items-center justify-center">
{singlePost.mainImage && singlePost.mainImage.asset && (
<img src = {singlePost.mainImage.asset.url} alt = {singlePost.title} title = {singlePost.title} className = "rounded-xl shadow-xl dark:shadow-gray-100/10" />
)}
</div>
<p className = "paragraph mt-5 mb-5">By Brandon Pyle</p>
<div className="">
<BlockContent serializers={serializers} blocks={singlePost.body} projectId="2hp9gld0" dataset="production" />
</div>
<button>
<Link to = "/blog" className = "button">Read more articles</Link>
</button>
</section>
)}
</div>
)
}
Here is my blockContent.js file:
/**
* This is the schema definition for the rich text fields used for
* for this blog studio. When you import it in schemas.js it can be
* reused in other parts of the studio with:
* {
* name: 'someName',
* title: 'Some title',
* type: 'blockContent'
* }
*/
export default {
title: 'Block Content',
name: 'blockContent',
type: 'array',
of: [
{
title: 'Block',
type: 'block',
// Styles let you set what your user can mark up blocks with. These
// correspond with HTML tags, but you can set any title or value
// you want and decide how you want to deal with it where you want to
// use your content.
styles: [
{title: 'Normal', value: 'normal'},
{title: 'H1', value: 'h1'},
{title: 'H2', value: 'h2'},
{title: 'H3', value: 'h3'},
{title: 'H4', value: 'h4'},
{title: 'Quote', value: 'blockquote'},
],
lists: [{title: 'Bullet', value: 'bullet'}],
// Marks let you mark up inline text in the block editor.
marks: {
// Decorators usually describe a single property – e.g. a typographic
// preference or highlighting by editors.
decorators: [
{title: 'Strong', value: 'strong'},
{title: 'Emphasis', value: 'em'},
{title: 'Code', value: 'code'},
{title: 'Highlight', value: 'highlight'},
],
// Annotations can be any object structure – e.g. a link or a footnote.
annotations: [
{
title: 'URL',
name: 'link',
type: 'object',
fields: [
{
title: 'URL',
name: 'href',
type: 'url',
},
],
},
],
},
},
// You can add additional types here. Note that you can't use
// primitive types such as 'string' and 'number' in the same array
// as a block type.
{
type: 'image',
options: {hotspot: true},
},
],
}
You can find the complete source code here: https://github.com/bpyle02/portfolio
If you would like to see a live example of the error, check out this blog post I made that is meant to have different headings, bullet lists, etc.: https://brandonpyle.netlify.app/blog/how-to-properly-write-a-github-readme
I was very confused / surprised by the renderer not rendering my configured container
all of a sudden. It took me a while to realise that the reason was that the block content only contained one element. I also nearly missed the option renderContainerOnSingleChild
.
To have differing element hierarchies between content with one element and with multiple elements seems to be a special requirement rather then a good default. I suggest therefore to change the default of renderContainerOnSingleChild
to true
.
It would be a breaking change, though :-/
Thanks for considering :-)
Right now, SanityBlockContent handles the className
attribute, but not, for example handle style
attribute (very common attribute, surprised me className was accepted but not style). It's confusing that in cases of single nodes, <SanityBlockContent>
does not render a container <div>
element, but when multiple nodes it does, and when it does render a container, unknown attributes to <SanityBlockContent>
are not passed along to the container to render them as attributes.
Please do one of the following:
<SanityBlockContent>
. Breaking change in the sense that attributes that were specified but were ignored will no longer be ignored.With this implementation, my content is rendering as I would expect it to:
<BlockContent
projectId={process.env.NEXT_PUBLIC_PROJECT_ID}
dataset={process.env.NEXT_PUBLIC_DATASET}
blocks={post?.body}
/>
However, the addition of the following serializer makes it so the embedded images in the block content do not render:
const serializers = {
block: (props): JSX.Element => {
const { children, node } = props;
const { style } = node;
if (style === "h1") {
return (
<Typography className="mb-4" variant="h1">
{children}
</Typography>
);
}
if (style === "h2") {
return (
<Typography className="mb-4" variant="h2">
{children}
</Typography>
);
}
if (style === "h3") {
return (
<Typography className="mb-4" variant="h3">
{children}
</Typography>
);
}
if (style === "h4") {
return (
<Typography className="mb-4" variant="h4">
{children}
</Typography>
);
}
if (style === "h5") {
return (
<Typography className="mb-4" variant="h5">
{children}
</Typography>
);
}
if (style === "h6") {
return (
<Typography className="mb-4" variant="h6">
{children}
</Typography>
);
}
return BlockContent.defaultSerializers.types.block(props);
}
};
I am thinking that return BlockContent.defaultSerializers.types.block(props);
does not properly render images, but it's not clear from the docs how I would do so.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.